一个线程安全的轻量级的日志类

做服务类应用,总要输出日志,便于调试及掌握应用服务器的运行状态,在界面上显示日志内容,就要访问VCL可视控件,因此,输入日志的代码,一定要线程安全,才不会在写日志时,让应用产生AV错误。下面是基于网友梧桐树下Logger1.0版做的修改,适用于DelphiXE6。附了完整代码。使用时请保留原始信息。

下面是具体的调用方法:

1.建立log对象,并指定显示的控件。
 
log := TLogger.Create;
  log.LogShower:=memo1;
2.输出日志
  log.WriteLog(logstr);

真是轻量的代码,很易用,感谢 梧桐树下

附完整代码。

unit Logger;
//=======================================================================
// 日志类(TLogger) ver.1.0
// 红鱼儿 http://blog.sina.com.cn/hblyuhong
// 2014/06/24
// 基于1.0修改
// PFeng  ( http://www.pfeng.org / xxmc01#gmail.com)
// 2012/11/08
// 日志级别约定:
// 0 - Information
// 1 - Notice
// 2 - Warning
// 3 - Error
//=======================================================================

interface

uses Windows, Classes, SysUtils, StdCtrls, ComCtrls, ComObj,Messages;

const
  WRITE_LOG_DIR = 'log\'; // 记录日志默认目录
  WRITE_LOG_MIN_LEVEL = 0; //记录日志的最低级别,小于此级别只显示不记录
  WRITE_LOG_ADD_TIME = True; // 记录日志是否添加时间
  WRITE_LOG_TIME_FORMAT = 'hh:nn:ss.zzz'; //记录日志添加时间的格式
  SHOW_LOG_ADD_TIME = True; // 日志显示容器是否添加时间
  SHOW_LOG_TIME_FORMAT = 'yyyy/mm/ddhh:nn:ss.zzz'; // 日志显示添加时间的格式
  SHOW_LOG_CLEAR_COUNT = 1000; //日志显示容器最大显示条数

type
  TLogger = class
  private
      FCSLock:TRTLCriticalSection; // 临界区
      FFileStream:TFileStream; // 文件流
      FLogShower:TComponent; // 日志显示容器
      FLogName:String; // 日志名称
      FEnabled:Boolean;
      FLogFileDir:string; // 日志目录
      procedureSetEnabled(const Value: Boolean);
      procedureSetLogFileDir(const Value: string);
      procedureSetLogShower(const Value: TComponent);
  protected
      procedureShowLog(Log: String; const LogLevel: Integer = 0);
  public
      procedureWriteLog(Log: String; const LogLevel: Integer = 0); overload;
      procedureWriteLog(Log: String; const Args: array of const; const LogLevel:Integer = 0); overload;

      constructorCreate;
      destructorDestroy; override;

      //是否允许记录日志
      propertyEnabled: Boolean read FEnabled write SetEnabled;
      //日志文件目录,默认当前目录的Log目录
      propertyLogFileDir: string read FLogFileDir write SetLogFileDir;
      //显示日志的组件
      propertyLogShower: TComponent read FLogShower write SetLogShower;

  end;

implementation

constructor TLogger.Create;
begin
  InitializeCriticalSectio n(FCSLock);
  FLogShower := nil;
  LogFileDir := ExtractFilePath(ParamStr(0)) +WRITE_LOG_DIR;
end;

procedure TLogger.WriteLog(Log: String; const Args: array of const;const LogLevel: Integer = 0);
begin
  WriteLog(Format(Log, Args), LogLevel);
end;

procedure TLogger.WriteLog(Log: String; const LogLevel: Integer =0);
var
  logName: String;
  fMode: Word;
  bytes: TBytes;
begin
  EnterCriticalSection(FCSLock);
  try
      if notEnabled then
          Exit;

      ShowLog(Log,LogLevel); // 显示日志到容器
      if LogLevel>= WRITE_LOG_MIN_LEVEL then
      begin
          logName := FormatDateTime('yyyymmdd', Now) + '.log';
          if FLogName <> logName then
          begin
              FLogName := logName;
              if FileExists(FLogFileDir + FLogName) then // 如果当天的日志文件存在
                  fMode := fmOpenWrite or fmShareDenyNone
              else
                  fMode := fmCreate or fmShareDenyNone;

              if Assigned(FFileStream) then
                  FreeAndNil(FFileStream);
              FFileStream := TFileStream.Create(FLogFileDir + FLogName,fMode);
          end;

          FFileStream.Position := FFileStream.Size; // 追加到最后
          case LogLevel of
              0:
                  Log := '[Information] ' + Log;
              1:
                  Log := '[Notice] ' + Log;
              2:
                  Log := '[Warning] ' + Log;
              3:
                  Log := '[Error] ' + Log;
          end;
          if WRITE_LOG_ADD_TIME then
              Log := FormatDateTime(WRITE_LOG_TIME_FORMAT, Now) + ' ' + Log +#13#10;

          bytes := TEnCoding.UTF8.GetBytes(Log);
          FFileStream.Write(bytes, Length(bytes));

      end;
  finally
      LeaveCriticalSection(FCSLock);
  end;
end;

procedure TLogger.SetEnabled(const Value: Boolean);
begin
  FEnabled := Value;
end;

procedure TLogger.SetLogFileDir(const Value: string);
begin
  FLogFileDir := Value;
  if not DirectoryExists(FLogFileDir) then
      if notForceDirectories(FLogFileDir) then
      begin
          raise Exception.Create('日志路径错误,日志类对象不能被创建');
      end;
end;

procedure TLogger.SetLogShower(const Value: TComponent);
begin
  FLogShower := Value;
end;

procedure TLogger.ShowLog(Log: String; const LogLevel: Integer =0);
var
  lineCount: Integer;
  listItem: TListItem;
begin
  if FLogShower = nil then
      Exit;
  if (FLogShower is TMemo) then
  begin
      ifSHOW_LOG_ADD_TIME then
          Log := FormatDateTime(SHOW_LOG_TIME_FORMAT, Now) + ' ' + Log;
      lineCount :=TMemo(FLogShower).Lines.Add(Log);
      //滚屏到最后一行
      SendMessage(TMemo(FLogShower).Handle, WM_VSCROLL, SB_LINEDOWN,0);
      if lineCount>= SHOW_LOG_CLEAR_COUNT then
          TMemo(FLogShower).Clear;
  end
  else if (FLogShower is TListBox) then
  begin
      ifSHOW_LOG_ADD_TIME then
          Log := FormatDateTime(SHOW_LOG_TIME_FORMAT, Now) + ' ' + Log;
      lineCount :=TListBox(FLogShower).Items.Add(Log);
      SendMessage(TListBox(FLogShower).Handle, WM_VSCROLL, SB_LINEDOWN,0);
      if lineCount>= SHOW_LOG_CLEAR_COUNT then
          TListBox(FLogShower).Clear;
  end
  else if (FLogShower is TListView) then
  begin
      listItem :=TListView(FLogShower).Items.Add;
      ifSHOW_LOG_ADD_TIME then
          listItem.Caption := FormatDateTime(SHOW_LOG_TIME_FORMAT,Now);
      ifAssigned(TListView(FLogShower).SmallImages) and(TListView(FLogShower).SmallImages.Count - 1 >= LogLevel)then
          listItem.ImageIndex := LogLevel; // 可以根据不同等级显示不同图片
      listItem.SubItems.Add(Log);
      SendMessage(TListView(FLogShower).Handle, WM_VSCROLL, SB_LINEDOWN,0);
      ifTListView(FLogShower).Items.Count >= SHOW_LOG_CLEAR_COUNTthen
          TListView(FLogShower).Items.Clear;
  end
  else
      raiseException.Create('日志容器类型不支持:' + FLogShower.ClassName);
end;

destructor TLogger.Destroy;
begin
  DeleteCriticalSection(FCSLock);
  if Assigned(FFileStream) then
      FreeAndNil(FFileStream);

end;

end.
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Delphi Logger { 超简单实用的DELPHI日志单元 1.0.0 优化版 引用本单元即可使用 一共四个方法 procedure log4error(msg: AnsiString); //写ERROR级别的日志 procedure log4info(msg: AnsiString); //写INFO级别的日志 procedure log4debug(msg: AnsiString); //写DEBUG级别的日志 function log4filename():AnsiString; //得到当前日志文件全名 一个配置文件 log4me.ini #配置文件和主程序在同一目录.没有这个文件或不在主目录中则不写日志 [log4me] #path,日志的存放目录.必须是主程序目录及子目录. #例子:主程序目录 #path=. #例子:子目录 #path=temp\logs path=logs #level,日志等级,只能是 error,info,debug之一 #为error时,只有log4error打印的日志被输出. #为info时,log4error和log4info打印的日志被输出. #为debug时,log4error,log4info,log4debug打印的日志都被输出. level=info 一个可选工具 tail.exe 命令行中输入 >tail.exe -1000f 日志文件名 即可动态查看日志输出 或用程序调用 var cmd :AnsiString; log_file:AnsiString; begin log_file := log4filename(); //得到当前日志文件全名 cmd := ExtractFilePath(ParamStr(0)) + 'tail.exe -1000f "'+ log_file +'"'; WinExec(PAnsiChar(cmd),SW_SHOWNORMAL); //程序调用 tail.exe工具来查看日志 } unit log4me; interface uses classes, sysutils, ComObj, windows,IniFiles; procedure log4error(msg: AnsiString); //写ERROR级别的日志 procedure log4info(msg: AnsiString); //写INFO级别的日志 procedure log4debug(msg: AnsiString); //写DEBUG级别的日志 function log4filename():AnsiString; //得到当前日志文件全名 var log_filename: AnsiString; //日志文件全名 implementation var log_ThreadLock: TRTLCriticalSection; // 临界区 log_fileStream: TFileStream; log_filepath: AnsiString; //日志文件路径 log_initime:TDateTime; log_doerror, log_dodebug, log_doinfo: Boolean; procedure log_init(); var tmpStr:String; begin log_doerror := False; log_dodebug := False; log_doinfo := False; if FileExists(ExtractFilePath(ParamStr(0)) + 'log4me.ini') then begin with TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'log4me.ini') do try log_filepath := ExtractFilePath(ParamStr(0)) + '\' + ReadString('log4me','path','logs') + '\'; tmpStr := LowerCase(ReadString('log4me','level','info')); log_doerror := (tmpStr = 'debug') or (tmpStr = 'info') or (tmpStr = 'error'); log_doinfo := (tmpStr = 'debug') or (tmpStr = 'info'); log_dodebug := (tmpStr = 'debug') ; finally Free; end; end; log_initime := Now; end; procedure log4me_addLog(filename: AnsiString; p: PAnsiChar); var fmode :Word; tmp:AnsiString; begin //进入临界区,多线程时可以保护资源 EnterCriticalSection(log_ThreadLock); try try //如果要写的日志文件和打开的不同(在程序第一次运行和跨天的时候出现) //则关闭打开的日志文件。 if filename log_filename then begin log_filename := filename; if Assigned(log_fileStream) then begin log_fileStream.Free; log_fileStream := nil; end; end; //如果要写的日志文件没有打开(在程序第一次运行和跨天的时候出现) //则打开日志文件。 if not Assigned(log_fileStream) then begin if FileExists(log_filename) then fmode := fmOpenWrite or fmShareDenyNone else fmode := fmCreate or fmShareDenyNone ; log_fileStream := TFileStream.Create(log_filename,fmode); log_fileStream.Position := log_fileStream.Size; end; //在日志文件中写入日志 log_fileStream.Write(p^, strlen(p)); except on E:Exception do begin try tmp := ExtractFilePath(ParamStr(0)) + 'log4me_err.log'; if FileExists(tmp) then fmode := fmOpenWrite or fmShareDenyNone else fmode := fmCreate or fmShareDenyNone ; with TFileStream.Create(tmp, fmode) do begin Position := Size; tmp := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', Now) + ' ' + E.Message + #13#10; Write(tmp[1],Length(tmp)); Free; end; except end; end; end; finally //无论如何,离开临界区 LeaveCriticalSection(log_ThreadLock); end; end; procedure log4write(msg: AnsiString); var strline: AnsiString; begin // 最多每秒重加载一次配置文件 if (Now() - log_initime) > (1/(24*60*60)) then log_init(); //日志开头加时间 strline := FormatDateTime('hh:nn:ss.zzz', Now) + ' ' + msg + #13#10; //写到当天的日志文件中 log4me_addLog(log_filepath + FormatDateTime('yyyy-mm-dd', Now) + '.log', PAnsiChar(strline)); end; //-----下面4个是对外方法------------------------- function log4filename():AnsiString; begin Result := log_filename; end; procedure log4error(msg: AnsiString); begin if log_doerror then log4write('[error]' + msg); end; procedure log4info(msg: AnsiString); begin if log_doinfo then log4write('[info ]' + msg); end; procedure log4debug(msg: AnsiString); begin if log_dodebug then log4write('[debug]' + msg); end; // ----------- 类初始化 -------------// initialization InitializeCriticalSection(log_ThreadLock); log_init; log4info('log4me:application starting....'); // ----------- 类销毁 -------------// finalization log4info('log4me:application stoping....'); DeleteCriticalSection(log_ThreadLock); if Assigned(log_fileStream) then log_fileStream.Free; end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值