Logging.pas 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. unit Logging;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2007 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Logging functions
  8. $jrsoftware: issrc/Projects/Logging.pas,v 1.12 2009/03/23 23:27:14 mlaan Exp $
  9. }
  10. interface
  11. procedure Log(const S: String);
  12. procedure LogFmt(const S: String; const Args: array of const);
  13. procedure StartLogging(const Prefix: String);
  14. procedure StartLoggingWithFixedFilename(const Filename: String);
  15. function GetLogFileName: String;
  16. const
  17. SYesNo: array[Boolean] of String = ('No', 'Yes');
  18. implementation
  19. uses
  20. Windows, SysUtils, Int64Em, CmnFunc2, FileClass, 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. procedure Log(const S: String);
  115. procedure WriteStr(const S: String);
  116. begin
  117. LogFile.Write(S);
  118. end;
  119. var
  120. ST: TSystemTime;
  121. LineStart, I: Integer;
  122. begin
  123. if Assigned(LogFile) then begin
  124. GetFixedLocalTime(ST);
  125. try
  126. WriteStr(Format('%.4u-%.2u-%.2u %.2u:%.2u:%.2u.%.3u ',
  127. [ST.wYear, ST.wMonth, ST.wDay, ST.wHour, ST.wMinute, ST.wSecond,
  128. ST.wMilliseconds]));
  129. LineStart := 1;
  130. { Lines except for last line }
  131. for I := 1 to Length(S) do begin
  132. if S[I] = #10 then begin
  133. WriteStr(Copy(S, LineStart, I - LineStart + 1));
  134. LineStart := I + 1;
  135. { Indent }
  136. WriteStr(' ');
  137. end;
  138. end;
  139. { Last line }
  140. if LineStart <= Length(S) then
  141. WriteStr(Copy(S, LineStart, Length(S) - LineStart + 1));
  142. WriteStr(#13#10);
  143. except
  144. { Failed to write? Close the file and don't log anything further. }
  145. try
  146. FreeAndNil(LogFile);
  147. except
  148. end;
  149. end;
  150. end;
  151. if Debugging then
  152. DebugNotifyLogMessage(S);
  153. end;
  154. procedure LogFmt(const S: String; const Args: array of const);
  155. begin
  156. if Assigned(LogFile) or Debugging then
  157. Log(Format(S, Args));
  158. end;
  159. initialization
  160. InitLocalTimeBias;
  161. finalization
  162. if Assigned(LogFile) then begin
  163. Log('Log closed.');
  164. FreeAndNil(LogFile);
  165. end;
  166. end.