Setup.LoggingFunc.pas 5.0 KB

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