#1 2014-09-16 15:32:11

cheemeng
Member
From: Malaysia
Registered: 2011-08-09
Posts: 61

LogFile Custom Name

Hi Arnaud,

Is it possible to have a callback event for SynLog that returns a custom filename? Either that, or move the filename generation routine into another procedure/function that is virtual and may be overridden by a descendant class?

This is useful if I want to only have a daily log, so if the programme gets restarted, logging may resume in the same file. Also, if the file already exists, perhaps logHeaderWritten should be set in fInternalFlags, to prevent writing of header again.

Thank you!

Offline

#2 2014-09-19 12:28:22

cheemeng
Member
From: Malaysia
Registered: 2011-08-09
Posts: 61

Re: LogFile Custom Name

Hi Arnaud,

What do you think of this? (SynCommons.pas)

 SynCommons.pas | 95 +++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 57 insertions(+), 38 deletions(-)

diff --git a/SynCommons.pas b/SynCommons.pas
index d673641..3fec809 100644
--- a/SynCommons.pas
+++ b/SynCommons.pas
@@ -10705,6 +10705,7 @@ type
 
   /// how stack trace shall be computed
   TSynLogStackTraceUse = (stManualAndAPI,stOnlyAPI,stOnlyManual);
+  TSynLogExistsAction = (acOverwrite, acAppend, acAppendWithHeader);
 
   {/ regroup several logs under an unique family name
    - you should usualy use one family per application or per architectural
@@ -10762,6 +10763,7 @@ type
     fRotateFileCount: cardinal;
     fRotateFileSize: cardinal;
     fRotateFileAtHour: integer;
+    fLogExistsAction: TSynLogExistsAction;
     function CreateSynLog: TSynLog;
     {$ifdef MSWINDOWS}
     procedure SetAutoFlush(TimeOut: cardinal);
@@ -10945,6 +10947,8 @@ type
     // be appended instead
     // - TSynLogFile class and our LogView tool will handle both patterns
     property EndOfLineCRLF: boolean read fEndOfLineCRLF write fEndOfLineCRLF;
+    // action to take if log file already exists. Defaults to append with header
+    property LogExistsAction: TSynLogExistsAction read fLogExistsAction write fLogExistsAction;
   end;
 
   /// thread-specific internal context used during logging
@@ -11025,12 +11029,12 @@ type
     procedure CreateLogWriter; virtual;
 {$ifndef DELPHI5OROLDER}
     procedure LogInternal(Level: TSynLogInfo; TextFmt: PWinAnsiChar;
-      const TextArgs: array of const; Instance: TObject); overload; 
+      const TextArgs: array of const; Instance: TObject); overload;
 {$endif}
     procedure LogInternal(Level: TSynLogInfo; const Text: RawUTF8;
       Instance: TObject; TextTruncateAtLength: integer); overload;
     procedure LogInternal(Level: TSynLogInfo; aName: PWinAnsiChar;
-     aTypeInfo: pointer; var aValue; Instance: TObject=nil); overload; 
+     aTypeInfo: pointer; var aValue; Instance: TObject=nil); overload;
     // any call to this method MUST call UnLock
     function LogHeaderLock(Level: TSynLogInfo): boolean;
     procedure LogTrailerUnLock(Level: TSynLogInfo); {$ifdef HASINLINE}inline;{$endif}
@@ -11048,6 +11052,7 @@ type
     function Instance: TSynLog;
     function ConsoleEcho(Sender: TTextWriter; Level: TSynLogInfo;
       const Text: RawUTF8): boolean; virtual;
+    procedure GenerateFileName; virtual;
   public
     /// intialize for a TSynLog class instance
     // - WARNING: not to be called directly! Use Enter or Add class function instead
@@ -41663,6 +41668,7 @@ begin
   fExceptionIgnore := TList.Create;
   fLevelStackTrace :=
     [sllError,sllException,sllExceptionOS,sllFail,sllLastError,sllStackTrace];
+  fLogExistsAction := acAppendWithHeader;
 end;
 
 function TSynLogFamily.CreateSynLog: TSynLog;
@@ -42644,48 +42650,61 @@ begin
   end;
 end;
 
-procedure TSynLog.CreateLogWriter;
+procedure TSynLog.GenerateFileName;
 {$ifndef MSWINDOWS}
 var i: integer;
 {$endif}
-var timeNow,hourRotate,timeBeforeRotate: TDateTime;
+var timeNow, hourRotate, timeBeforeRotate: TDateTime;
+begin
+  {$ifdef MSWINDOWS}
+  ExeVersionRetrieve;
+  fFileName := Ansi7ToString(ExeVersion.ProgramName);
+  if fFamily.IncludeComputerNameInFileName then
+    fFileName := fFileName+' ('+Ansi7ToString(ExeVersion.Host)+')';
+  {$else}
+  fFileName := ExtractFileName(ParamStr(0));
+  i := Pos('.',fFileName);
+  if i>0 then
+    SetLength(fFileName,i-1);
+  {$endif}
+  fFileRotationSize := 0;
+  if fFamily.fRotateFileCount>0 then begin
+    if fFamily.fRotateFileSize>0 then
+      fFileRotationSize := fFamily.fRotateFileSize shl 10; // size KB -> B
+    if fFamily.fRotateFileAtHour in [0..23] then begin
+      hourRotate := EncodeTime(fFamily.fRotateFileAtHour,0,0,0);
+      timeNow := Time;
+      if hourRotate<timeNow then
+        hourRotate := hourRotate+1; // trigger will be tomorrow
+      timeBeforeRotate := hourRotate-timeNow;
+      fFileRotationNextHour := GetTickCount64+trunc(timeBeforeRotate*MSecsPerDay);
+    end;
+  end;
+  if (fFileRotationSize=0) and (fFileRotationNextHour=0) then
+    fFileName := fFileName+' '+Ansi7ToString(NowToString(false));
+   {$ifdef MSWINDOWS}
+  if IsLibrary then
+    fFileName := fFileName+' '+ExtractFileName(GetModuleName(HInstance));
+  {$endif}
+  if fFamily.fPerThreadLog=ptOneFilePerThread then
+    fFileName := fFileName+' '+IntToString(GetCurrentThreadId);
+  fFileName := fFamily.fDestinationPath+fFileName+fFamily.fDefaultExtension;
+end;
+
+procedure TSynLog.CreateLogWriter;
 begin
   if fWriterStream=nil then begin
-    {$ifdef MSWINDOWS}
-    ExeVersionRetrieve;
-    fFileName := Ansi7ToString(ExeVersion.ProgramName);
-    if fFamily.IncludeComputerNameInFileName then
-      fFileName := fFileName+' ('+Ansi7ToString(ExeVersion.Host)+')';
-    {$else}
-    fFileName := ExtractFileName(ParamStr(0));
-    i := Pos('.',fFileName);
-    if i>0 then
-      SetLength(fFileName,i-1);
-    {$endif}
-    fFileRotationSize := 0;
-    if fFamily.fRotateFileCount>0 then begin
-      if fFamily.fRotateFileSize>0 then
-        fFileRotationSize := fFamily.fRotateFileSize shl 10; // size KB -> B
-      if fFamily.fRotateFileAtHour in [0..23] then begin
-        hourRotate := EncodeTime(fFamily.fRotateFileAtHour,0,0,0);
-        timeNow := Time;
-        if hourRotate<timeNow then
-          hourRotate := hourRotate+1; // trigger will be tomorrow
-        timeBeforeRotate := hourRotate-timeNow;
-        fFileRotationNextHour := GetTickCount64+trunc(timeBeforeRotate*MSecsPerDay);
-      end;
-    end;
-    if (fFileRotationSize=0) and (fFileRotationNextHour=0) then
-      fFileName := fFileName+' '+Ansi7ToString(NowToString(false));
-     {$ifdef MSWINDOWS}
-    if IsLibrary then
-      fFileName := fFileName+' '+ExtractFileName(GetModuleName(HInstance));
-    {$endif}
-    if fFamily.fPerThreadLog=ptOneFilePerThread then
-      fFileName := fFileName+' '+IntToString(GetCurrentThreadId);
-    fFileName := fFamily.fDestinationPath+fFileName+fFamily.fDefaultExtension;
+    GenerateFileName;
     if fFamily.NoFile then
-      fWriterStream := TFakeWriterStream.Create else begin
+      fWriterStream := TFakeWriterStream.Create else
+    begin
+      if FileExists(fFileName) then
+      begin
+        if fFamily.fLogExistsAction = acOverwrite then
+          DeleteFile(fFileName)
+        else if fFamily.fLogExistsAction = acAppend then
+          Include(fInternalFlags,logHeaderWritten);
+      end;
       if (fFileRotationSize=0) or not FileExists(fFileName) then
         TFileStream.Create(fFileName,fmCreate).Free;   // create a void file
       fWriterStream := TFileStream.Create(fFileName, // open with read sharing

Alternatively, if this is not possible, then I will subclass TSynLog. Thank you!

Last edited by cheemeng (2014-09-19 12:35:49)

Offline

#3 2014-09-19 16:05:34

cheemeng
Member
From: Malaysia
Registered: 2011-08-09
Posts: 61

Re: LogFile Custom Name

Additional updates (my previous patch was buggy and incomplete)...

procedure TSynLog.CreateLogWriter;
begin
  if fWriterStream=nil then begin
    GenerateFileName;
    if fFamily.NoFile then
      fWriterStream := TFakeWriterStream.Create else
    begin
      if FileExists(fFileName) and (fFamily.fLogExistsAction <> acOverwrite) then
      begin
        if fFamily.fLogExistsAction = acAppend then
          Include(fInternalFlags,logHeaderWritten);
      end else
      if (fFileRotationSize=0) or not FileExists(fFileName) then
        TFileStream.Create(fFileName,fmCreate).Free;   // create a void file
      fWriterStream := TFileStream.Create(fFileName, // open with read sharing
        fmOpenReadWrite or fmShareDenyWrite);
    end;
    if (fFileRotationSize>0) or (fFamily.fLogExistsAction <> acOverwrite) then
      fWriterStream.Seek(0,soFromEnd); // in rotation mode, append at the end
  end;
  if fWriterClass=nil then
    fWriterClass := TTextWriter;
  if fWriter=nil then
    fWriter := fWriterClass.Create(fWriterStream,fFamily.BufferSize);
  fWriter.EndOfLineCRLF := fFamily.EndOfLineCRLF;
  if integer(fFamily.EchoToConsole)<>0 then
    fWriter.EchoAdd(ConsoleEcho);
  if Assigned(fFamily.EchoCustom) then
    fWriter.EchoAdd(fFamily.EchoCustom);
  if Assigned(fFamily.fEchoRemoteClient) then
    fWriter.EchoAdd(fFamily.fEchoRemoteEvent);
end;

Offline

#4 2014-09-20 14:18:53

cheemeng
Member
From: Malaysia
Registered: 2011-08-09
Posts: 61

Re: LogFile Custom Name

Hi Arnaud,

Would just like to know what do you think of the proposed changes.

If it should not go to the main code, then let me know and I'll subclass it myself. Thanks!

Offline

#5 2014-09-26 18:04:05

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,182
Website

Re: LogFile Custom Name

Offline

#6 2014-09-27 02:02:04

cheemeng
Member
From: Malaysia
Registered: 2011-08-09
Posts: 61

Re: LogFile Custom Name

Thanks Arnaud!

Offline

#7 2014-09-28 18:21:12

cheemeng
Member
From: Malaysia
Registered: 2011-08-09
Posts: 61

Re: LogFile Custom Name

There is a bug that prevents log file appends from working correctly... it ends up overwriting the log file nevertheless if RotationSize is not set (Line 42753), which is why I proposed by code above.

To cater for your updated log file access retry code, the code can be updated to be as follows:

      for retry := 0 to 2 do begin
        for i := 1 to 10 do
        try
          if FileExists(fFileName) and (fFamily.FileExistsAction <> acOverwrite) then
          begin
            if fFamily.FileExistsAction = acAppend then
            Include(fInternalFlags,logHeaderWritten);
          end else if (fFileRotationSize=0) or not FileExists(fFileName) then
            TFileStream.Create(fFileName,fmCreate).Free;   // create a void file
          fWriterStream := TFileStream.Create(fFileName,
            fmOpenReadWrite or fmShareDenyWrite); // open with read sharing
          break;
        except
          on Exception do
            Sleep(100);
        end;
        if fWriterStream<>nil then
          break;
        fFileName := ChangeFileExt(fFileName,'-'+fFamily.fDefaultExtension);
      end;

Lines 42742 to 42748 are unnecessary.

Offline

#8 2014-09-28 18:51:12

cheemeng
Member
From: Malaysia
Registered: 2011-08-09
Posts: 61

Re: LogFile Custom Name

Line 42774 also needs amendment to

    if (fFileRotationSize>0) or (fFamily.FileExistsAction <> acOverwrite) then

Last edited by cheemeng (2014-09-28 18:52:00)

Offline

Board footer

Powered by FluxBB