Logger.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. {
  2. # Delphi 日志类(Logger Class for delphi xe)
  3. - v1.0.3
  4. - 2024-12-02 by gale
  5. - https://github.com/higale/LoggerXE
  6. ## 方法:
  7. - Debug:调试,通常在开发中会将其设置为最低的日志级别,用于输出详细的调试信息。
  8. - Info:信息,用于输出常用的信息,使用较为频繁。
  9. - Warn:警告,表明会出现潜在错误的情形,虽然程序不会报错,但仍需注意。
  10. - Error:错误,记录错误和异常信息。
  11. - Fatal:致命错误,一旦发生,程序基本上需要停止。
  12. ## 日志文件位置
  13. 可以通过设置Root来改变日志文件位置,如果不设置,日志缺省存储在
  14. - Windows:
  15. *程序所在目录/log/*
  16. - MacOS
  17. */Users/当前用户/.程序名/log/*
  18. ## 注意
  19. g_Logger实例已经自动创建,可以直接使用
  20. ## 使用方法:
  21. uses
  22. Logger;
  23. ...
  24. g_Logger.Debug('This is a %s log',['debug'])
  25. g_Logger.Error('发生了一些错误!');
  26. VCL显示日志
  27. g_Logger.OnLog := procedure(Sender: TObject; ALevel: TLogLevel; ALevelTag: string; ALog: string; ATime: TDateTime)
  28. begin
  29. if mmoLog.Lines.Count > 1000 then
  30. mmoLog.Text := 'clear...';
  31. mmoLog.Lines.Add(Format('%s%s%s', [FormatDateTime('hh:mm:ss', ATime), ALevelTag, ALog]));
  32. end;
  33. FMX显示日志
  34. g_Logger.OnLog := procedure(Sender: TObject; ALevel: TLogLevel; ALevelTag: string; ALog: string; ATime: TDateTime)
  35. begin
  36. if mmoLog.Lines.Count > 1000 then
  37. mmoLog.Text := 'clear...';
  38. mmoLog.Lines.Add(Format('%s%s%s', [FormatDateTime('hh:mm:ss', ATime), ALevelTag, ALog]));
  39. mmoLog.GoToTextEnd;
  40. end;
  41. }
  42. unit Logger;
  43. // 兼容旧版日志类,可以使用WriteLog函数输出日志
  44. //{$DEFINE Compatible_Old_Version}
  45. interface
  46. uses
  47. System.IOUtils, System.Classes, System.SysUtils, System.SyncObjs;
  48. type
  49. /// <summary>日志级别</summary>
  50. TLogLevel = (llAll, llDebug, llInfo, llWarn, llError, llFatal, llOff);
  51. /// <summary>日志回调事件</summary>
  52. TOnLogerLog = reference to procedure(Sender: TObject; ALevel: TLogLevel; ALevelTag: string; ALog: string; ATime: TDateTime);
  53. /// <summary>日志类</summary>
  54. TLogger = class
  55. private
  56. FCSLock: TCriticalSection;
  57. FRoot: string;
  58. FSubFormat: string;
  59. FFilenameFormat: string;
  60. FEncoding: TEncoding;
  61. FLevel: TLogLevel;
  62. FTimeFormat: string;
  63. FTags: array [TLogLevel] of string;
  64. FOnLog: TOnLogerLog;
  65. procedure SetRoot(AValue: string);
  66. procedure Log(ALog: string; const ALogLevel: TLogLevel); overload;
  67. procedure Log(ALog: string; const Args: array of const; const LogLevel: TLogLevel); overload;
  68. public
  69. /// <summary>日志目录,缺省为log,可以是相对或绝对路径</summary>
  70. property Root: string read FRoot write SetRoot;
  71. /// <summary>子目录格式,缺省为yyyymm(每月一个子目录),为空不使用子目录</summary>
  72. property SubFormat: string read FSubFormat write FSubFormat;
  73. /// <summary>日志文件名格式,缺省为yyyymmdd(每天一个文件)</summary>
  74. property FilenameFormat: string read FFilenameFormat write FFilenameFormat;
  75. /// <summary>日志编码格式,缺省UTF8</summary>
  76. property Encoding: TEncoding read FEncoding write FEncoding;
  77. /// <summary>低于此级别的日志将被忽略,缺省为llAll, llOff为全部忽略</summary>
  78. property Level: TLogLevel read FLevel write FLevel;
  79. /// <summary>保存日志时间格式,缺省为 'hhnnss'</summary>
  80. property TimeFormat: string read FTimeFormat write FTimeFormat;
  81. /// <summary>日志触发事件,线程安全</summary>
  82. property OnLog: TOnLogerLog read FOnLog write FOnLog;
  83. /// <summary>不同日志级别对应的名称标签,缺省为 '[D]', '[I]', '[W]', '[E]', '[F]'</summary>
  84. /// <param name="ADebugTag">调试日志标签</param>
  85. /// <param name="AInfoTag">信息日志标签</param>
  86. /// <param name="AWarnTag">警告日志标签</param>
  87. /// <param name="AErrorTag">错误日志标签</param>
  88. /// <param name="AFatalTag">致命错误日志标签</param>
  89. procedure SetTags(ADebugTag, AInfoTag, AWarnTag, AErrorTag, AFatalTag: string);
  90. public
  91. /// <summary>构造函数,如无特殊需求,可以直接使用g_Logger,它已经自动初始化,不需要手动创建</summary>
  92. constructor Create;
  93. /// <summary>析构函数</summary>
  94. destructor Destroy; override;
  95. /// <summary>输出调试日志</summary>
  96. /// <param name="ALog">日志内容</param>
  97. procedure Debug(ALog: string); overload;
  98. /// <summary>输出调试日志</summary>
  99. /// <param name="ALog">包含格式化格式信息的日志数据</param>
  100. /// <param name="Args">用于格式化的参数</param>
  101. procedure Debug(ALog: string; const Args: array of const); overload;
  102. /// <summary>输出信息日志</summary>
  103. /// <param name="ALog">日志内容</param>
  104. procedure Info(ALog: string); overload;
  105. /// <summary>输出信息日志</summary>
  106. /// <param name="ALog">包含格式化格式信息的日志数据</param>
  107. /// <param name="Args">用于格式化的参数</param>
  108. procedure Info(ALog: string; const Args: array of const); overload;
  109. /// <summary>输出警告日志</summary>
  110. /// <param name="ALog">日志内容</param>
  111. procedure Warn(ALog: string); overload;
  112. /// <summary>输出警告日志</summary>
  113. /// <param name="ALog">包含格式化格式信息的日志数据</param>
  114. /// <param name="Args">用于格式化的参数</param>
  115. procedure Warn(ALog: string; const Args: array of const); overload;
  116. /// <summary>输出错误日志</summary>
  117. /// <param name="ALog">日志内容</param>
  118. procedure Error(ALog: string); overload;
  119. /// <summary>输出错误日志</summary>
  120. /// <param name="ALog">包含格式化格式信息的日志数据</param>
  121. /// <param name="Args">用于格式化的参数</param>
  122. procedure Error(ALog: string; const Args: array of const); overload;
  123. /// <summary>输出致命错误日志</summary>
  124. /// <param name="ALog">日志内容</param>
  125. procedure Fatal(ALog: string); overload;
  126. /// <summary>输出致命错误日志</summary>
  127. /// <param name="ALog">包含格式化格式信息的日志数据</param>
  128. /// <param name="Args">用于格式化的参数</param>
  129. procedure Fatal(ALog: string; const Args: array of const); overload;
  130. {$IFDEF Compatible_Old_Version}
  131. /// <summary>输出日志 【警告】不建议使用此函数! 此函数仅为兼容旧版本程序</summary>
  132. /// <param name="ALog">日志内容</param>
  133. /// <param name="ALogLevel">日志级别 0:信息 1,2:警告 other:错误</param>
  134. procedure WriteLog(ALog: String; const ALogLevel: integer = 0); overload;
  135. deprecated '函数 WriteLog 已不建议使用,请直接使用语义更清晰的 Debug、Info、Warn、Error或Fatal 函数输出日志';
  136. /// <summary>输出日志 【警告】不建议使用此函数! 此函数仅为兼容旧版本程序</summary>
  137. /// <param name="ALog">包含格式化格式信息的日志数据</param>
  138. /// <param name="Args">用于格式化的参数</param>
  139. /// <param name="ALogLevel">日志级别 0:信息 1,2:警告 other:错误</param>
  140. procedure WriteLog(ALog: String; const Args: array of const; const ALogLevel: integer = 0); overload;
  141. deprecated '函数 WriteLog 已不建议使用,请直接使用语义更清晰的 Debug、Info、Warn、Error或Fatal 函数输出日志';
  142. {$ENDIF}
  143. end;
  144. var
  145. g_Logger: TLogger;
  146. implementation
  147. { ------------------------------------------------------------------------------
  148. 名称: TLogger.Create
  149. 说明: 构造函数
  150. ------------------------------------------------------------------------------ } constructor TLogger.Create;
  151. begin
  152. inherited Create;
  153. FCSLock := TCriticalSection.Create;
  154. {$IFDEF MACOS}
  155. FRoot := ExtractFilePath(TPath.GetDocumentsPath) + '.' + ExtractFileName(ParamStr(0)) + PathDelim + 'log' + PathDelim;
  156. {$ELSE}
  157. FRoot := ExtractFilePath(ParamStr(0)) + 'log' + PathDelim;
  158. {$ENDIF}
  159. FSubFormat := 'yyyymm';
  160. FFilenameFormat := 'yyyymmdd';
  161. FEncoding := TEncoding.UTF8;
  162. FLevel := llAll;
  163. FTimeFormat := 'hhnnss';
  164. SetTags('[D]', '[I]', '[W]', '[E]', '[F]');
  165. end;
  166. { ------------------------------------------------------------------------------
  167. 名称: TLogger.Destroy
  168. 说明: 析构函数
  169. ------------------------------------------------------------------------------ }
  170. destructor TLogger.Destroy;
  171. begin
  172. FCSLock.Free;
  173. inherited;
  174. end;
  175. { ------------------------------------------------------------------------------
  176. 名称: TLogger.SetRoot
  177. 说明: LogDir属性设置函数
  178. 参数: AValue
  179. ------------------------------------------------------------------------------ }
  180. procedure TLogger.SetRoot(AValue: string);
  181. begin
  182. FRoot := ExpandFileName(AValue);
  183. if FRoot[FRoot.Length] <> PathDelim then
  184. begin
  185. FRoot := FRoot + PathDelim;
  186. end;
  187. if not ForceDirectories(FRoot) then
  188. begin
  189. raise Exception.Create('The log file directory cannot be created: ' + FRoot);
  190. end;
  191. end;
  192. { ------------------------------------------------------------------------------
  193. 名称: TLogger.SetLevelTags
  194. 说明: LevelTags属性设置函数
  195. 参数: ADebugTag 调试
  196. AInfoTag 信息
  197. AWarnTag 警告
  198. AErrorTag 错误
  199. ------------------------------------------------------------------------------ }
  200. procedure TLogger.SetTags(ADebugTag, AInfoTag, AWarnTag, AErrorTag, AFatalTag: string);
  201. begin
  202. FTags[llDebug] := ADebugTag;
  203. FTags[llInfo] := AInfoTag;
  204. FTags[llWarn] := AWarnTag;
  205. FTags[llError] := AErrorTag;
  206. FTags[llFatal] := AFatalTag;
  207. end;
  208. { ------------------------------------------------------------------------------
  209. 名称: TLogger.Log
  210. 说明: Log输出函数
  211. 参数: ALog 包含格式化格式信息的日志数据
  212. Args 用于格式化的参数
  213. LogLevel 日志级别
  214. ------------------------------------------------------------------------------ }
  215. procedure TLogger.Log(ALog: string; const Args: array of const; const LogLevel: TLogLevel);
  216. begin
  217. Log(Format(ALog, Args), LogLevel);
  218. end;
  219. { ------------------------------------------------------------------------------
  220. 名称: TLogger.Log
  221. 说明: Log输出函数
  222. 参数: ALog 日志数据
  223. ALogLevel 日志级别
  224. ------------------------------------------------------------------------------ }
  225. procedure TLogger.Log(ALog: string; const ALogLevel: TLogLevel);
  226. var
  227. FullDir, SubDir: string;
  228. logFileName: string;
  229. strLogAll: string;
  230. LogLevelTag: string;
  231. LogTime: TDateTime;
  232. begin
  233. if ALogLevel >= FLevel then
  234. begin
  235. LogTime := Now;
  236. LogLevelTag := FTags[ALogLevel];
  237. logFileName := FormatDateTime(FFilenameFormat, LogTime) + '.log';
  238. FullDir := FRoot;
  239. if FSubFormat <> '' then
  240. begin
  241. SubDir := FormatDateTime(FSubFormat, LogTime);
  242. FullDir := FRoot + SubDir + PathDelim;
  243. end;
  244. strLogAll := Format('%s%s%s' + sLineBreak, [FormatDateTime(FTimeFormat, LogTime), LogLevelTag, ALog]);
  245. FCSLock.Enter;
  246. try
  247. if not DirectoryExists(FullDir) then
  248. begin
  249. ForceDirectories(FullDir);
  250. end;
  251. try
  252. TFile.AppendAllText(FullDir + logFileName, strLogAll, FEncoding);
  253. except
  254. end;
  255. finally
  256. FCSLock.Leave;
  257. end;
  258. if Assigned(FOnLog) then
  259. begin
  260. TThread.Synchronize(TThread.CurrentThread,
  261. procedure
  262. begin
  263. FOnLog(self, ALogLevel, FTags[ALogLevel], ALog, LogTime);
  264. end);
  265. end;
  266. end;
  267. end;
  268. { ------------------------------------------------------------------------------
  269. 名称: TLogger.Debug
  270. 说明: 调试日志
  271. 参数: ALog 日志数据
  272. ------------------------------------------------------------------------------ }
  273. procedure TLogger.Debug(ALog: string);
  274. begin
  275. Log(ALog, llDebug);
  276. end;
  277. { ------------------------------------------------------------------------------
  278. 名称: TLogger.Debug
  279. 说明: 调试日志
  280. 参数: ALog 包含格式化格式信息的日志数据
  281. Args 用于格式化的参数
  282. ------------------------------------------------------------------------------ }
  283. procedure TLogger.Debug(ALog: string; const Args: array of const);
  284. begin
  285. Log(ALog, Args, llDebug);
  286. end;
  287. { ------------------------------------------------------------------------------
  288. 名称: TLogger.Info
  289. 说明: 信息日志
  290. 参数: ALog 日志数据
  291. ------------------------------------------------------------------------------ }
  292. procedure TLogger.Info(ALog: string);
  293. begin
  294. Log(ALog, llInfo);
  295. end;
  296. { ------------------------------------------------------------------------------
  297. 名称: TLogger.Info
  298. 说明: 信息日志
  299. 参数: ALog 包含格式化格式信息的日志数据
  300. Args 用于格式化的参数
  301. ------------------------------------------------------------------------------ }
  302. procedure TLogger.Info(ALog: string; const Args: array of const);
  303. begin
  304. Log(ALog, Args, llInfo);
  305. end;
  306. { ------------------------------------------------------------------------------
  307. 名称: TLogger.Warn
  308. 说明: 警告日志
  309. 参数: ALog 日志数据
  310. ------------------------------------------------------------------------------ }
  311. procedure TLogger.Warn(ALog: string);
  312. begin
  313. Log(ALog, llWarn);
  314. end;
  315. { ------------------------------------------------------------------------------
  316. 名称: TLogger.Warn
  317. 说明: 警告日志
  318. 参数: ALog 包含格式化格式信息的日志数据
  319. Args 用于格式化的参数
  320. ------------------------------------------------------------------------------ }
  321. procedure TLogger.Warn(ALog: string; const Args: array of const);
  322. begin
  323. Log(ALog, Args, llWarn);
  324. end;
  325. { ------------------------------------------------------------------------------
  326. 名称: TLogger.Error
  327. 说明: 错误日志
  328. 参数: ALog 日志数据
  329. ------------------------------------------------------------------------------ }
  330. procedure TLogger.Error(ALog: string);
  331. begin
  332. Log(ALog, llError);
  333. end;
  334. { ------------------------------------------------------------------------------
  335. 名称: TLogger.Error
  336. 说明: 错误日志
  337. 参数: ALog 包含格式化格式信息的日志数据
  338. Args 用于格式化的参数
  339. ------------------------------------------------------------------------------ }
  340. procedure TLogger.Error(ALog: string; const Args: array of const);
  341. begin
  342. Log(ALog, Args, llError);
  343. end;
  344. { ------------------------------------------------------------------------------
  345. 名称: TLogger.Fatal
  346. 说明: 致命错误日志
  347. 参数: ALog 日志数据
  348. ------------------------------------------------------------------------------ }
  349. procedure TLogger.Fatal(ALog: string);
  350. begin
  351. Log(ALog, llFatal);
  352. end;
  353. { ------------------------------------------------------------------------------
  354. 名称: TLogger.Fatal
  355. 说明: 致命错误日志
  356. 参数: ALog 包含格式化格式信息的日志数据
  357. Args 用于格式化的参数
  358. ------------------------------------------------------------------------------ }
  359. procedure TLogger.Fatal(ALog: string; const Args: array of const);
  360. begin
  361. Log(ALog, Args, llFatal);
  362. end;
  363. {$IFDEF Compatible_Old_Version}
  364. { -------------------------------------------------------------------------------
  365. 名称: TLogger.WriteLog
  366. 说明: 旧版本日志输出函数,为兼容保留,不建议使用
  367. 参数: ALog 日志数据
  368. ALogLevel 日志级别
  369. ------------------------------------------------------------------------------- }
  370. procedure TLogger.WriteLog(ALog: String; const ALogLevel: integer);
  371. var
  372. ALvl: TLogLevel;
  373. begin
  374. case ALogLevel of
  375. 0:
  376. ALvl := llInfo;
  377. 1, 2:
  378. ALvl := llWarn;
  379. else
  380. ALvl := llError;
  381. end;
  382. Log(ALog, ALvl);
  383. end;
  384. { -------------------------------------------------------------------------------
  385. 名称: TLogger.WriteLog
  386. 说明: 旧版本日志输出函数,为兼容保留,不建议使用
  387. 参数: ALog 包含格式化格式信息的日志数据
  388. Args 用于格式化的参数
  389. ALogLevel 日志级别
  390. ------------------------------------------------------------------------------- }
  391. procedure TLogger.WriteLog(ALog: String; const Args: array of const; const ALogLevel: integer);
  392. var
  393. ALvl: TLogLevel;
  394. begin
  395. case ALogLevel of
  396. 0:
  397. ALvl := llInfo;
  398. 1, 2:
  399. ALvl := llWarn;
  400. else
  401. ALvl := llError;
  402. end;
  403. Log(ALog, Args, ALvl);
  404. end;
  405. {$ENDIF}
  406. initialization
  407. g_Logger := TLogger.Create;
  408. finalization
  409. FreeAndNil(g_Logger);
  410. end.