{******************************************} { } { FastReport v4.0 } { HTTP Report Server Logs } { } { Copyright (c) 1998-2007 } { by Alexander Fediachov, } { Fast Reports Inc. } { } {******************************************} unit frxServerLog; {$I frx.inc} interface uses Windows, SysUtils, Classes, SyncObjs, frxUtils, frxServerUtils, frxNetUtils; type TfrxServerLog = class(TThread) private FCurrentReports: Integer; FCurrentSessions: Integer; FErrorsCount: Integer; FLevelFName: TStringList; FLevels: TList; FLogDir: String; FLogging: Boolean; FMaxReports: Integer; FMaxReportTime: Integer; FMaxSessions: Integer; FTotalReports: Integer; FTotalReportTime: Integer; FTotalSessions: Integer; FMaxLogSize: Integer; FMaxLogFiles: Integer; FCS: TCriticalSection; FTotalCacheHits: Integer; FThreadActive: Boolean; procedure WriteFile(const FName: String; const Text: String); procedure LogRotate(const FName: String); protected procedure Execute; override; public constructor Create; destructor Destroy; override; function AddLevel(const FileName: String): Integer; procedure Clear; procedure Flush; procedure ErrorReached; procedure StartAddReportTime(i: Integer); procedure StatAddCurrentReport; procedure StatAddCurrentSession; procedure StatAddCacheHit; procedure StatAddReports(i: Integer); procedure StatAddSessions(i: Integer); procedure StatRemoveCurrentReport; procedure StatRemoveCurrentSession; procedure Write(const Level: Integer; const Msg: String); property Active: Boolean read FLogging write FLogging; property CurrentReports: Integer read FCurrentSessions write FCurrentReports; property CurrentSessions: Integer read FCurrentSessions write FCurrentSessions; property ErrorsCount: Integer read FErrorsCount write FErrorsCount; property LogDir: String read FLogDir write FLogDir; property MaxReports: Integer read FMaxReports write FMaxReports; property MaxReportTime: Integer read FMaxReportTime write FMaxReportTime; property MaxSessions: Integer read FMaxSessions write FMaxSessions; property TotalReports: Integer read FTotalReports write FTotalReports; property TotalReportTime: Integer read FTotalReportTime write FTotalReportTime; property TotalSessions: Integer read FTotalSessions write FTotalSessions; property MaxLogSize: Integer read FMaxLogSize write FMaxLogSize; property MaxLogFiles: Integer read FMaxLogFiles write FMaxLogFiles; property TotalCacheHits: Integer read FTotalCacheHits write FTotalCacheHits; end; var LogWriter: TfrxServerLog; const ERROR_LEVEL = 0; ACCESS_LEVEL = 1; REFERER_LEVEL = 2; AGENT_LEVEL = 3; SERVER_LEVEL = 4; implementation { TfrxServerLog } constructor TfrxServerLog.Create; begin inherited Create(True); FLogging := False; Priority := tpLowest; FLevels := TList.Create; FLevelFName := TStringList.Create; FTotalSessions := 0; FMaxSessions := 0; FTotalReports := 0; FMaxReports := 0; FMaxReportTime := 0; FTotalReportTime := 0; FTotalCacheHits := 0; FCurrentSessions := 0; FCurrentReports := 0; FErrorsCount := 0; FCS := TCriticalSection.Create; FThreadActive := False; Resume; end; destructor TfrxServerLog.Destroy; begin Clear; Terminate; while FThreadActive do begin Sleep(10); PMessages; end; FLevels.Free; FLevelFName.Free; FCS.Free; inherited; end; procedure TfrxServerLog.Clear; var i: Integer; begin Flush; for i := 0 to FLevels.Count - 1 do TStringList(FLevels[i]).Free; FLevels.Clear; FLevelFName.Clear; end; function TfrxServerLog.AddLevel(const FileName: String): Integer; var Level: TStringList; begin Level := TStringList.Create; FLevels.Add(Level); FLevelFName.Add(FileName); Result := FLevels.Count - 1; end; procedure TfrxServerLog.Write(const Level: Integer; const Msg: String); begin if Length(Msg) > 0 then TStringList(FLevels[Level]).Add(Msg); end; procedure TfrxServerLog.Execute; var i: Integer; begin FThreadActive := True; while not Terminated do begin Flush; i := 0; while (not Terminated) and (i < 100) do begin Sleep(10); Inc(i); end; end; FThreadActive := False; end; procedure TfrxServerLog.Flush; var i: Integer; Level: TStringList; Msg: String; begin FCS.Enter; try for i := 0 to FLevels.Count - 1 do begin Level := TStringList(FLevels[i]); if (Level.Count > 0) then begin Msg := Level.Text; Level.Clear; if FLogging then WriteFile(FLevelFName[i], Msg); end; end; finally FCS.Leave; end; end; procedure TfrxServerLog.WriteFile(const FName, Text: String); var FStream: TFileStream; FSize: Extended; begin FSize := 0; if FLogging and (Length(Trim(Text)) > 0) then begin if not FileExists(FLogDir + FName) then begin try FStream := TFileStream.Create(FLogDir + FName, fmCreate); FStream.Free; except FLogging := False; end; end; try FStream := TFileStream.Create(FLogDir + FName, fmOpenWrite + fmShareDenyWrite); try FStream.Seek(0, soFromEnd); FStream.Write(Text[1], Length(Text)); FSize := FStream.Size div 1024; finally FStream.Free; if FSize > FMaxLogSize then LogRotate(FLogDir + FName); end; except FLogging := False; end; end; end; procedure TfrxServerLog.StatAddReports(i: Integer); begin FCS.Enter; try FTotalReports := FTotalReports + i; finally FCS.Leave; end; end; procedure TfrxServerLog.StatAddSessions(i: Integer); begin FCS.Enter; try FTotalSessions := FTotalSessions + i; finally FCS.Leave; end; end; procedure TfrxServerLog.StartAddReportTime(i: Integer); begin FCS.Enter; try FTotalReportTime := FTotalReportTime + i; if i > FMaxReportTime then FMaxReportTime := i; finally FCS.Leave; end; end; procedure TfrxServerLog.StatAddCurrentReport; begin FCS.Enter; try FCurrentReports := FCurrentReports + 1; if FCurrentReports > FMaxReports then FMaxReports := FCurrentReports; finally FCS.Leave; end; StatAddReports(1); end; procedure TfrxServerLog.StatAddCurrentSession; begin FCS.Enter; try FCurrentSessions := FCurrentSessions + 1; if FCurrentSessions > FMaxSessions then FMaxSessions := FCurrentSessions; finally FCS.Leave; end; StatAddSessions(1); end; procedure TfrxServerLog.StatAddCacheHit; begin FCS.Enter; try Inc(FTotalCacheHits); finally FCS.Leave; end; end; procedure TfrxServerLog.StatRemoveCurrentReport; begin FCS.Enter; try FCurrentReports := FCurrentReports - 1; finally FCS.Leave; end; end; procedure TfrxServerLog.StatRemoveCurrentSession; begin FCS.Enter; try FCurrentSessions := FCurrentSessions - 1; finally FCS.Leave; end; end; procedure TfrxServerLog.ErrorReached; begin FCS.Enter; try Inc(FErrorsCount); finally FCS.Leave; end; end; procedure TfrxServerLog.LogRotate(const FName: String); var TmpStream: TFileStream; OutStream: TFileStream; i: Integer; s: String; FRotated: Boolean; begin FRotated := False; if FMaxLogFiles > 1 then begin i := FMaxLogFiles - 1; while i > 0 do begin s := ChangeFileExt(FName, '.log-' + IntToStr(i)); if FileExists(s) then if i < (FMaxLogFiles - 1) then RenameFile(s, ChangeFileExt(s, '.log-' + IntToStr(i + 1))) else DeleteFile(s); i := i - 1; end; RenameFile(FName, ChangeFileExt(FName, '.log-1')) end else begin try TmpStream := TFileStream.Create(FName, fmOpenRead + fmShareDenyWrite); try if TmpStream.Size > FMaxLogSize * 1024 then begin TmpStream.Position := TmpStream.Size - FMaxLogSize * 1024; s := ' '; while (s[1] <> #13) and (TmpStream.Position < TmpStream.Size) do TmpStream.Read(s[1], 1); if s[1] = #13 then begin TmpStream.Read(s[1], 1); if s[1] = #10 then begin try s := ChangeFileExt(FName, '.tmp'); OutStream := TFileStream.Create(s, fmCreate); try OutStream.CopyFrom(TmpStream, TmpStream.Size - TmpStream.Position); FRotated := True; finally OutStream.Free; end; except end; end; end; end; finally TmpStream.Free; if FRotated then begin DeleteFile(FName); RenameFile(s, FName); end; end; except end; end; end; end.