Setup.LoggingFunc.pas 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. unit Setup.LoggingFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2024 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Logging functions
  8. }
  9. interface
  10. procedure Log(const S: String);
  11. procedure LogFmt(const S: String; const Args: array of const);
  12. procedure StartLogging(const Prefix: String);
  13. procedure StartLoggingWithFixedFilename(const Filename: String);
  14. function GetLogFileName: String;
  15. function GetLogActive: Boolean; { Returns True if logging was started or when debugging from the IDE }
  16. const
  17. SYesNo: array[Boolean] of String = ('No', 'Yes');
  18. implementation
  19. uses
  20. Windows, SysUtils, Shared.Int64Em, Shared.CommonFunc, Shared.FileClass, Setup.DebugClient;
  21. var
  22. LogFile: TTextFileWriter;
  23. LogFileName: String;
  24. LocalTimeBias: Integer64;
  25. procedure InitLocalTimeBias;
  26. var
  27. UTCTime, LocalTime: Integer64;
  28. begin
  29. GetSystemTimeAsFileTime(TFileTime(UTCTime));
  30. if FileTimeToLocalFileTime(TFileTime(UTCTime), TFileTime(LocalTime)) then begin
  31. Dec6464(LocalTime, UTCTime);
  32. LocalTimeBias := LocalTime;
  33. end;
  34. end;
  35. procedure GetFixedLocalTime(var ST: TSystemTime);
  36. { Like GetLocalTime, but uses our LocalTimeBias as the offset, which cannot
  37. change while the program is running }
  38. var
  39. FT: Integer64;
  40. begin
  41. GetSystemTimeAsFileTime(TFileTime(FT));
  42. Inc6464(FT, LocalTimeBias);
  43. FileTimeToSystemTime(TFileTime(FT), ST);
  44. end;
  45. procedure LogLogOpened;
  46. var
  47. Offset: Integer64;
  48. PlusOrMinus: Char;
  49. begin
  50. Offset := LocalTimeBias;
  51. if Longint(Offset.Hi) >= 0 then
  52. PlusOrMinus := '+'
  53. else begin
  54. PlusOrMinus := '-';
  55. { Negate it }
  56. Offset.Lo := not Offset.Lo;
  57. Offset.Hi := not Offset.Hi;
  58. Inc64(Offset, 1);
  59. end;
  60. Div64(Offset, 60 * 10000000);
  61. LogFmt('Log opened. (Time zone: UTC%s%.2u:%.2u)', [PlusOrMinus,
  62. Offset.Lo div 60, Offset.Lo mod 60]);
  63. end;
  64. procedure StartLogging(const Prefix: String);
  65. var
  66. Dir, DateStr, Filename: String;
  67. I: Cardinal;
  68. ST: TSystemTime;
  69. F: TTextFileWriter;
  70. begin
  71. if Assigned(LogFile) then
  72. Exit; { logging was already started }
  73. Dir := GetTempDir;
  74. GetFixedLocalTime(ST);
  75. DateStr := Format('%.4u-%.2u-%.2u', [ST.wYear, ST.wMonth, ST.wDay]);
  76. I := 1;
  77. while True do begin
  78. Filename := Dir + Format('%s Log %s #%.3u.txt', [Prefix, DateStr, I]);
  79. if not FileOrDirExists(Filename) then begin
  80. F := nil;
  81. try
  82. F := TTextFileWriter.Create(Filename, fdCreateNew, faWrite, fsRead);
  83. except
  84. on E: EFileError do begin
  85. { Don't propagate ERROR_FILE_EXISTS errors; just try again.
  86. (Yes, we already checked if the file existed first, but this helps
  87. to make it race-proof.) }
  88. if E.ErrorCode <> ERROR_FILE_EXISTS then
  89. raise;
  90. end;
  91. end;
  92. if Assigned(F) then begin
  93. LogFile := F;
  94. LogFileName := FileName;
  95. Break;
  96. end;
  97. end;
  98. Inc(I);
  99. end;
  100. LogLogOpened;
  101. end;
  102. procedure StartLoggingWithFixedFilename(const Filename: String);
  103. begin
  104. if Assigned(LogFile) then
  105. Exit; { logging was already started }
  106. LogFile := TTextFileWriter.Create(Filename, fdCreateAlways, faWrite, fsRead);
  107. LogFileName := FileName;
  108. LogLogOpened;
  109. end;
  110. function GetLogFileName: String;
  111. begin
  112. Result := LogFileName;
  113. end;
  114. function GetLogActive: Boolean;
  115. begin
  116. Result := Assigned(LogFile) or Debugging;
  117. end;
  118. procedure Log(const S: String);
  119. procedure WriteStr(const S: String);
  120. begin
  121. LogFile.Write(S);
  122. end;
  123. var
  124. ST: TSystemTime;
  125. LineStart, I: Integer;
  126. begin
  127. if Assigned(LogFile) then begin
  128. GetFixedLocalTime(ST);
  129. try
  130. WriteStr(Format('%.4u-%.2u-%.2u %.2u:%.2u:%.2u.%.3u ',
  131. [ST.wYear, ST.wMonth, ST.wDay, ST.wHour, ST.wMinute, ST.wSecond,
  132. ST.wMilliseconds]));
  133. LineStart := 1;
  134. { Lines except for last line }
  135. for I := 1 to Length(S) do begin
  136. if S[I] = #10 then begin
  137. WriteStr(Copy(S, LineStart, I - LineStart + 1));
  138. LineStart := I + 1;
  139. { Indent }
  140. WriteStr(' ');
  141. end;
  142. end;
  143. { Last line }
  144. if LineStart <= Length(S) then
  145. WriteStr(Copy(S, LineStart, Length(S) - LineStart + 1));
  146. WriteStr(#13#10);
  147. except
  148. { Failed to write? Close the file and don't log anything further. }
  149. try
  150. FreeAndNil(LogFile);
  151. except
  152. end;
  153. end;
  154. end;
  155. if Debugging then
  156. DebugNotifyLogMessage(S);
  157. end;
  158. procedure LogFmt(const S: String; const Args: array of const);
  159. begin
  160. if GetLogActive then
  161. Log(Format(S, Args));
  162. end;
  163. initialization
  164. InitLocalTimeBias;
  165. finalization
  166. if Assigned(LogFile) then begin
  167. Log('Log closed.');
  168. FreeAndNil(LogFile);
  169. end;
  170. end.