Quick.Log.pas 8.0 KB

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