using MailKit;
namespace Dpz.Core.Service;
public sealed class SerilogProtocolLogger(ILogger<SerilogProtocolLogger> logger) : IProtocolLogger
{
private readonly EventId _eventId = new(10001, "邮件日志");
private static readonly byte[] Secret = "********"u8.ToArray();
private static readonly byte[] DefaultClientPrefix = "Client: "u8.ToArray();
private static readonly byte[] DefaultServerPrefix = "Server: "u8.ToArray();
private bool _clientMidline;
private bool _serverMidline;
private readonly MemoryStream _stream = new();
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool _disposed;
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_stream.Flush();
_stream.Position = 0;
using var reader = new StreamReader(_stream);
var message = reader.ReadToEnd();
logger.LogInformation(_eventId, message);
_stream.Dispose();
}
_disposed = true;
}
}
~SerilogProtocolLogger() => Dispose(false);
public void LogConnect(Uri uri)
{
var bytes = Encoding.ASCII.GetBytes($"{DateTime.Now} Connected to {uri}\r\n");
if (_clientMidline || _serverMidline)
{
_clientMidline = false;
_serverMidline = false;
}
_stream.Write(bytes, 0, bytes.Length);
_stream.Flush();
}
public void LogClient(byte[]? buffer, int offset, int count)
{
LogBuffer(buffer, offset, count, DefaultClientPrefix, ref _clientMidline, true);
}
public void LogServer(byte[]? buffer, int offset, int count)
{
LogBuffer(buffer, offset, count, DefaultServerPrefix, ref _serverMidline, false);
}
private void LogBuffer(
byte[]? buffer,
int offset,
int count,
byte[] prefix,
ref bool midline,
bool isClient
)
{
if (buffer == null)
{
logger.LogError(_eventId, "buffer is null");
return;
}
if (offset < 0 || offset > buffer.Length)
{
logger.LogError(
_eventId,
"offset less than 0 or offset greater than {Offset}",
buffer.Length
);
return;
}
if (count < 0 || count > buffer.Length - offset)
{
logger.LogError(
_eventId,
"count less than 0 or count greater than {Offset}",
buffer.Length - offset
);
return;
}
Log(prefix, ref midline, buffer, offset, count, isClient);
}
private void Log(
byte[] prefix,
ref bool midline,
byte[] buffer,
int offset,
int count,
bool isClient
)
{
var number = offset + count;
var index = offset;
while (index < number)
{
var offset1 = index;
while (index < number && buffer[index] != 10)
{
++index;
}
if (!midline)
{
_stream.Write(prefix, 0, prefix.Length);
}
if (index < number && buffer[index] == 10)
{
midline = false;
++index;
}
else
{
midline = true;
}
if (isClient)
{
var authenticationSecrets = AuthenticationSecretDetector?.DetectSecrets(
buffer,
offset1,
index - offset1
);
if (authenticationSecrets != null)
{
foreach (var detectSecret in authenticationSecrets)
{
if (detectSecret.StartIndex > offset1)
{
_stream.Write(buffer, offset1, detectSecret.StartIndex - offset1);
}
offset1 = detectSecret.StartIndex + detectSecret.Length;
_stream.Write(Secret, 0, Secret.Length);
}
}
}
_stream.Write(buffer, offset1, index - offset1);
}
}
public IAuthenticationSecretDetector? AuthenticationSecretDetector { get; set; }
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个实现了 MailKit 的 IProtocolLogger 接口的邮件协议日志记录器类,主要用于记录邮件客户端和服务器之间的通信过程。以下是详细解释:
类的基本结构
public sealed class SerilogProtocolLogger(ILogger<SerilogProtocolLogger> logger) : IProtocolLogger
- 使用 C# 12 的主构造函数语法
- 实现了
IProtocolLogger接口 - 通过依赖注入接收一个
ILogger实例
核心字段和属性
private readonly EventId _eventId = new(10001, "邮件日志");
private static readonly byte[] Secret = "********"u8.ToArray();
private static readonly byte[] DefaultClientPrefix = "Client: "u8.ToArray();
private static readonly byte[] DefaultServerPrefix = "Server: "u8.ToArray();
_eventId: 用于标识日志事件的ID和名称Secret: 用于替换敏感信息的占位符- 前缀字节数组:用于区分客户端和服务器的消息
主要功能方法
1. 连接日志记录
public void LogConnect(Uri uri)
记录与邮件服务器的连接信息,包括时间戳和URI。
2. 客户端/服务器消息记录
public void LogClient(byte[]? buffer, int offset, int count)
public void LogServer(byte[]? buffer, int offset, int count)
分别记录客户端发送和服务器接收的数据。
3. 核心日志处理逻辑
private void Log(byte[] prefix, ref bool midline, byte[] buffer, int offset, int count, bool isClient)
这个方法处理实际的日志写入:
- 按行分割数据(以换行符
\n为分隔符) - 为每行添加适当的前缀("Client: " 或 "Server: ")
- 处理跨行的数据(通过
midline标志)
安全特性
public IAuthenticationSecretDetector? AuthenticationSecretDetector { get; set; }
- 支持检测和隐藏认证敏感信息
- 当检测到敏感数据时,用
Secret("********")替换实际内容 - 只对客户端数据进行敏感信息检测
资源管理
实现了完整的 Dispose 模式:
public void Dispose()
private void Dispose(bool disposing)
~SerilogProtocolLogger()
- 在释放时将缓冲的日志内容写入到 Serilog
- 使用
MemoryStream作为临时缓冲区 - 确保资源正确释放
使用场景
这个类主要用于:
- 邮件协议调试: 记录 SMTP、POP3、IMAP 等协议的通信过程
- 问题排查: 帮助开发者分析邮件发送/接收过程中的问题
- 安全审计: 记录邮件通信日志,同时保护敏感认证信息
设计亮点
- 安全性: 自动检测和隐藏认证敏感信息
- 可读性: 为客户端和服务器消息添加清晰的前缀
- 性能: 使用
MemoryStream缓冲,减少频繁的日志写入 - 集成性: 与 Serilog 日志框架完美集成
这是一个设计良好的邮件协议日志记录器,既保证了调试的便利性,又考虑了安全性和性能。
AI 正在分析代码…
评论加载中...