Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

438 linhas
14 KiB

  1. unit uutlLogger;
  2. { Package: Utils
  3. Prefix: utl - UTiLs
  4. Beschreibung: diese Unit enthält das Logging-Framework
  5. Anzusprechen über Singleton: utlLogger
  6. Die einzelnen Level sind über die Methoden Debug(), Info(), Warning(), Error() zugänglich.
  7. Sender: entweder eigener Text oder TObject-Referenz, dann wird Klassenname und Adresse ausgegeben.
  8. Log-Zeilen werden nicht weiter behandelt, sondern an Consumer verteilt.
  9. Es können beliebig viele Consumer per RegisterConsumer für bestimmte Level registriert werden.
  10. Jeder davon bekommt die Rohdaten eines Logeintrags auf einer beobachteten Stufe.
  11. Zum einfacheren Ausgeben gibt es eine Hilfsfunktion FormatLine vom Logger.
  12. Vordefinierte Consumer:
  13. TutlFileLogger - schreibt in eine Datei
  14. TutlConsoleLogger - schreibt auf die Konsole (ggf. mit CriticalSection)
  15. TutlEventLogger - ruft beliebiges Event auf
  16. }
  17. {$mode objfpc}{$H+}
  18. interface
  19. uses
  20. {$IFDEF MSWINDOWS}Windows{$ELSE}unix{$ENDIF},
  21. Classes, SysUtils, uutlGenerics, syncobjs, uutlCommon;
  22. type
  23. TutlLogLevel = (llDebug, llInfo, llWarning, llError);
  24. TutlLogLevels = set of TutlLogLevel;
  25. const
  26. utlLogLevel_Any = [low(TutlLogLevel)..high(TutlLogLevel)];
  27. utlLogLevel_NoDebug = utlLogLevel_Any - [llDebug];
  28. utlLogLevelStrings: array[TutlLogLevel] of string =
  29. ('Debug','Info','Warning','Error');
  30. type
  31. TutlLogger = class;
  32. IutlLogConsumer = interface(IUnknown)
  33. procedure WriteLog(const aLogger: TutlLogger; const aTime:TDateTime; const aLevel:TutlLogLevel; const aSender: string; const aMessage: String);
  34. end;
  35. TutlLogConsumerList = specialize TutlInterfaceList<IutlLogConsumer>;
  36. { TutlLogger }
  37. TutlLogger = class(TObject)
  38. private
  39. fConsumersLock: TCriticalSection;
  40. fConsumers: array[TutlLogLevel] of TutlLogConsumerList;
  41. protected
  42. class function FormatTime(const aTime:TDateTime): string;
  43. function FormatSender(const aSender: TObject): String;
  44. procedure InternalLog(const aLevel:TutlLogLevel; const aSender: TObject; const aMessage: String; const aParams: array of const); overload;
  45. procedure InternalLog(const aLevel:TutlLogLevel; const aSender: String; const aMessage: String; const aParams: array of const); overload;
  46. public
  47. procedure RegisterConsumer(const aConsumer: IutlLogConsumer; const aFilter:TutlLogLevels=utlLogLevel_Any);
  48. procedure UnRegisterConsumer(const aConsumer: IutlLogConsumer; const aFilter:TutlLogLevels=utlLogLevel_Any);
  49. class function FormatLine(const aTime:TDateTime; const aLevel: TutlLogLevel; const aSender: string; const aMessage: String): string;
  50. procedure Debug(const aSender: TObject; const aMessage: String; const aParams: array of const); overload;
  51. procedure Debug(const aSender: String; const aMessage: String; const aParams: array of const); overload;
  52. procedure Log(const aSender: TObject; const aMessage: String; const aParams: array of const); overload;
  53. procedure Log(const aSender: String; const aMessage: String; const aParams: array of const); overload;
  54. procedure Warning(const aSender: TObject; const aMessage: String; const aParams: array of const); overload;
  55. procedure Warning(const aSender: String; const aMessage: String; const aParams: array of const); overload;
  56. procedure Error(const aSender: TObject; const aMessage: String; const aParams: array of const); overload;
  57. procedure Error(const aSender: String; const aMessage: String; const aParams: array of const); overload;
  58. procedure Error(const aSender: String; const aMessage: String; const aException: Exception); overload;
  59. procedure Error(const aSender: TObject; const aMessage: String; const aException: Exception); overload;
  60. constructor Create;
  61. destructor Destroy; override;
  62. end;
  63. { TutlFileLogger }
  64. TutlFileLoggerMode = (flmCreateNew, flmAppend);
  65. TutlFileLogger = class(TutlInterfaceNoRefCount, IutlLogConsumer)
  66. private
  67. fStream: TFileStream;
  68. fAutoFlush: boolean;
  69. protected
  70. procedure WriteLog(const aLogger: TutlLogger; const aTime: TDateTime; const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  71. public
  72. constructor Create(const aFilename: String; const aMode: TutlFileLoggerMode);
  73. destructor Destroy; override;
  74. procedure Flush(); overload;
  75. published
  76. property AutoFlush:boolean read fAutoFlush write fAutoFlush;
  77. end;
  78. { TutlConsoleLogger }
  79. TutlConsoleLogger = class(TutlInterfaceNoRefCount, IutlLogConsumer)
  80. private
  81. fFreeConsoleCS: boolean;
  82. fConsoleCS: TCriticalSection;
  83. fOnBeforeLog: TNotifyEvent;
  84. fOnAfterLog: TNotifyEvent;
  85. protected
  86. procedure WriteLog(const aLogger: TutlLogger; const aTime: TDateTime; const aLevel: TutlLogLevel; const aSender: string; const aMessage: String); virtual;
  87. public
  88. property ConsoleCS: TCriticalSection read fConsoleCS;
  89. property OnBeforeLog: TNotifyEvent read fOnBeforeLog write fOnBeforeLog;
  90. property OnAfterLog: TNotifyEvent read fOnAfterLog write fOnAfterLog;
  91. constructor Create(const aSection: TCriticalSection = nil);
  92. destructor Destroy; override;
  93. end;
  94. { TutlEventLogger }
  95. TutlWriteLogEvent = procedure (const aLogger: TutlLogger; const aTime: TDateTime; const aLevel: TutlLogLevel; const aSender: string; const aMessage: String) of object;
  96. TutlEventLogger = class(TutlInterfaceNoRefCount, IutlLogConsumer)
  97. private
  98. fWriteLogEvt: TutlWriteLogEvent;
  99. protected
  100. procedure WriteLog(const aLogger: TutlLogger; const aTime: TDateTime; const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  101. public
  102. constructor Create(const aEvent: TutlWriteLogEvent);
  103. end;
  104. function utlLogger: TutlLogger;
  105. function utlCreateStackTrace(const aMessage: String; const aException: Exception): String;
  106. implementation
  107. var
  108. utlLogger_Singleton: TutlLogger;
  109. function utlLogger: TutlLogger;
  110. begin
  111. if not Assigned(utlLogger_Singleton) then
  112. utlLogger_Singleton:= TutlLogger.Create;
  113. Result:= utlLogger_Singleton;
  114. end;
  115. function utlCreateStackTrace(const aMessage: String; const aException: Exception): String;
  116. var
  117. i: Integer;
  118. frames: PPointer;
  119. begin
  120. result := aMessage;
  121. if Assigned(aException) then
  122. result := result + sLineBreak +
  123. ' Exception: ' + aException.ClassName + sLineBreak +
  124. ' Message: ' + aException.Message + sLineBreak +
  125. ' Thread: ' + IntToStr(GetCurrentThreadId) + sLineBreak +
  126. ' StackTrace:' + sLineBreak +
  127. ' ' + BackTraceStrFunc(ExceptAddr)
  128. else
  129. result := result + 'no Exception passed';
  130. frames := ExceptFrames;
  131. for i := 0 to ExceptFrameCount-1 do
  132. result := result + sLineBreak + ' ' + BackTraceStrFunc(frames[i]);
  133. end;
  134. { TutlFileLogger }
  135. function FileFlush(Handle: THandle): Boolean;
  136. begin
  137. {$IFDEF MSWINDOWS}
  138. Result:= FlushFileBuffers(Handle);
  139. {$ELSE}
  140. Result:= (fpfsync(Handle) = 0);
  141. {$ENDIF}
  142. end;
  143. procedure TutlFileLogger.WriteLog(const aLogger: TutlLogger; const aTime: TDateTime;
  144. const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  145. var
  146. buf: AnsiString;
  147. begin
  148. if Assigned(fStream) then begin
  149. buf:= aLogger.FormatLine(aTime, aLevel, aSender, aMessage)+sLineBreak;
  150. fStream.Write(buf[1], Length(buf));
  151. if AutoFlush then
  152. FileFlush(fStream.Handle);
  153. end;
  154. end;
  155. constructor TutlFileLogger.Create(const aFilename: String; const aMode: TutlFileLoggerMode);
  156. const
  157. RIGHTS: Cardinal = {$IFNDEF UNIX}fmShareDenyWrite{$ELSE}%0100100100 {-r--r--r--}{$ENDIF};
  158. begin
  159. try
  160. if (aMode = flmCreateNew) or not FileExists(aFilename) then begin
  161. if FileExists(aFilename) then
  162. DeleteFile(aFilename);
  163. fStream := TFileStream.Create(aFilename, fmCreate{$IFNDEF UNIX} or RIGHTS{$ENDIF}, RIGHTS)
  164. end else
  165. fStream := TFileStream.Create(aFilename, fmOpenReadWrite{$IFNDEF UNIX} or RIGHTS{$ENDIF}, RIGHTS);
  166. fStream.Position := fStream.Size;
  167. except
  168. on e: EStreamError do begin
  169. fStream:= nil;
  170. utlLogger.Error('Logger', 'Could not open log file "%s"',[aFilename]);
  171. end else
  172. raise;
  173. end;
  174. AutoFlush:=true;
  175. end;
  176. destructor TutlFileLogger.Destroy;
  177. begin
  178. FreeAndNil(fStream);
  179. inherited Destroy;
  180. end;
  181. procedure TutlFileLogger.Flush;
  182. begin
  183. if Assigned(fStream) then
  184. FileFlush(fStream.Handle);
  185. end;
  186. { TutlConsoleLogger }
  187. procedure TutlConsoleLogger.WriteLog(const aLogger: TutlLogger; const aTime: TDateTime;
  188. const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  189. begin
  190. fConsoleCS.Acquire;
  191. try
  192. if Assigned(fOnBeforeLog) then
  193. fOnBeforeLog(Self);
  194. WriteLn(aLogger.FormatLine(aTime, aLevel, aSender, aMessage));
  195. if Assigned(fOnAfterLog) then
  196. fOnAfterLog(Self);
  197. finally
  198. fConsoleCS.Release;
  199. end;
  200. end;
  201. constructor TutlConsoleLogger.Create(const aSection: TCriticalSection);
  202. begin
  203. inherited Create;
  204. if Assigned(aSection) then
  205. fConsoleCS:= aSection
  206. else
  207. fConsoleCS:= TCriticalSection.Create;
  208. fFreeConsoleCS:= not Assigned(aSection);
  209. end;
  210. destructor TutlConsoleLogger.Destroy;
  211. begin
  212. if fFreeConsoleCS then
  213. FreeAndNil(fConsoleCS);
  214. inherited Destroy;
  215. end;
  216. { TutlEventLogger }
  217. procedure TutlEventLogger.WriteLog(const aLogger: TutlLogger; const aTime: TDateTime;
  218. const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  219. begin
  220. fWriteLogEvt(aLogger,aTime, aLevel, aSender, aMessage);
  221. end;
  222. constructor TutlEventLogger.Create(const aEvent: TutlWriteLogEvent);
  223. begin
  224. inherited Create;
  225. fWriteLogEvt:= aEvent;
  226. end;
  227. { TutlLogger }
  228. procedure TutlLogger.RegisterConsumer(const aConsumer: IutlLogConsumer; const aFilter: TutlLogLevels);
  229. var
  230. ll: TutlLogLevel;
  231. begin
  232. fConsumersLock.Acquire;
  233. try
  234. for ll:= low(ll) to high(ll) do
  235. if (ll in aFilter) and (fConsumers[ll].IndexOf(aConsumer)<0) then
  236. fConsumers[ll].Add(aConsumer);
  237. finally
  238. fConsumersLock.Release;
  239. end;
  240. if llDebug in aFilter then
  241. aConsumer.WriteLog(Self, Now, llDebug, 'System', 'Attached to Logger');
  242. end;
  243. procedure TutlLogger.UnRegisterConsumer(const aConsumer: IutlLogConsumer; const aFilter: TutlLogLevels);
  244. var
  245. ll: TutlLogLevel;
  246. begin
  247. fConsumersLock.Acquire;
  248. try
  249. for ll:= low(ll) to high(ll) do
  250. if ll in aFilter then
  251. fConsumers[ll].Remove(aConsumer);
  252. finally
  253. fConsumersLock.Release;
  254. end;
  255. end;
  256. class function TutlLogger.FormatTime(const aTime: TDateTime): string;
  257. begin
  258. Result:= FormatDateTime('hh:nn:ss.zzz',aTime);
  259. end;
  260. function TutlLogger.FormatSender(const aSender: TObject): String;
  261. begin
  262. if Assigned(aSender) then
  263. result := format('%s[0x%P]', [aSender.ClassName, Pointer(aSender)])
  264. else
  265. result := '';
  266. end;
  267. class function TutlLogger.FormatLine(const aTime: TDateTime; const aLevel: TutlLogLevel; const aSender: string; const aMessage: String): string;
  268. begin
  269. if (aSender <> '') then
  270. Result:= Format('%s %-9s %s: %s', [FormatTime(aTime), UpperCase(utlLogLevelStrings[aLevel]), aSender, aMessage])
  271. else
  272. Result:= Format('%s %-9s %s', [FormatTime(aTime), UpperCase(utlLogLevelStrings[aLevel]), aMessage]);
  273. end;
  274. procedure TutlLogger.InternalLog(const aLevel: TutlLogLevel; const aSender: TObject; const aMessage: String; const aParams: array of const);
  275. begin
  276. InternalLog(aLevel, FormatSender(aSender), aMessage, aParams);
  277. end;
  278. procedure TutlLogger.InternalLog(const aLevel: TutlLogLevel; const aSender: String; const aMessage: String; const aParams: array of const);
  279. var
  280. msg: string;
  281. when: TDateTime;
  282. i: integer;
  283. begin
  284. if length(aParams) = 0 then
  285. msg:= aMessage
  286. else
  287. msg:= Format(aMessage, aParams);
  288. when:= Now;
  289. fConsumersLock.Acquire;
  290. try
  291. for i:= 0 to fConsumers[aLevel].Count-1 do begin
  292. fConsumers[aLevel][i].WriteLog(Self, when, aLevel, aSender, msg);
  293. end;
  294. finally
  295. fConsumersLock.Release;
  296. end;
  297. end;
  298. procedure TutlLogger.Debug(const aSender: TObject; const aMessage: String; const aParams: array of const);
  299. begin
  300. InternalLog(llDebug, aSender, aMessage, aParams);
  301. end;
  302. procedure TutlLogger.Debug(const aSender: String; const aMessage: String; const aParams: array of const);
  303. begin
  304. InternalLog(llDebug, aSender, aMessage, aParams);
  305. end;
  306. procedure TutlLogger.Log(const aSender: TObject; const aMessage: String; const aParams: array of const);
  307. begin
  308. InternalLog(llInfo, aSender, aMessage, aParams);
  309. end;
  310. procedure TutlLogger.Log(const aSender: String; const aMessage: String; const aParams: array of const);
  311. begin
  312. InternalLog(llInfo, aSender, aMessage, aParams);
  313. end;
  314. procedure TutlLogger.Warning(const aSender: TObject; const aMessage: String; const aParams: array of const);
  315. begin
  316. InternalLog(llWarning, aSender, aMessage, aParams);
  317. end;
  318. procedure TutlLogger.Warning(const aSender: String; const aMessage: String; const aParams: array of const);
  319. begin
  320. InternalLog(llWarning, aSender, aMessage, aParams);
  321. end;
  322. procedure TutlLogger.Error(const aSender: TObject; const aMessage: String; const aParams: array of const);
  323. begin
  324. InternalLog(llError, aSender, aMessage, aParams);
  325. end;
  326. procedure TutlLogger.Error(const aSender: String; const aMessage: String; const aParams: array of const);
  327. begin
  328. InternalLog(llError, aSender, aMessage, aParams);
  329. end;
  330. procedure TutlLogger.Error(const aSender: String; const aMessage: String; const aException: Exception);
  331. begin
  332. InternalLog(llError, aSender, utlCreateStackTrace(aMessage, aException), []);
  333. end;
  334. procedure TutlLogger.Error(const aSender: TObject; const aMessage: String; const aException: Exception);
  335. begin
  336. InternalLog(llError, aSender, utlCreateStackTrace(aMessage, aException), []);
  337. end;
  338. constructor TutlLogger.Create;
  339. var
  340. ll: TutlLogLevel;
  341. begin
  342. inherited Create;
  343. fConsumersLock:= TCriticalSection.Create;
  344. fConsumersLock.Acquire;
  345. try
  346. for ll:= low(ll) to high(ll) do begin
  347. fConsumers[ll]:= TutlLogConsumerList.Create;
  348. end;
  349. finally
  350. fConsumersLock.Release;
  351. end;
  352. end;
  353. destructor TutlLogger.Destroy;
  354. var
  355. ll: TutlLogLevel;
  356. begin
  357. fConsumersLock.Acquire;
  358. try
  359. for ll:= low(ll) to high(ll) do begin
  360. fConsumers[ll].Clear;
  361. FreeAndNil(fConsumers[ll]);
  362. end;
  363. finally
  364. fConsumersLock.Release;
  365. end;
  366. FreeAndNil(fConsumersLock);
  367. inherited Destroy;
  368. end;
  369. finalization
  370. FreeAndNil(utlLogger_Singleton);
  371. end.