
1. .NET日志框架核心原理剖析日志系统是现代应用程序不可或缺的组成部分它如同飞机的黑匣子记录着程序运行时的关键信息。在.NET生态中日志框架的设计哲学主要体现在以下几个核心维度1.1 日志分级机制.NET日志系统采用分级设计这种设计类似于医院的急诊分诊制度public enum LogLevel { Trace 0, // 诊断级细节如方法入参值 Debug 1, // 调试阶段关注信息 Information 2, // 常规运行信息 Warning 3, // 异常但可恢复的情况 Error 4, // 当前操作失败 Critical 5, // 应用级灾难事件 None 6 // 完全禁用 }日志分级带来三个关键优势性能优化生产环境可只记录Warning及以上级别日志问题定位通过级别快速过滤关键事件存储控制不同级别日志可配置不同存储策略1.2 日志提供器(Provider)架构.NET日志系统采用Provider模式这种设计类似于多端同步的云笔记应用核心接口关系ILoggerFactory日志工厂管理Provider集合ILoggerProvider提供器基类如Console、File等ILogger实际执行日志记录典型配置示例Host.CreateDefaultBuilder(args) .ConfigureLogging(builder { builder.ClearProviders(); builder.AddConsole(); builder.AddFile(logs/app-{Date}.txt); });1.3 日志过滤规则过滤系统如同海关安检决定哪些日志可以通关{ Logging: { LogLevel: { Default: Information, Microsoft: Warning, System: Error }, Console: { LogLevel: { MyApp.Controllers: Debug } } } }过滤规则执行优先级特定Provider的特定命名空间规则特定Provider的默认规则全局特定命名空间规则全局默认规则1.4 结构化日志的优势传统文本日志如同手写笔记而结构化日志则是数据库记录// 传统日志 logger.LogInformation($User {userId} accessed {resource}); // 结构化日志 logger.LogInformation(User {UserId} accessed {Resource}, userId, resource);结构化日志的核心价值查询能力可按特定字段过滤如UserId123分析效率日志系统可直接解析为JSON上下文关联通过关联字段追踪请求链路经验之谈在微服务架构中建议所有日志消息至少包含TraceId、ServiceName等基础字段2. 手写日志框架实战理解原理后我们动手实现一个轻量级日志框架。这个框架将包含核心功能代码控制在200行以内。2.1 基础架构设计我们的迷你框架包含以下组件classDiagram class ILogger { interface Log(level, message, exception) } class Logger { -_providers ListILoggerProvider AddProvider() Log() } class ConsoleLogger { Log() } class FileLogger { Log() } ILogger |.. Logger ILogger |.. ConsoleLogger ILogger |.. FileLogger2.2 核心接口实现首先定义关键接口public enum LogLevel { Debug, Info, Warn, Error, Fatal } public interface ILogProvider { void WriteLog(LogLevel level, string message, Exception ex null); } public class Logger { private readonly ListILogProvider _providers new(); public void AddProvider(ILogProvider provider) _providers.Add(provider); public void Log(LogLevel level, string message, Exception ex null) { foreach (var provider in _providers) { try { provider.WriteLog(level, message, ex); } catch { /* 防止日志循环 */ } } } }2.3 控制台日志实现实现彩色控制台输出public class ConsoleLogger : ILogProvider { private static readonly DictionaryLogLevel, ConsoleColor _colors new() { [LogLevel.Debug] ConsoleColor.Gray, [LogLevel.Info] ConsoleColor.White, [LogLevel.Warn] ConsoleColor.Yellow, [LogLevel.Error] ConsoleColor.Red, [LogLevel.Fatal] ConsoleColor.DarkRed }; public void WriteLog(LogLevel level, string message, Exception ex) { var color _colors.GetValueOrDefault(level, ConsoleColor.White); var prefix $[{DateTime.Now:HH:mm:ss} {level.ToString().ToUpper()}]; Console.ForegroundColor color; Console.Write(prefix); Console.ResetColor(); Console.WriteLine($ {message}); if (ex ! null) { Console.ForegroundColor ConsoleColor.DarkRed; Console.WriteLine(ex.ToString()); Console.ResetColor(); } } }2.4 文件日志实现带日志滚动的文件记录public class FileLogger : ILogProvider { private readonly string _basePath; private readonly long _maxFileSize; private readonly object _lock new(); public FileLogger(string basePath logs, long maxFileSize 10 * 1024 * 1024) { _basePath basePath; _maxFileSize maxFileSize; Directory.CreateDirectory(basePath); } public void WriteLog(LogLevel level, string message, Exception ex) { lock (_lock) { var date DateTime.Today.ToString(yyyyMMdd); var filePath Path.Combine(_basePath, $app-{date}.log); // 检查文件大小 if (File.Exists(filePath) new FileInfo(filePath).Length _maxFileSize) { var ts DateTime.Now.ToString(HHmmss); filePath Path.Combine(_basePath, $app-{date}-{ts}.log); } var content $[{DateTime.Now:HH:mm:ss} {level}] {message}\n; if (ex ! null) { content ${ex}\n\n; } File.AppendAllText(filePath, content); } } }2.5 使用示例组合各种提供器var logger new Logger(); logger.AddProvider(new ConsoleLogger()); logger.AddProvider(new FileLogger()); logger.Log(LogLevel.Info, 系统启动完成); try { throw new InvalidOperationException(模拟异常); } catch (Exception ex) { logger.Log(LogLevel.Error, 处理请求时出错, ex); }3. 高级功能扩展基础框架完成后我们可以添加一些增强功能。3.1 日志过滤支持public class FilteredLogger : ILogProvider { private readonly ILogProvider _inner; private readonly FuncLogLevel, bool _filter; public FilteredLogger(ILogProvider inner, FuncLogLevel, bool filter) { _inner inner; _filter filter; } public void WriteLog(LogLevel level, string message, Exception ex) { if (_filter(level)) { _inner.WriteLog(level, message, ex); } } } // 使用示例 logger.AddProvider( new FilteredLogger( new ConsoleLogger(), level level LogLevel.Warn ) );3.2 异步日志处理避免阻塞主线程public class AsyncLogger : ILogProvider { private readonly ILogProvider _inner; private readonly BlockingCollectionLogItem _queue new(); private readonly Task _worker; public AsyncLogger(ILogProvider inner) { _inner inner; _worker Task.Run(ProcessQueue); } private void ProcessQueue() { foreach (var item in _queue.GetConsumingEnumerable()) { _inner.WriteLog(item.Level, item.Message, item.Exception); } } public void WriteLog(LogLevel level, string message, Exception ex) { _queue.Add(new LogItem(level, message, ex)); } private record LogItem(LogLevel Level, string Message, Exception Exception); }3.3 结构化日志支持public static class LoggerExtensions { public static void LogInfo(this Logger logger, string template, params object[] args) { var message FormatTemplate(template, args); logger.Log(LogLevel.Info, message); } private static string FormatTemplate(string template, object[] args) { // 简单实现实际项目可使用更复杂的模板引擎 for (int i 0; i args.Length; i) { template template.Replace(${{{i}}}, args[i]?.ToString()); } return template; } } // 使用示例 logger.LogInfo(用户 {0} 从 {1} 登录, userId, ipAddress);4. 性能优化技巧日志系统性能直接影响应用整体表现以下是关键优化点4.1 日志级别预检查public class OptimizedLogger { private readonly LogLevel _minLevel; public bool IsEnabled(LogLevel level) level _minLevel; public void Log(LogLevel level, string message, Exception ex null) { if (!IsEnabled(level)) return; // 实际记录逻辑... } }4.2 避免字符串拼接低效写法logger.Log(LogLevel.Debug, 开始处理订单 orderId);高效写法if (logger.IsEnabled(LogLevel.Debug)) { logger.Log(LogLevel.Debug, $开始处理订单 {orderId}); }4.3 缓冲写入策略文件日志的缓冲优化public class BufferedFileLogger : IDisposable { private readonly StreamWriter _writer; private readonly Timer _flushTimer; public BufferedFileLogger(string path) { _writer new StreamWriter(path, true) { AutoFlush false }; _flushTimer new Timer(_ _writer.Flush(), null, 1000, 1000); } public void WriteLog(string message) { _writer.WriteLine(message); } public void Dispose() { _flushTimer?.Dispose(); _writer?.Flush(); _writer?.Dispose(); } }5. 常见问题排查5.1 日志丢失问题可能原因及解决方案现象可能原因解决方案部分日志消失异步日志未正确关闭实现IDisposable并确保Flush文件日志不全进程崩溃未刷新缓冲区减小缓冲大小或定时Flush控制台日志缺失日志级别设置过高检查过滤规则5.2 性能问题排查日志性能问题诊断步骤使用Stopwatch测量日志调用耗时检查是否有同步IO操作分析日志消息构建成本特别是字符串操作检查日志框架锁竞争情况5.3 日志配置最佳实践推荐配置方案{ Logging: { MinimumLevel: { Default: Information, Microsoft: Warning }, WriteTo: [ { Name: Console, Args: { outputTemplate: {Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception} } }, { Name: File, Args: { path: logs/app-.log, rollingInterval: Day, retainedFileCountLimit: 7 } } ] } }在实现自己的日志框架时有几个关键点需要特别注意首先是线程安全性特别是在多Provider场景下其次是异常处理要避免日志系统自身异常影响主业务流程最后是资源释放确保文件句柄等资源及时释放。