Quick.Log.pas 8.1 KB

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