ULog.pas 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. unit ULog;
  2. { Copyright (c) 2016 by Albert Molina
  3. Distributed under the MIT software license, see the accompanying file LICENSE
  4. or visit http://www.opensource.org/licenses/mit-license.php.
  5. This unit is a part of the PascalCoin Project, an infinitely scalable
  6. cryptocurrency. Find us here:
  7. Web: https://www.pascalcoin.org
  8. Source: https://github.com/PascalCoin/PascalCoin
  9. If you like it, consider a donation using Bitcoin:
  10. 16K3HCZRhFUtM8GdWRcfKeaa6KsuyxZaYk
  11. THIS LICENSE HEADER MUST NOT BE REMOVED.
  12. }
  13. {$IFDEF FPC}
  14. {$MODE Delphi}
  15. {$ENDIF}
  16. interface
  17. uses
  18. Classes, UThread, SyncObjs, UConst;
  19. type
  20. TLogType = (ltinfo, ltupdate, lterror, ltdebug);
  21. TLogTypes = set of TLogType;
  22. TNewLogEvent = procedure(logtype : TLogType; Time : TDateTime; ThreadID : TThreadID; Const sender, logtext : AnsiString) of object;
  23. TLog = Class;
  24. { TThreadSafeLogEvent }
  25. TThreadSafeLogEvent = Class(TPCThread)
  26. FLog : TLog;
  27. Procedure SynchronizedProcess;
  28. protected
  29. procedure BCExecute; override;
  30. public
  31. Constructor Create(Suspended : Boolean);
  32. End;
  33. TLogData = Record
  34. Logtype : TLogType;
  35. Time : TDateTime;
  36. ThreadID : TThreadID;
  37. Sender, Logtext : AnsiString
  38. End;
  39. TLog = Class(TComponent)
  40. private
  41. FLogDataList : TThreadList;
  42. FOnNewLog: TNewLogEvent;
  43. FOnInThreadNewLog : TNewLogEvent;
  44. FFileStream : TFileStream;
  45. FFileName: AnsiString;
  46. FSaveTypes: TLogTypes;
  47. FThreadSafeLogEvent : TThreadSafeLogEvent;
  48. FProcessGlobalLogs: Boolean;
  49. FLock : TCriticalSection;
  50. procedure SetFileName(const Value: AnsiString);
  51. protected
  52. Procedure DoLog(logtype : TLogType; sender, logtext : AnsiString); virtual;
  53. public
  54. Constructor Create(AOwner : TComponent); override;
  55. Destructor Destroy; override;
  56. Class Procedure NewLog(logtype : TLogType; Const sender, logtext : String);
  57. Property OnInThreadNewLog : TNewLogEvent read FOnInThreadNewLog write FOnInThreadNewLog;
  58. Property OnNewLog : TNewLogEvent read FOnNewLog write FOnNewLog;
  59. Property FileName : AnsiString read FFileName write SetFileName;
  60. Property SaveTypes : TLogTypes read FSaveTypes write FSaveTypes;
  61. Property ProcessGlobalLogs : Boolean read FProcessGlobalLogs write FProcessGlobalLogs;
  62. Procedure NotifyNewLog(logtype : TLogType; Const sender, logtext : String);
  63. End;
  64. Const
  65. CT_LogType : Array[TLogType] of AnsiString = ('Info','Update','Error','Debug');
  66. CT_TLogTypes_ALL : TLogTypes = [ltinfo, ltupdate, lterror, ltdebug];
  67. CT_TLogTypes_DEFAULT : TLogTypes = [ltinfo, ltupdate, lterror];
  68. implementation
  69. uses SysUtils;
  70. var _logs : TList;
  71. Type PLogData = ^TLogData;
  72. { TLog }
  73. constructor TLog.Create(AOwner: TComponent);
  74. begin
  75. FLock := TCriticalSection.Create;
  76. FProcessGlobalLogs := true;
  77. FLogDataList := TThreadList.Create;
  78. FFileStream := Nil;
  79. FFileName := '';
  80. FSaveTypes := CT_TLogTypes_DEFAULT;
  81. FOnInThreadNewLog:=Nil;
  82. FOnNewLog:=Nil;
  83. if (Not assigned(_logs)) then _logs := TList.Create;
  84. _logs.Add(self);
  85. FThreadSafeLogEvent := TThreadSafeLogEvent.Create(true);
  86. FThreadSafeLogEvent.FLog := Self;
  87. FThreadSafeLogEvent.Suspended := false;
  88. inherited;
  89. end;
  90. destructor TLog.Destroy;
  91. var
  92. l : TList;
  93. i : Integer;
  94. P : PLogData;
  95. begin
  96. FOnNewLog:=Nil;
  97. FOnInThreadNewLog:=Nil;
  98. FThreadSafeLogEvent.Terminate;
  99. FThreadSafeLogEvent.WaitFor;
  100. FreeAndNil(FThreadSafeLogEvent);
  101. _logs.Remove(Self);
  102. FreeAndNil(FFileStream);
  103. l := FLogDataList.LockList;
  104. try
  105. for i := 0 to l.Count - 1 do begin
  106. P := PLogData(l[i]);
  107. Dispose(P);
  108. end;
  109. l.Clear;
  110. finally
  111. FLogDataList.UnlockList;
  112. end;
  113. FreeAndNil(FLogDataList);
  114. FreeAndNil(FLock);
  115. inherited;
  116. end;
  117. procedure TLog.DoLog(logtype: TLogType; sender, logtext: AnsiString);
  118. begin
  119. //
  120. end;
  121. class procedure TLog.NewLog(logtype: TLogType; Const sender, logtext: String);
  122. var i : Integer;
  123. begin
  124. if (Not Assigned(_logs)) then exit;
  125. for i := 0 to _logs.Count - 1 do begin
  126. if (TLog(_logs[i]).FProcessGlobalLogs) then begin
  127. TLog(_logs[i]).NotifyNewLog(logtype,sender,logtext);
  128. end;
  129. end;
  130. end;
  131. procedure TLog.NotifyNewLog(logtype: TLogType; Const sender, logtext: String);
  132. Var s,tid : AnsiString;
  133. P : PLogData;
  134. begin
  135. FLock.Acquire;
  136. try
  137. if assigned(FFileStream) And (logType in FSaveTypes) then begin
  138. if TThread.CurrentThread.ThreadID=MainThreadID then tid := ' MAIN:' else tid:=' TID:';
  139. s := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz',now)+tid+IntToHex(PtrInt(TThread.CurrentThread.ThreadID),8)+' ['+CT_LogType[logtype]+'] <'+sender+'> '+logtext+#13#10;
  140. FFileStream.Write(s[1],length(s));
  141. end;
  142. if Assigned(FOnInThreadNewLog) then begin
  143. FOnInThreadNewLog(logtype,now,TThread.CurrentThread.ThreadID,sender,logtext);
  144. end;
  145. if Assigned(FOnNewLog) then begin
  146. // Add to a thread safe list
  147. New(P);
  148. P^.Logtype := logtype;
  149. P^.Time := now;
  150. P^.ThreadID :=TThread.CurrentThread.ThreadID;
  151. P^.Sender := sender;
  152. P^.Logtext := logtext;
  153. FLogDataList.Add(P);
  154. end;
  155. finally
  156. FLock.Release;
  157. end;
  158. DoLog(logtype,sender,logtext);
  159. end;
  160. procedure TLog.SetFileName(const Value: AnsiString);
  161. var fm : Word;
  162. begin
  163. if FFileName = Value then exit;
  164. if assigned(FFileStream) then Begin
  165. FreeAndNil(FFileStream);
  166. End;
  167. FFileName := Value;
  168. if (FFileName<>'') then begin
  169. If Not ForceDirectories(ExtractFileDir(FFileName)) then exit;
  170. if FileExists(FFileName) then fm := fmOpenWrite + fmShareDenyWrite
  171. else fm := fmCreate + fmShareDenyWrite;
  172. FFileStream := TFileStream.Create(FFileName,fm);
  173. FFileStream.Position := FFileStream.size; // To the end!
  174. NotifyNewLog(ltinfo,Classname,'Log file start: '+FFileName);
  175. end;
  176. end;
  177. { TThreadSafeLogEvent }
  178. procedure TThreadSafeLogEvent.BCExecute;
  179. begin
  180. while (not Terminated) do begin
  181. sleep(100);
  182. If (Not Terminated) And (Assigned(FLog.OnNewLog)) then begin
  183. Synchronize(SynchronizedProcess);
  184. end;
  185. end;
  186. end;
  187. constructor TThreadSafeLogEvent.Create(Suspended: Boolean);
  188. begin
  189. inherited Create(Suspended);
  190. end;
  191. procedure TThreadSafeLogEvent.SynchronizedProcess;
  192. Var l : TList;
  193. i : Integer;
  194. P : PLogData;
  195. begin
  196. If Not Assigned(FLog) then Exit;
  197. If Not Assigned(FLog.FOnNewLog) then Exit;
  198. // This event is thread safe and will do OnNewLog on main thread
  199. l := FLog.FLogDataList.LockList;
  200. try
  201. try
  202. for i := 0 to l.Count - 1 do begin
  203. P := PLogData(l[i]);
  204. if Assigned(FLog.FOnNewLog) then begin
  205. FLog.OnNewLog( P^.Logtype,P^.Time,P^.ThreadID,P^.Sender,P^.Logtext );
  206. end;
  207. Dispose(P);
  208. end;
  209. finally
  210. // Protection for possible raise
  211. l.Clear;
  212. end;
  213. finally
  214. FLog.FLogDataList.UnlockList;
  215. end;
  216. end;
  217. initialization
  218. _logs := Nil;
  219. finalization
  220. FreeAndNil(_logs);
  221. end.