Quick.Log.pas 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. { ***************************************************************************
  2. Copyright (c) 2016-2018 Kike Pérez
  3. Unit : Quick.Log
  4. Description : Threadsafe Log
  5. Author : Kike Pérez
  6. Version : 1.18
  7. Created : 10/04/2016
  8. Modified : 08/04/2018
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Log;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. {$IFDEF MSWINDOWS}
  26. Windows,
  27. {$ENDIF}
  28. Classes,
  29. Quick.Commons,
  30. {$IFNDEF FPC}
  31. System.IOUtils,
  32. {$IFDEF DELPHILINUX}
  33. Quick.SyncObjs.Linux.Compatibility,
  34. {$ENDIF}
  35. {$ELSE}
  36. Quick.Files,
  37. {$ENDIF}
  38. {$IFDEF LINUX}
  39. syncObjs,
  40. {$ENDIF}
  41. SysUtils;
  42. type
  43. TMemoryLog = class
  44. private
  45. fLines : TStrings;
  46. fEnabled : Boolean;
  47. function GetLogText : string;
  48. public
  49. constructor Create;
  50. destructor Destroy; override;
  51. property Lines : TStrings read fLines write fLines;
  52. property Text : string read GetLogText;
  53. property Enabled : Boolean read fEnabled write fEnabled;
  54. end;
  55. TQuickLog = class
  56. private
  57. fLogFileName : string;
  58. fCurrentDateToFileName : Boolean;
  59. fHideHour : Boolean;
  60. fFMTName : string;
  61. fVerbose : TLogVerbose;
  62. fLimitLogSize : Int64;
  63. fCurrentLogSize : Int64;
  64. fShowEventTypes : Boolean;
  65. fShowHeaderInfo : Boolean;
  66. fCheckFileInUse : Boolean;
  67. fMemoryLog : TMemoryLog;
  68. function GetLogFileName : string;
  69. procedure WriteLog(cMsg : string);
  70. public
  71. property Verbose : TLogVerbose read fVerbose write fVerbose;
  72. property LogFileName : string read GetLogFileName;
  73. property HideHour : Boolean read fHideHour write fHideHour;
  74. property ShowEventType : Boolean read fShowEventTypes write fShowEventTypes;
  75. property ShowHeaderInfo : Boolean read fShowHeaderInfo write fShowHeaderInfo;
  76. property CheckFileInUse : Boolean read fCheckFileInUse write fCheckFileInUse;
  77. property MemoryLog : TMemoryLog read fMemoryLog write fMemoryLog;
  78. constructor Create;
  79. destructor Destroy; override;
  80. function SetLog(logname : string; AddCurrentDateToFileName : Boolean; LimitSizeInMB : Integer = 0) : Boolean;
  81. procedure Add(const cMsg : string; cEventType : TLogEventType = TLogEventType.etInfo); overload;
  82. procedure Add(const cFormat : string; cParams : array of TVarRec ; cEventType : TLogEventType = TLogEventType.etInfo); overload;
  83. end;
  84. var
  85. CS : TRTLCriticalSection;
  86. Log : TQuickLog;
  87. implementation
  88. constructor TMemoryLog.Create;
  89. begin
  90. inherited;
  91. fLines := TStringList.Create;
  92. fEnabled := False;
  93. end;
  94. destructor TMemoryLog.Destroy;
  95. begin
  96. if Assigned(fLines) then fLines.Free;
  97. inherited;
  98. end;
  99. function TMemoryLog.GetLogText : string;
  100. begin
  101. Result := fLines.Text;
  102. end;
  103. constructor TQuickLog.Create;
  104. begin
  105. inherited;
  106. fVerbose := LOG_ALL;
  107. fLimitLogSize := 0;
  108. fShowEventTypes := True;
  109. fShowHeaderInfo := True;
  110. fCheckFileInUse := False;
  111. fMemoryLog := TMemoryLog.Create;
  112. end;
  113. destructor TQuickLog.Destroy;
  114. begin
  115. if Assigned(fMemoryLog) then fMemoryLog.Free;
  116. inherited;
  117. end;
  118. {Returns date + time in english format (to add to filename}
  119. function TQuickLog.GetLogFileName : string;
  120. begin
  121. Result := '';
  122. if fCurrentDateToFileName then
  123. begin
  124. try
  125. //Checks if needs to show time or not
  126. if HideHour then Result := FormatDateTime('yyyymmdd', Now())
  127. else Result := FormatDateTime('yyyymmdd_hhmm', Now());
  128. Result := Format(fFMTName,[Result]);
  129. except
  130. Result := 'Error';
  131. end;
  132. end
  133. else Result := fLogFileName;
  134. end;
  135. {$IFDEF FPC}
  136. function OSVersion: String;
  137. begin
  138. Result:={$I %FPCTARGETOS%}+'-'+{$I %FPCTARGETCPU%};
  139. end;
  140. {$ENDIF}
  141. function TQuickLog.SetLog(logname : string; AddCurrentDateToFileName : Boolean; LimitSizeInMB : Integer = 0) : Boolean;
  142. begin
  143. if logname = '' then logname := TPath.GetDirectoryName(ParamStr(0)) + '\' + TPath.GetFileNameWithoutExtension(ParamStr(0)) + '.log';
  144. fFMTName := ExtractFilePath(logname) + ExtractFileNameWithoutExt(logname) + '_%s' + ExtractFileExt(logname);
  145. fHideHour := True;
  146. fCurrentDateToFileName := AddCurrentDateToFileName;
  147. fLogFileName := logname;
  148. //Checks if logfile is too big and deletes
  149. fLimitLogSize := LimitSizeInMB * 1024 * 1024;
  150. if (fLimitLogSize > 0) and (TFile.Exists(logname)) then
  151. begin
  152. try
  153. fCurrentLogSize := TFile.GetSize(logname);
  154. if fCurrentLogSize > fLimitLogSize then if DeleteFile(logname) then fCurrentLogSize := 0;
  155. except
  156. raise Exception.Create('can''t access to log file!');
  157. end;
  158. end;
  159. //Checks if it's in use
  160. if (fCheckFileInUse) and (TFile.IsInUse(logname)) Then fLogFileName := Format('%s_(1).%s',[ExtractFileNameWithoutExt(logname),ExtractFileExt(logname)]);
  161. //writes header info
  162. if fShowHeaderInfo then
  163. begin
  164. try
  165. Self.WriteLog(FillStr('-',70));
  166. Self.WriteLog(Format('Application : %s %s',[ExtractFilenameWithoutExt(ParamStr(0)),GetAppVersionFullStr]));
  167. Self.WriteLog(Format('Path : %s',[ExtractFilePath(ParamStr(0))]));
  168. Self.WriteLog(Format('CPU cores : %d',[CPUCount]));
  169. Self.WriteLog(Format('OS version : %s',{$IFDEF FPC}[OSVersion]{$ELSE}[TOSVersion.ToString]{$ENDIF}));
  170. Self.WriteLog(Format('Host : %s',[GetComputerName]));
  171. Self.WriteLog(Format('Username : %s',[Trim(GetLoggedUserName)]));
  172. Self.WriteLog(Format('Started : %s',[NowStr]));
  173. {$IFDEF MSWINDOWS}
  174. if IsService then Self.WriteLog('AppType : Service')
  175. else if System.IsConsole then Self.WriteLog('AppType : Console');
  176. {$ENDIF}
  177. if IsDebug then Self.WriteLog('Debug mode : On');
  178. Self.WriteLog(FillStr('-',70));
  179. except
  180. on E : Exception do Self.WriteLog('Can''t get info: ' + e.message);
  181. end;
  182. end;
  183. Result := True;
  184. end;
  185. procedure TQuickLog.Add(const cMsg : string; cEventType : TLogEventType = TLogEventType.etInfo);
  186. begin
  187. //Check Log Verbose level
  188. if cEventType in fVerbose then
  189. begin
  190. if fShowEventTypes then Self.WriteLog(Format('%s [%s] %s',[DateTimeToStr(Now()),EventStr[Integer(cEventType)],cMsg]))
  191. else Self.WriteLog(Format('%s %s',[DateTimeToStr(Now()),cMsg]));
  192. end;
  193. end;
  194. procedure TQuickLog.WriteLog(cMsg : string);
  195. var
  196. stream: TFileStream;
  197. logname : string;
  198. LBuffer : TBytes;
  199. LByteOrderMark: TBytes;
  200. LOffset : Integer;
  201. LEncoding, DestEncoding: TEncoding;
  202. begin
  203. //Checks if need to add date to filename
  204. logname := GetLogFileName;
  205. EnterCriticalSection(CS);
  206. try
  207. cMsg := cMsg + #13#10;
  208. LEncoding:= TEncoding.Unicode;
  209. DestEncoding := TEncoding.Unicode;
  210. SetLength(LBuffer, length(cMsg) * sizeof(char));
  211. if cMsg <> '' then Move(cMsg[1], lbuffer[0], Length(lbuffer));
  212. LOffset := TEncoding.GetBufferEncoding(LBuffer, LEncoding);
  213. LBuffer := LEncoding.Convert(LEncoding, DestEncoding, LBuffer,
  214. LOffset, Length(LBuffer) - LOffset);
  215. if TFile.Exists(logname) then
  216. begin
  217. stream := TFileStream.Create(logname, fmOpenReadWrite or fmShareDenyWrite);
  218. stream.Position := stream.Size;
  219. end
  220. else
  221. begin
  222. stream := TFileStream.Create(logname, fmCreate or fmShareDenyWrite);
  223. LByteOrderMark := DestEncoding.GetPreamble;
  224. stream.Write(LByteOrderMark[0], Length(LByteOrderMark));
  225. end;
  226. with stream do
  227. begin
  228. try
  229. Write(LBuffer[0], Length(LBuffer));
  230. fCurrentLogSize := fCurrentLogSize + Length(LBuffer);
  231. finally
  232. Free;
  233. end;
  234. end;
  235. //writes internal log
  236. if fMemoryLog.Enabled then
  237. begin
  238. fMemoryLog.Lines.Add(cMsg);
  239. end;
  240. //check if limits max size
  241. try
  242. if (fLimitLogSize > 0) and (fCurrentLogSize > fLimitLogSize) then if DeleteFile(logname) then fCurrentLogSize := 0;
  243. except
  244. //
  245. end;
  246. finally
  247. LeaveCriticalSection(CS);
  248. end;
  249. end;
  250. procedure TQuickLog.Add(const cFormat : string; cParams : array of TVarRec ; cEventType : TLogEventType = TLogEventType.etInfo);
  251. begin
  252. Self.Add(Format(cFormat,cParams),cEventType);
  253. end;
  254. initialization
  255. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  256. InitCriticalSection(CS);
  257. {$ELSE}
  258. InitializeCriticalSection(CS);
  259. {$ENDIF}
  260. finalization
  261. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  262. DoneCriticalsection(CS);
  263. {$ELSE}
  264. DeleteCriticalSection(CS);
  265. {$ENDIF}
  266. end.