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.

437 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. ' StackTrace:' + sLineBreak +
  126. ' ' + BackTraceStrFunc(ExceptAddr)
  127. else
  128. result := result + 'no Exception passed';
  129. frames := ExceptFrames;
  130. for i := 0 to ExceptFrameCount-1 do
  131. result := result + sLineBreak + ' ' + BackTraceStrFunc(frames[i]);
  132. end;
  133. { TutlFileLogger }
  134. function FileFlush(Handle: THandle): Boolean;
  135. begin
  136. {$IFDEF MSWINDOWS}
  137. Result:= FlushFileBuffers(Handle);
  138. {$ELSE}
  139. Result:= (fpfsync(Handle) = 0);
  140. {$ENDIF}
  141. end;
  142. procedure TutlFileLogger.WriteLog(const aLogger: TutlLogger; const aTime: TDateTime;
  143. const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  144. var
  145. buf: AnsiString;
  146. begin
  147. if Assigned(fStream) then begin
  148. buf:= aLogger.FormatLine(aTime, aLevel, aSender, aMessage)+sLineBreak;
  149. fStream.Write(buf[1], Length(buf));
  150. if AutoFlush then
  151. FileFlush(fStream.Handle);
  152. end;
  153. end;
  154. constructor TutlFileLogger.Create(const aFilename: String; const aMode: TutlFileLoggerMode);
  155. const
  156. RIGHTS: Cardinal = {$IFNDEF UNIX}fmShareDenyWrite{$ELSE}%0100100100 {-r--r--r--}{$ENDIF};
  157. begin
  158. try
  159. if (aMode = flmCreateNew) or not FileExists(aFilename) then begin
  160. if FileExists(aFilename) then
  161. DeleteFile(aFilename);
  162. fStream := TFileStream.Create(aFilename, fmCreate{$IFNDEF UNIX} or RIGHTS{$ENDIF}, RIGHTS)
  163. end else
  164. fStream := TFileStream.Create(aFilename, fmOpenReadWrite{$IFNDEF UNIX} or RIGHTS{$ENDIF}, RIGHTS);
  165. fStream.Position := fStream.Size;
  166. except
  167. on e: EStreamError do begin
  168. fStream:= nil;
  169. utlLogger.Error('Logger', 'Could not open log file "%s"',[aFilename]);
  170. end else
  171. raise;
  172. end;
  173. AutoFlush:=true;
  174. end;
  175. destructor TutlFileLogger.Destroy;
  176. begin
  177. FreeAndNil(fStream);
  178. inherited Destroy;
  179. end;
  180. procedure TutlFileLogger.Flush;
  181. begin
  182. if Assigned(fStream) then
  183. FileFlush(fStream.Handle);
  184. end;
  185. { TutlConsoleLogger }
  186. procedure TutlConsoleLogger.WriteLog(const aLogger: TutlLogger; const aTime: TDateTime;
  187. const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  188. begin
  189. fConsoleCS.Acquire;
  190. try
  191. if Assigned(fOnBeforeLog) then
  192. fOnBeforeLog(Self);
  193. WriteLn(aLogger.FormatLine(aTime, aLevel, aSender, aMessage));
  194. if Assigned(fOnAfterLog) then
  195. fOnAfterLog(Self);
  196. finally
  197. fConsoleCS.Release;
  198. end;
  199. end;
  200. constructor TutlConsoleLogger.Create(const aSection: TCriticalSection);
  201. begin
  202. inherited Create;
  203. if Assigned(aSection) then
  204. fConsoleCS:= aSection
  205. else
  206. fConsoleCS:= TCriticalSection.Create;
  207. fFreeConsoleCS:= not Assigned(aSection);
  208. end;
  209. destructor TutlConsoleLogger.Destroy;
  210. begin
  211. if fFreeConsoleCS then
  212. FreeAndNil(fConsoleCS);
  213. inherited Destroy;
  214. end;
  215. { TutlEventLogger }
  216. procedure TutlEventLogger.WriteLog(const aLogger: TutlLogger; const aTime: TDateTime;
  217. const aLevel: TutlLogLevel; const aSender: string; const aMessage: String);
  218. begin
  219. fWriteLogEvt(aLogger,aTime, aLevel, aSender, aMessage);
  220. end;
  221. constructor TutlEventLogger.Create(const aEvent: TutlWriteLogEvent);
  222. begin
  223. inherited Create;
  224. fWriteLogEvt:= aEvent;
  225. end;
  226. { TutlLogger }
  227. procedure TutlLogger.RegisterConsumer(const aConsumer: IutlLogConsumer; const aFilter: TutlLogLevels);
  228. var
  229. ll: TutlLogLevel;
  230. begin
  231. fConsumersLock.Acquire;
  232. try
  233. for ll:= low(ll) to high(ll) do
  234. if (ll in aFilter) and (fConsumers[ll].IndexOf(aConsumer)<0) then
  235. fConsumers[ll].Add(aConsumer);
  236. finally
  237. fConsumersLock.Release;
  238. end;
  239. if llDebug in aFilter then
  240. aConsumer.WriteLog(Self, Now, llDebug, 'System', 'Attached to Logger');
  241. end;
  242. procedure TutlLogger.UnRegisterConsumer(const aConsumer: IutlLogConsumer; const aFilter: TutlLogLevels);
  243. var
  244. ll: TutlLogLevel;
  245. begin
  246. fConsumersLock.Acquire;
  247. try
  248. for ll:= low(ll) to high(ll) do
  249. if ll in aFilter then
  250. fConsumers[ll].Remove(aConsumer);
  251. finally
  252. fConsumersLock.Release;
  253. end;
  254. end;
  255. class function TutlLogger.FormatTime(const aTime: TDateTime): string;
  256. begin
  257. Result:= FormatDateTime('hh:nn:ss.zzz',aTime);
  258. end;
  259. function TutlLogger.FormatSender(const aSender: TObject): String;
  260. begin
  261. if Assigned(aSender) then
  262. result := format('%s[0x%P]', [aSender.ClassName, Pointer(aSender)])
  263. else
  264. result := '';
  265. end;
  266. class function TutlLogger.FormatLine(const aTime: TDateTime; const aLevel: TutlLogLevel; const aSender: string; const aMessage: String): string;
  267. begin
  268. if (aSender <> '') then
  269. Result:= Format('%s %-9s %s: %s', [FormatTime(aTime), UpperCase(utlLogLevelStrings[aLevel]), aSender, aMessage])
  270. else
  271. Result:= Format('%s %-9s %s', [FormatTime(aTime), UpperCase(utlLogLevelStrings[aLevel]), aMessage]);
  272. end;
  273. procedure TutlLogger.InternalLog(const aLevel: TutlLogLevel; const aSender: TObject; const aMessage: String; const aParams: array of const);
  274. begin
  275. InternalLog(aLevel, FormatSender(aSender), aMessage, aParams);
  276. end;
  277. procedure TutlLogger.InternalLog(const aLevel: TutlLogLevel; const aSender: String; const aMessage: String; const aParams: array of const);
  278. var
  279. msg: string;
  280. when: TDateTime;
  281. i: integer;
  282. begin
  283. if length(aParams) = 0 then
  284. msg:= aMessage
  285. else
  286. msg:= Format(aMessage, aParams);
  287. when:= Now;
  288. fConsumersLock.Acquire;
  289. try
  290. for i:= 0 to fConsumers[aLevel].Count-1 do begin
  291. fConsumers[aLevel][i].WriteLog(Self, when, aLevel, aSender, msg);
  292. end;
  293. finally
  294. fConsumersLock.Release;
  295. end;
  296. end;
  297. procedure TutlLogger.Debug(const aSender: TObject; const aMessage: String; const aParams: array of const);
  298. begin
  299. InternalLog(llDebug, aSender, aMessage, aParams);
  300. end;
  301. procedure TutlLogger.Debug(const aSender: String; const aMessage: String; const aParams: array of const);
  302. begin
  303. InternalLog(llDebug, aSender, aMessage, aParams);
  304. end;
  305. procedure TutlLogger.Log(const aSender: TObject; const aMessage: String; const aParams: array of const);
  306. begin
  307. InternalLog(llInfo, aSender, aMessage, aParams);
  308. end;
  309. procedure TutlLogger.Log(const aSender: String; const aMessage: String; const aParams: array of const);
  310. begin
  311. InternalLog(llInfo, aSender, aMessage, aParams);
  312. end;
  313. procedure TutlLogger.Warning(const aSender: TObject; const aMessage: String; const aParams: array of const);
  314. begin
  315. InternalLog(llWarning, aSender, aMessage, aParams);
  316. end;
  317. procedure TutlLogger.Warning(const aSender: String; const aMessage: String; const aParams: array of const);
  318. begin
  319. InternalLog(llWarning, aSender, aMessage, aParams);
  320. end;
  321. procedure TutlLogger.Error(const aSender: TObject; const aMessage: String; const aParams: array of const);
  322. begin
  323. InternalLog(llError, aSender, aMessage, aParams);
  324. end;
  325. procedure TutlLogger.Error(const aSender: String; const aMessage: String; const aParams: array of const);
  326. begin
  327. InternalLog(llError, aSender, aMessage, aParams);
  328. end;
  329. procedure TutlLogger.Error(const aSender: String; const aMessage: String; const aException: Exception);
  330. begin
  331. InternalLog(llError, aSender, utlCreateStackTrace(aMessage, aException), []);
  332. end;
  333. procedure TutlLogger.Error(const aSender: TObject; const aMessage: String; const aException: Exception);
  334. begin
  335. InternalLog(llError, aSender, utlCreateStackTrace(aMessage, aException), []);
  336. end;
  337. constructor TutlLogger.Create;
  338. var
  339. ll: TutlLogLevel;
  340. begin
  341. inherited Create;
  342. fConsumersLock:= TCriticalSection.Create;
  343. fConsumersLock.Acquire;
  344. try
  345. for ll:= low(ll) to high(ll) do begin
  346. fConsumers[ll]:= TutlLogConsumerList.Create;
  347. end;
  348. finally
  349. fConsumersLock.Release;
  350. end;
  351. end;
  352. destructor TutlLogger.Destroy;
  353. var
  354. ll: TutlLogLevel;
  355. begin
  356. fConsumersLock.Acquire;
  357. try
  358. for ll:= low(ll) to high(ll) do begin
  359. fConsumers[ll].Clear;
  360. FreeAndNil(fConsumers[ll]);
  361. end;
  362. finally
  363. fConsumersLock.Release;
  364. end;
  365. FreeAndNil(fConsumersLock);
  366. inherited Destroy;
  367. end;
  368. finalization
  369. FreeAndNil(utlLogger_Singleton);
  370. end.