Quick.Log.pas 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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. {$ELSE}
  33. Quick.Files,
  34. {$IFDEF LINUX}
  35. syncObjs,
  36. {$ENDIF}
  37. {$ENDIF}
  38. SysUtils;
  39. type
  40. TMemoryLog = class
  41. private
  42. fLines : TStrings;
  43. fEnabled : Boolean;
  44. function GetLogText : string;
  45. public
  46. constructor Create;
  47. destructor Destroy; override;
  48. property Lines : TStrings read fLines write fLines;
  49. property Text : string read GetLogText;
  50. property Enabled : Boolean read fEnabled write fEnabled;
  51. end;
  52. TQuickLog = class
  53. private
  54. fLogFileName : string;
  55. fCurrentDateToFileName : Boolean;
  56. fHideHour : Boolean;
  57. fFMTName : string;
  58. fVerbose : TLogVerbose;
  59. fLimitLogSize : Int64;
  60. fCurrentLogSize : Int64;
  61. fShowEventTypes : Boolean;
  62. fShowHeaderInfo : Boolean;
  63. fCheckFileInUse : Boolean;
  64. fMemoryLog : TMemoryLog;
  65. function GetLogFileName : string;
  66. procedure WriteLog(cMsg : string);
  67. public
  68. property Verbose : TLogVerbose read fVerbose write fVerbose;
  69. property LogFileName : string read GetLogFileName;
  70. property HideHour : Boolean read fHideHour write fHideHour;
  71. property ShowEventType : Boolean read fShowEventTypes write fShowEventTypes;
  72. property ShowHeaderInfo : Boolean read fShowHeaderInfo write fShowHeaderInfo;
  73. property CheckFileInUse : Boolean read fCheckFileInUse write fCheckFileInUse;
  74. property MemoryLog : TMemoryLog read fMemoryLog write fMemoryLog;
  75. constructor Create;
  76. destructor Destroy; override;
  77. function SetLog(logname : string; AddCurrentDateToFileName : Boolean; LimitSizeInMB : Integer = 0) : Boolean;
  78. procedure Add(const cMsg : string; cEventType : TLogEventType = TLogEventType.etInfo); overload;
  79. procedure Add(const cFormat : string; cParams : array of TVarRec ; cEventType : TLogEventType = TLogEventType.etInfo); overload;
  80. end;
  81. var
  82. CS : TRTLCriticalSection;
  83. Log : TQuickLog;
  84. implementation
  85. constructor TMemoryLog.Create;
  86. begin
  87. inherited;
  88. fLines := TStringList.Create;
  89. fEnabled := False;
  90. end;
  91. destructor TMemoryLog.Destroy;
  92. begin
  93. if Assigned(fLines) then fLines.Free;
  94. inherited;
  95. end;
  96. function TMemoryLog.GetLogText : string;
  97. begin
  98. Result := fLines.Text;
  99. end;
  100. constructor TQuickLog.Create;
  101. begin
  102. inherited;
  103. fVerbose := LOG_ALL;
  104. fLimitLogSize := 0;
  105. fShowEventTypes := True;
  106. fShowHeaderInfo := True;
  107. fCheckFileInUse := False;
  108. fMemoryLog := TMemoryLog.Create;
  109. end;
  110. destructor TQuickLog.Destroy;
  111. begin
  112. if Assigned(fMemoryLog) then fMemoryLog.Free;
  113. inherited;
  114. end;
  115. {Returns date + time in english format (to add to filename}
  116. function TQuickLog.GetLogFileName : string;
  117. begin
  118. Result := '';
  119. if fCurrentDateToFileName then
  120. begin
  121. try
  122. //Checks if needs to show time or not
  123. if HideHour then Result := FormatDateTime('yyyymmdd', Now())
  124. else Result := FormatDateTime('yyyymmdd_hhmm', Now());
  125. Result := Format(fFMTName,[Result]);
  126. except
  127. Result := 'Error';
  128. end;
  129. end
  130. else Result := fLogFileName;
  131. end;
  132. {$IFDEF FPC}
  133. function OSVersion: String;
  134. begin
  135. Result:={$I %FPCTARGETOS%}+'-'+{$I %FPCTARGETCPU%};
  136. end;
  137. {$ENDIF}
  138. function TQuickLog.SetLog(logname : string; AddCurrentDateToFileName : Boolean; LimitSizeInMB : Integer = 0) : Boolean;
  139. begin
  140. if logname = '' then logname := TPath.GetDirectoryName(ParamStr(0)) + '\' + TPath.GetFileNameWithoutExtension(ParamStr(0)) + '.log';
  141. fFMTName := ExtractFilePath(logname) + ExtractFileNameWithoutExt(logname) + '_%s' + ExtractFileExt(logname);
  142. fHideHour := True;
  143. fCurrentDateToFileName := AddCurrentDateToFileName;
  144. fLogFileName := logname;
  145. //Checks if logfile is too big and deletes
  146. fLimitLogSize := LimitSizeInMB * 1024 * 1024;
  147. if (fLimitLogSize > 0) and (TFile.Exists(logname)) then
  148. begin
  149. try
  150. fCurrentLogSize := TFile.GetSize(logname);
  151. if fCurrentLogSize > fLimitLogSize then if DeleteFile(logname) then fCurrentLogSize := 0;
  152. except
  153. raise Exception.Create('can''t access to log file!');
  154. end;
  155. end;
  156. //Checks if it's in use
  157. if (fCheckFileInUse) and (TFile.IsInUse(logname)) Then fLogFileName := Format('%s_(1).%s',[ExtractFileNameWithoutExt(logname),ExtractFileExt(logname)]);
  158. //writes header info
  159. if fShowHeaderInfo then
  160. begin
  161. try
  162. Self.WriteLog(FillStr('-',70));
  163. Self.WriteLog(Format('Application : %s %s',[ExtractFilenameWithoutExt(ParamStr(0)),GetAppVersionFullStr]));
  164. Self.WriteLog(Format('Path : %s',[ExtractFilePath(ParamStr(0))]));
  165. Self.WriteLog(Format('CPU cores : %d',[CPUCount]));
  166. Self.WriteLog(Format('OS version : %s',{$IFDEF FPC}[OSVersion]{$ELSE}[TOSVersion.ToString]{$ENDIF}));
  167. Self.WriteLog(Format('Host : %s',[GetComputerName]));
  168. Self.WriteLog(Format('Username : %s',[Trim(GetLoggedUserName)]));
  169. Self.WriteLog(Format('Started : %s',[NowStr]));
  170. {$IFDEF MSWINDOWS}
  171. if IsService then Self.WriteLog('AppType : Service')
  172. else if System.IsConsole then Self.WriteLog('AppType : Console');
  173. {$ENDIF}
  174. if IsDebug then Self.WriteLog('Debug mode : On');
  175. Self.WriteLog(FillStr('-',70));
  176. except
  177. on E : Exception do Self.WriteLog('Can''t get info: ' + e.message);
  178. end;
  179. end;
  180. Result := True;
  181. end;
  182. procedure TQuickLog.Add(const cMsg : string; cEventType : TLogEventType = TLogEventType.etInfo);
  183. begin
  184. //Check Log Verbose level
  185. if cEventType in fVerbose then
  186. begin
  187. if fShowEventTypes then Self.WriteLog(Format('%s [%s] %s',[DateTimeToStr(Now()),EventStr[Integer(cEventType)],cMsg]))
  188. else Self.WriteLog(Format('%s %s',[DateTimeToStr(Now()),cMsg]));
  189. end;
  190. end;
  191. procedure TQuickLog.WriteLog(cMsg : string);
  192. var
  193. stream: TFileStream;
  194. logname : string;
  195. LBuffer : TBytes;
  196. LByteOrderMark: TBytes;
  197. LOffset : Integer;
  198. LEncoding, DestEncoding: TEncoding;
  199. begin
  200. //Checks if need to add date to filename
  201. logname := GetLogFileName;
  202. EnterCriticalSection(CS);
  203. try
  204. cMsg := cMsg + #13#10;
  205. LEncoding:= TEncoding.Unicode;
  206. DestEncoding := TEncoding.Unicode;
  207. SetLength(LBuffer, length(cMsg) * sizeof(char));
  208. if cMsg <> '' then Move(cMsg[1], lbuffer[0], Length(lbuffer));
  209. LOffset := TEncoding.GetBufferEncoding(LBuffer, LEncoding);
  210. LBuffer := LEncoding.Convert(LEncoding, DestEncoding, LBuffer,
  211. LOffset, Length(LBuffer) - LOffset);
  212. if TFile.Exists(logname) then
  213. begin
  214. stream := TFileStream.Create(logname, fmOpenReadWrite or fmShareDenyWrite);
  215. stream.Position := stream.Size;
  216. end
  217. else
  218. begin
  219. stream := TFileStream.Create(logname, fmCreate or fmShareDenyWrite);
  220. LByteOrderMark := DestEncoding.GetPreamble;
  221. stream.Write(LByteOrderMark[0], Length(LByteOrderMark));
  222. end;
  223. with stream do
  224. begin
  225. try
  226. Write(LBuffer[0], Length(LBuffer));
  227. fCurrentLogSize := fCurrentLogSize + Length(LBuffer);
  228. finally
  229. Free;
  230. end;
  231. end;
  232. //writes internal log
  233. if fMemoryLog.Enabled then
  234. begin
  235. fMemoryLog.Lines.Add(cMsg);
  236. end;
  237. //check if limits max size
  238. try
  239. if (fLimitLogSize > 0) and (fCurrentLogSize > fLimitLogSize) then if DeleteFile(logname) then fCurrentLogSize := 0;
  240. except
  241. //
  242. end;
  243. finally
  244. LeaveCriticalSection(CS);
  245. end;
  246. end;
  247. procedure TQuickLog.Add(const cFormat : string; cParams : array of TVarRec ; cEventType : TLogEventType = TLogEventType.etInfo);
  248. begin
  249. Self.Add(Format(cFormat,cParams),cEventType);
  250. end;
  251. initialization
  252. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  253. InitCriticalSection(CS);
  254. {$ELSE}
  255. InitializeCriticalSection(CS);
  256. {$ENDIF}
  257. finalization
  258. {$IF DEFINED(FPC) AND DEFINED(LINUX)}
  259. DoneCriticalsection(CS);
  260. {$ELSE}
  261. DeleteCriticalSection(CS);
  262. {$ENDIF}
  263. end.