Quick.Log.pas 8.4 KB

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