Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 93 additions & 1 deletion LoggerPro.Builder.pas
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface
IAppenderConfigurator = interface
['{A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D}']
function Done: ILoggerProBuilder;
function WithMasking: IMaskingAppenderConfigurator;
end;

{ Console appender configurator }
Expand Down Expand Up @@ -211,6 +212,12 @@ interface
function WithFilter(aFilter: TLogItemFilterFunc): IFilteredAppenderConfigurator;
end;

{ Masking appender configurator - wraps another appender with masking functionality }
IMaskingAppenderConfigurator = interface(IAppenderConfigurator)
['{E9F0A1B2-C3D4-2F3A-6C7D-8E9F0A1B2C3D}']
function WithLogLevel(aLogLevel: TLogType): IMaskingAppenderConfigurator;
end;

{ Main builder interface }
ILoggerProBuilder = interface
['{1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D}']
Expand Down Expand Up @@ -270,7 +277,8 @@ implementation
LoggerPro.MemoryAppender,
LoggerPro.OutputDebugStringAppender,
LoggerPro.UDPSyslogAppender,
LoggerPro.DBAppender.FireDAC
LoggerPro.DBAppender.FireDAC,
LoggerPro.MaskingAppender;
{$IF Defined(MSWINDOWS)}
, LoggerPro.VCLMemoAppender
, LoggerPro.VCLListBoxAppender
Expand All @@ -295,6 +303,7 @@ TBaseAppenderConfigurator = class(TInterfacedObject)
FRenderer: ILogItemRenderer;
procedure ApplyLogLevel(aAppender: ILogAppender);
function GetRenderer: ILogItemRenderer;
function CreateAppender: ILogAppender; virtual; abstract;
public
constructor Create(aBuilder: TLoggerProBuilder);
end;
Expand All @@ -305,13 +314,15 @@ TConsoleAppenderConfigurator = class(TBaseAppenderConfigurator, IConsoleAppend
function WithLogLevel(aLogLevel: TLogType): IConsoleAppenderConfigurator;
function WithRenderer(aRenderer: ILogItemRenderer): IConsoleAppenderConfigurator;
function Done: ILoggerProBuilder;
function CreateAppender: ILogAppender; override;
end;

{ Simple console appender configurator }
TSimpleConsoleAppenderConfigurator = class(TBaseAppenderConfigurator, ISimpleConsoleAppenderConfigurator)
public
function WithLogLevel(aLogLevel: TLogType): ISimpleConsoleAppenderConfigurator;
function Done: ILoggerProBuilder;
function CreateAppender: ILogAppender; override;
end;

{ File appender configurator }
Expand All @@ -332,6 +343,7 @@ TFileAppenderConfigurator = class(TBaseAppenderConfigurator, IFileAppenderConf
function WithEncoding(aEncoding: TEncoding): IFileAppenderConfigurator;
function WithRenderer(aRenderer: ILogItemRenderer): IFileAppenderConfigurator;
function Done: ILoggerProBuilder;
function CreateAppender: ILogAppender; override;
end;

{ JSONL file appender configurator }
Expand Down Expand Up @@ -451,6 +463,7 @@ TOutputDebugStringAppenderConfigurator = class(TBaseAppenderConfigurator, IOut
function WithLogLevel(aLogLevel: TLogType): IOutputDebugStringAppenderConfigurator;
function WithRenderer(aRenderer: ILogItemRenderer): IOutputDebugStringAppenderConfigurator;
function Done: ILoggerProBuilder;
function CreateAppender: ILogAppender; override;
end;

{ UDP Syslog appender configurator }
Expand Down Expand Up @@ -558,6 +571,16 @@ TFilteredAppenderConfigurator = class(TBaseAppenderConfigurator, IFilteredAppe
function Done: ILoggerProBuilder;
end;

{ Masking appender configurator }
TMaskingAppenderConfigurator = class(TBaseAppenderConfigurator, IMaskingAppenderConfigurator)
private
FInnerAppender: ILogAppender;
public
constructor Create(aBuilder: TLoggerProBuilder; aAppender: ILogAppender);
function WithLogLevel(aLogLevel: TLogType): IMaskingAppenderConfigurator;
function Done: ILoggerProBuilder;
end;

{ Builder implementation - hidden from interface }
TLoggerProBuilder = class(TInterfacedObject, ILoggerProBuilder)
private
Expand Down Expand Up @@ -637,6 +660,14 @@ function TBaseAppenderConfigurator.GetRenderer: ILogItemRenderer;
Result := FBuilder.GetDefaultRenderer;
end;

function TBaseAppenderConfigurator.WithMasking: IMaskingAppenderConfigurator;
var
LAppender: ILogAppender;
begin
LAppender := CreateAppender;
Result := TMaskingAppenderConfigurator.Create(FBuilder, LAppender);
end;

{ TLoggerProBuilder }

constructor TLoggerProBuilder.Create;
Expand Down Expand Up @@ -865,6 +896,12 @@ function TConsoleAppenderConfigurator.Done: ILoggerProBuilder;
Result := FBuilder;
end;

function TConsoleAppenderConfigurator.CreateAppender: ILogAppender;
begin
Result := TLoggerProConsoleAppender.Create(GetRenderer);
ApplyLogLevel(Result);
end;

{ TSimpleConsoleAppenderConfigurator }

function TSimpleConsoleAppenderConfigurator.WithLogLevel(aLogLevel: TLogType): ISimpleConsoleAppenderConfigurator;
Expand All @@ -884,6 +921,12 @@ function TSimpleConsoleAppenderConfigurator.Done: ILoggerProBuilder;
Result := FBuilder;
end;

function TSimpleConsoleAppenderConfigurator.CreateAppender: ILogAppender;
begin
Result := TLoggerProSimpleConsoleAppender.Create;
ApplyLogLevel(Result);
end;

{ TFileAppenderConfigurator }

constructor TFileAppenderConfigurator.Create(aBuilder: TLoggerProBuilder);
Expand Down Expand Up @@ -960,6 +1003,24 @@ function TFileAppenderConfigurator.Done: ILoggerProBuilder;
Result := FBuilder;
end;

function TFileAppenderConfigurator.CreateAppender: ILogAppender;
var
lFileNameFormat: string;
begin
if FFileBaseName.IsEmpty then
lFileNameFormat := TLoggerProFileAppenderBase.DEFAULT_FILENAME_FORMAT
else
lFileNameFormat := FFileBaseName + '.{number}.{tag}.log';
Result := TLoggerProFileAppender.Create(
FMaxBackupFiles,
FMaxFileSizeInKB,
FLogsFolder,
lFileNameFormat,
GetRenderer,
FEncoding);
ApplyLogLevel(Result);
end;

{ TJSONLFileAppenderConfigurator }

constructor TJSONLFileAppenderConfigurator.Create(aBuilder: TLoggerProBuilder);
Expand Down Expand Up @@ -1354,6 +1415,12 @@ function TOutputDebugStringAppenderConfigurator.Done: ILoggerProBuilder;
Result := FBuilder;
end;

function TOutputDebugStringAppenderConfigurator.CreateAppender: ILogAppender;
begin
Result := TLoggerProOutputDebugStringAppender.Create(GetRenderer);
ApplyLogLevel(Result);
end;

{ TUDPSyslogAppenderConfigurator }

constructor TUDPSyslogAppenderConfigurator.Create(aBuilder: TLoggerProBuilder);
Expand Down Expand Up @@ -1659,6 +1726,31 @@ function TFilteredAppenderConfigurator.Done: ILoggerProBuilder;
Result := FBuilder;
end;

{ TMaskingAppenderConfigurator }

constructor TMaskingAppenderConfigurator.Create(aBuilder: TLoggerProBuilder; aAppender: ILogAppender);
begin
inherited Create(aBuilder);
FInnerAppender := aAppender;
end;

function TMaskingAppenderConfigurator.WithLogLevel(aLogLevel: TLogType): IMaskingAppenderConfigurator;
begin
FLogLevel := aLogLevel;
FLogLevelSet := True;
Result := Self;
end;

function TMaskingAppenderConfigurator.Done: ILoggerProBuilder;
var
lMaskingAppender: ILogAppender;
begin
lMaskingAppender := TLoggerProMaskingAppender.Create(FInnerAppender);
ApplyLogLevel(lMaskingAppender);
FBuilder.InternalAddAppender(lMaskingAppender);
Result := FBuilder;
end;

{ Helper function }

function LoggerProBuilder: ILoggerProBuilder;
Expand Down
183 changes: 183 additions & 0 deletions LoggerPro.MaskingAppender.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// *************************************************************************** }
//
// LoggerPro
//
// Copyright (c) 2010-2026 Daniele Teti
//
// https://github.com/danieleteti/loggerpro
//
// ***************************************************************************

unit LoggerPro.MaskingAppender;

interface

uses
System.Classes,
System.SysUtils,
System.RegularExpressions,
LoggerPro;

type
/// <summary>
/// 脱敏日志装饰器,用于对日志正文进行脱敏处理
/// 隐藏 11 位中国手机号中间 4 位(如 138****5678)
/// 隐藏 password=xxx 后面的明文值
/// </summary>
TLoggerProMaskingAppender = class(TLoggerProAppenderBase)
private
FInnerAppender: ILogAppender;
FPhoneRegex: TRegEx;
FPasswordRegex: TRegEx;
protected
function MaskPhoneNumber(const AMessage: string): string;
function MaskPassword(const AMessage: string): string;
function MaskMessage(const AMessage: string): string;
public
/// <summary>
/// 创建脱敏装饰器实例
/// </summary>
/// <param name="AInnerAppender">被装饰的内部日志追加器</param>
constructor Create(AInnerAppender: ILogAppender); reintroduce; virtual;

procedure Setup; override;
procedure TearDown; override;
procedure WriteLog(const aLogItem: TLogItem); override;
procedure SetEnabled(const Value: Boolean); override;
function IsEnabled: Boolean; override;
procedure SetLogLevel(const Value: TLogType); override;
function GetLogLevel: TLogType; override;
procedure TryToRestart(var Restarted: Boolean); override;
procedure SetLastErrorTimeStamp(const LastErrorTimeStamp: TDateTime); override;
function GetLastErrorTimeStamp: TDateTime; override;
end;

implementation

{ TLoggerProMaskingAppender }

constructor TLoggerProMaskingAppender.Create(AInnerAppender: ILogAppender);
begin
inherited Create;
FInnerAppender := AInnerAppender;

// 预编译正则表达式,避免在高并发日志下产生性能瓶颈
// 中国手机号正则:11位数字,以1开头,第二位是3-9
FPhoneRegex := TRegEx.Create('(\b1[3-9]\d{1})(\d{4})(\d{4}\b)', [roCompiled]);
// 密码参数正则:password=后面的值
FPasswordRegex := TRegEx.Create('(password=)([^\s&]*)', [roCompiled, roIgnoreCase]);
end;

function TLoggerProMaskingAppender.MaskPhoneNumber(const AMessage: string): string;
begin
// 使用预编译的正则表达式替换手机号中间4位为****
Result := FPhoneRegex.Replace(AMessage, '$1****$3');
end;

function TLoggerProMaskingAppender.MaskPassword(const AMessage: string): string;
begin
// 使用预编译的正则表达式替换password=后面的值为****
Result := FPasswordRegex.Replace(AMessage, '$1****');
end;

function TLoggerProMaskingAppender.MaskMessage(const AMessage: string): string;
begin
Result := AMessage;

// 先处理密码脱敏,再处理手机号脱敏
Result := MaskPassword(Result);
Result := MaskPhoneNumber(Result);
end;

procedure TLoggerProMaskingAppender.Setup;
begin
if Assigned(FInnerAppender) then
FInnerAppender.Setup;
end;

procedure TLoggerProMaskingAppender.TearDown;
begin
if Assigned(FInnerAppender) then
FInnerAppender.TearDown;
end;

procedure TLoggerProMaskingAppender.WriteLog(const aLogItem: TLogItem);
var
LMaskedLogItem: TLogItem;
LMaskedMessage: string;
begin
if not Assigned(FInnerAppender) then
Exit;

// 对日志消息进行脱敏处理
LMaskedMessage := MaskMessage(aLogItem.LogMessage);

// 创建脱敏后的日志项
LMaskedLogItem := TLogItem.Create(
aLogItem.LogType,
LMaskedMessage,
aLogItem.LogTag,
aLogItem.TimeStamp,
aLogItem.ThreadID,
aLogItem.Context
);

try
// 将脱敏后的日志项传递给内部追加器
FInnerAppender.WriteLog(LMaskedLogItem);
finally
LMaskedLogItem.Free;
end;
end;

procedure TLoggerProMaskingAppender.SetEnabled(const Value: Boolean);
begin
if Assigned(FInnerAppender) then
FInnerAppender.SetEnabled(Value);
end;

function TLoggerProMaskingAppender.IsEnabled: Boolean;
begin
if Assigned(FInnerAppender) then
Result := FInnerAppender.IsEnabled
else
Result := False;
end;

procedure TLoggerProMaskingAppender.SetLogLevel(const Value: TLogType);
begin
if Assigned(FInnerAppender) then
FInnerAppender.SetLogLevel(Value);
end;

function TLoggerProMaskingAppender.GetLogLevel: TLogType;
begin
if Assigned(FInnerAppender) then
Result := FInnerAppender.GetLogLevel
else
Result := TLogType.Debug;
end;

procedure TLoggerProMaskingAppender.TryToRestart(var Restarted: Boolean);
begin
if Assigned(FInnerAppender) then
FInnerAppender.TryToRestart(Restarted)
else
Restarted := False;
end;

procedure TLoggerProMaskingAppender.SetLastErrorTimeStamp(const LastErrorTimeStamp: TDateTime);
begin
if Assigned(FInnerAppender) then
FInnerAppender.SetLastErrorTimeStamp(LastErrorTimeStamp);
end;

function TLoggerProMaskingAppender.GetLastErrorTimeStamp: TDateTime;
begin
if Assigned(FInnerAppender) then
Result := FInnerAppender.GetLastErrorTimeStamp
else
Result := 0;
end;

end.
Loading