1. .NET日志框架核心原理剖析
日志系统是任何应用程序不可或缺的组成部分,特别是在分布式系统和微服务架构中。.NET提供的日志框架通过统一的抽象接口,允许开发者以一致的方式记录日志,同时保持与具体实现的解耦。
1.1 ILogger接口体系
ILogger接口是.NET日志系统的核心抽象,定义在Microsoft.Extensions.Logging.Abstractions包中。其核心方法包括:
csharp复制void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception exception, Func<TState, Exception, string> formatter)
关键设计特点:
- 日志级别:Trace(0)到Critical(5)的六级体系
- 结构化日志:支持参数化消息模板(如
User {UserId} logged in) - 事件ID:通过EventId关联特定业务事件
- 异常记录:原生支持异常对象记录
1.2 日志提供程序(Provider)机制
日志框架采用提供程序模式,内置多种实现:
- ConsoleProvider:控制台输出
- DebugProvider:调试窗口输出
- EventSourceProvider:ETW事件跟踪
- AzureAppServices:Azure环境集成
自定义提供程序需实现ILoggerProvider接口,典型实现模式:
csharp复制public class CustomLoggerProvider : ILoggerProvider
{
public ILogger CreateLogger(string categoryName) => new CustomLogger();
public void Dispose() { /* 资源清理 */ }
}
public class CustomLogger : ILogger
{
public IDisposable BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(/* 参数 */) { /* 实现 */ }
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 手写简易日志框架实战
2.1 基础架构设计
我们实现一个支持控制台和文件输出的简易框架:
csharp复制// 核心接口
public interface ILogWriter
{
void Write(LogEntry entry);
}
// 日志实体
public record LogEntry(
DateTimeOffset Timestamp,
LogLevel Level,
string Category,
string Message,
Exception Exception = null);
2.2 控制台输出实现
csharp复制public class ConsoleWriter : ILogWriter
{
private static readonly Dictionary<LogLevel, ConsoleColor> _colors = new()
{
[LogLevel.Trace] = ConsoleColor.Gray,
[LogLevel.Debug] = ConsoleColor.Blue,
[LogLevel.Information] = ConsoleColor.Green,
[LogLevel.Warning] = ConsoleColor.Yellow,
[LogLevel.Error] = ConsoleColor.Red,
[LogLevel.Critical] = ConsoleColor.Magenta
};
public void Write(LogEntry entry)
{
var color = _colors.GetValueOrDefault(entry.Level, ConsoleColor.White);
Console.ForegroundColor = color;
Console.WriteLine($"[{entry.Timestamp:HH:mm:ss} {entry.Level}] {entry.Message}");
Console.ResetColor();
}
}
2.3 文件输出实现
csharp复制public class FileWriter : ILogWriter, IDisposable
{
private readonly StreamWriter _writer;
private readonly object _lock = new();
public FileWriter(string path)
{
var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
_writer = new StreamWriter(path, append: true) { AutoFlush = true };
}
public void Write(LogEntry entry)
{
lock (_lock)
{
_writer.WriteLine($"{entry.Timestamp:O}|{entry.Level}|{entry.Category}|{entry.Message}");
if (entry.Exception != null)
_writer.WriteLine($"EXCEPTION: {entry.Exception}");
}
}
public void Dispose() => _writer.Dispose();
}
3. 日志框架高级特性实现
3.1 日志作用域(Scope)
实现上下文关联日志:
csharp复制public class LogScope : IDisposable
{
private readonly string _scopeId = Guid.NewGuid().ToString()[..8];
private static readonly AsyncLocal<Stack<LogScope>> _current = new();
public LogScope(string message)
{
var stack = _current.Value ??= new Stack<LogScope>();
stack.Push(this);
}
public static string CurrentScope =>
_current.Value?.Peek()?._scopeId;
public void Dispose() => _current.Value?.Pop();
}
3.2 异步日志处理
使用生产者-消费者模式避免阻塞:
csharp复制public class AsyncLogProcessor : ILogWriter, IDisposable
{
private readonly BlockingCollection<LogEntry> _queue = new(1000);
private readonly Task _processTask;
private readonly ILogWriter _innerWriter;
public AsyncLogProcessor(ILogWriter innerWriter)
{
_innerWriter = innerWriter;
_processTask = Task.Run(ProcessQueue);
}
private void ProcessQueue()
{
foreach (var entry in _queue.GetConsumingEnumerable())
{
try { _innerWriter.Write(entry); }
catch { /* 防止日志循环 */ }
}
}
public void Write(LogEntry entry) => _queue.Add(entry);
public void Dispose()
{
_queue.CompleteAdding();
_processTask.Wait(1000);
_queue.Dispose();
}
}
4. 性能优化与最佳实践
4.1 日志性能关键指标
| 指标 | 标准值 | 优化目标 |
|---|---|---|
| 同步写入延迟 | <1ms | 0.1ms |
| 异步队列容量 | 1000条 | 5000条 |
| 内存占用 | <10MB | <5MB |
| 文件写入吞吐 | 1MB/s | 10MB/s |
4.2 高频问题解决方案
问题1:日志阻塞主线程
- 解决方案:使用AsyncLogProcessor包装所有写入器
- 验证方法:在压力测试中观察线程池使用情况
问题2:日志文件过大
- 解决方案:实现滚动日志策略
csharp复制public class RollingFileWriter : ILogWriter
{
private const int MaxFileSize = 10 * 1024 * 1024; // 10MB
private int _fileIndex;
private FileWriter _currentWriter;
public void Write(LogEntry entry)
{
if (_currentWriter == null ||
new FileInfo(_currentWriter.FilePath).Length > MaxFileSize)
{
_currentWriter?.Dispose();
_currentWriter = new FileWriter($"log_{++_fileIndex}.txt");
}
_currentWriter.Write(entry);
}
}
问题3:敏感信息泄露
- 解决方案:实现日志过滤器
csharp复制public class SensitiveDataFilter : ILogWriter
{
private readonly ILogWriter _inner;
private readonly Regex _patterns = new(@"\b(?:password|[token](https://taotoken.net?utm_source=ai))=[^&\s]+");
public void Write(LogEntry entry)
{
var sanitized = _patterns.Replace(entry.Message, "[REDACTED]");
_inner.Write(entry with { Message = sanitized });
}
}
5. 与官方框架集成
5.1 作为Provider接入
csharp复制public static class CustomLoggerExtensions
{
public static ILoggingBuilder AddCustomLogger(
this ILoggingBuilder builder,
Action<CustomLoggerOptions> configure = null)
{
builder.Services.AddSingleton<ILoggerProvider, CustomLoggerProvider>();
if (configure != null) builder.Services.Configure(configure);
return builder;
}
}
// 使用方式
var builder = WebApplication.CreateBuilder();
builder.Logging.AddCustomLogger(options => {
options.MinLevel = LogLevel.Debug;
options.OutputFile = "app.log";
});
5.2 配置系统集成
支持appsettings.json配置:
json复制{
"Logging": {
"CustomLogger": {
"MinLevel": "Information",
"OutputFile": "logs/app-{Date}.log",
"MaxFileSizeMB": 10
}
}
}
实现配置绑定:
csharp复制public class CustomLoggerOptions
{
public LogLevel MinLevel { get; set; } = LogLevel.Information;
public string OutputFile { get; set; }
public int MaxFileSizeMB { get; set; } = 10;
}
在日志系统构建过程中,我深刻体会到设计良好的日志框架需要平衡多个维度:性能与可靠性、灵活性与易用性、功能完备与资源消耗。特别是在高并发场景下,如何确保日志不成为系统瓶颈是架构设计的重点。通过实现自己的日志框架,可以更深入理解.NET官方日志系统的设计哲学,在实际项目中做出更合理的选型和定制。
