3
原生实现.NET 5.0+ 自定义日志 - China-Mr-zhong
source link: https://www.cnblogs.com/China-Mr-zhong/p/16420357.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
原生实现.NET 5.0+ 自定义日志
一、定义一个静态类 声明一个 ReaderWriterLockSlim 对象 用于并发控制
1 /// <summary> 2 /// IO锁 3 /// </summary> 4 public static class Lock 5 { 6 7 /// <summary> 8 /// 文件读写锁 9 /// </summary> 10 public static readonly ReaderWriterLockSlim _fileLockSlim = null; 11 12 /// <summary> 13 /// 构造方法 14 /// </summary> 15 static Lock() 16 { 17 _fileLockSlim = new ReaderWriterLockSlim(); 18 } 19 }
二、实现ILoggerProvider 接口
1 /// <summary> 2 /// 文件记录器提供商 3 /// </summary> 4 public class FileLoggerProvider : ILoggerProvider 5 { 6 7 /// <summary> 8 /// 配置 9 /// </summary> 10 private readonly IConfiguration _configuration; 11 12 /// <summary> 13 /// 日志对象缓存 14 /// </summary> 15 private readonly ConcurrentDictionary<string, FileLogger> _loggers = new ConcurrentDictionary<string, FileLogger>(); 16 17 /// <summary> 18 /// 构造方法 19 /// </summary> 20 /// <param name="configuration">配置</param> 21 public FileLoggerProvider(IConfiguration configuration) 22 { 23 _configuration = configuration; 24 } 25 26 /// <summary> 27 /// 创建记录器 28 /// </summary> 29 /// <param name="categoryName">类别名称</param> 30 /// <returns></returns> 31 public ILogger CreateLogger(string categoryName) 32 { 33 return _loggers.GetOrAdd(categoryName, k => 34 { 35 return new FileLogger(_configuration, k); 36 }); 37 } 38 39 /// <summary> 40 /// 释放方法 41 /// </summary> 42 public void Dispose() 43 { 44 _loggers.Clear(); 45 GC.SuppressFinalize(this); 46 } 47 }
三、实现 ILogger 接口
1 /// <summary> 2 /// 文件记录器 3 /// </summary> 4 public class FileLogger : ILogger 5 { 6 7 /// <summary> 8 /// 配置 9 /// </summary> 10 private readonly IConfiguration _configuration; 11 12 /// <summary> 13 /// 类别名称 14 /// </summary> 15 private readonly string _categoryName; 16 17 /// <summary> 18 /// 构造方法 19 /// </summary> 20 /// <param name="configuration">配置</param> 21 /// <param name="categoryName">类别名称</param> 22 public FileLogger(IConfiguration configuration, string categoryName) 23 { 24 _configuration = configuration; 25 _categoryName = categoryName; 26 } 27 28 /// <summary> 29 /// 开始范围 30 /// </summary> 31 /// <typeparam name="TState">状态类型</typeparam> 32 /// <param name="state">状态</param> 33 /// <returns></returns> 34 public IDisposable BeginScope<TState>(TState state) 35 { 36 return null; 37 } 38 39 /// <summary> 40 /// 是否使用 41 /// </summary> 42 /// <param name="logLevel">日志级别</param> 43 /// <returns></returns> 44 public bool IsEnabled(LogLevel logLevel) 45 { 46 var list = new List<IConfigurationSection>(); 47 list.AddRange(_configuration.GetSection("Logging:LogLevel").GetChildren()); 48 list.AddRange(_configuration.GetSection("Logging:FileLog:LogLevel").GetChildren()); 49 50 var category = list.LastOrDefault(f => _categoryName.StartsWith(f.Key)); 51 52 if (category == null) 53 { 54 category = list.LastOrDefault(f => f.Key == "Default"); 55 } 56 57 if (category != null && Enum.TryParse(typeof(LogLevel), category.Value, out var level)) 58 { 59 return (int)(LogLevel)level <= (int)logLevel; 60 } 61 return 2 <= (int)logLevel; 62 } 63 64 /// <summary> 65 /// 日志 66 /// </summary> 67 /// <typeparam name="TState">状态类型</typeparam> 68 /// <param name="logLevel">日志级别</param> 69 /// <param name="eventId">事件ID</param> 70 /// <param name="state">状态</param> 71 /// <param name="exception">异常</param> 72 /// <param name="formatter">格式化委托</param> 73 public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) 74 { 75 if (IsEnabled(logLevel)) 76 { 77 try 78 { 79 Lock._fileLockSlim.EnterWriteLock(); 80 var baseDirectory = _configuration.GetSection("Logging:FileLog:BaseDirectory").Value; 81 var fileName = _configuration.GetSection("Logging:FileLog:FileName").Value; 82 var extensionName = _configuration.GetSection("Logging:FileLog:ExtensionName").Value; 83 84 var directory = Path.Combine(AppContext.BaseDirectory, string.IsNullOrWhiteSpace(baseDirectory) ? "app_log" : baseDirectory); 85 86 directory = Path.Combine(directory, logLevel.ToString());//拼接子目录 87 88 if (!Directory.Exists(directory)) 89 { 90 Directory.CreateDirectory(directory); 91 } 92 if (string.IsNullOrWhiteSpace(fileName)) 93 { 94 fileName = DateTime.Now.ToString("yyyy-MM-dd"); 95 } 96 else 97 { 98 fileName = DateTime.Now.ToString(fileName); 99 } 100 extensionName = string.IsNullOrWhiteSpace(extensionName) ? ".log" : extensionName; 101 102 var path = Path.Combine(directory, $"{fileName}{extensionName}"); 103 var flag = true; 104 if (File.Exists(path)) 105 { 106 var maxSize = _configuration.GetSection("Logging:FileLog:MaxFileSize").Value; 107 var fileInfo = new FileInfo(path); 108 flag = fileInfo.Length / 1024.00 > (string.IsNullOrWhiteSpace(maxSize) ? 2048.00 : Convert.ToDouble(maxSize)); 109 } 110 111 var streamWrite = flag ? File.CreateText(path) : File.AppendText(path); 112 var dateTimeFormart = _configuration.GetSection("Logging:FileLog:DateTimeFormat").Value; 113 114 var logTime = DateTime.Now.ToString((string.IsNullOrWhiteSpace(dateTimeFormart) ? "yyyy-MM-dd HH:mm:ss.fff" : dateTimeFormart)); 115 var message = formatter(state, exception); 116 117 var stackTrace = exception?.StackTrace; 118 119 var template = _configuration.GetSection("Logging:FileLog:Template").Value; 120 121 if (string.IsNullOrWhiteSpace(template)) 122 { 123 streamWrite.WriteLine($"日志时间:{logTime} 类别名称:{_categoryName}[{eventId.Id}] 日志级别:{logLevel} 消息:{message}"); 124 125 if (!string.IsNullOrWhiteSpace(stackTrace)) 126 { 127 streamWrite.WriteLine(stackTrace); 128 } 129 } 130 else 131 { 132 template = template.Replace("{logTime}", logTime, StringComparison.OrdinalIgnoreCase); 133 template = template.Replace("{catetoryName}", _categoryName, StringComparison.OrdinalIgnoreCase); 134 template = template.Replace("{eventId}", eventId.Id.ToString(), StringComparison.OrdinalIgnoreCase); 135 template = template.Replace("{eventName}", eventId.Name, StringComparison.OrdinalIgnoreCase); 136 template = template.Replace("{logLevel}", logLevel.ToString(), StringComparison.OrdinalIgnoreCase); 137 template = template.Replace("{message}", message, StringComparison.OrdinalIgnoreCase); 138 template = template.Replace("{stackTrace}", stackTrace, StringComparison.OrdinalIgnoreCase); 139 template = template.Trim(); 140 streamWrite.WriteLine(template); 141 } 142 143 streamWrite.WriteLine(); 144 streamWrite.Close(); 145 146 var directoryInfo = new DirectoryInfo(directory); 147 var fileInfos = directoryInfo.GetFiles(); 148 var fileCount = Convert.ToInt32(_configuration.GetSection("Logging:FileLog:MaxFileCount").Value); 149 if (fileInfos.Length > fileCount && fileCount > 0) 150 { 151 var removeFileInfo = fileInfos.OrderBy(o => o.CreationTime).ThenBy(o => o.LastWriteTime).SkipLast(fileCount); 152 foreach (var item in removeFileInfo) 153 { 154 File.Delete(item.FullName); 155 } 156 } 157 } 158 catch (Exception ex) 159 { 160 Console.WriteLine($"写入文件日志异常:{ex.Message}"); 161 Console.WriteLine(ex.StackTrace); 162 } 163 finally 164 { 165 Lock._fileLockSlim.ExitWriteLock(); 166 } 167 } 168 } 169 }
四、创建一个静态类增加一个扩展方法 注册服务
1 /// <summary> 2 /// 日志生成器扩展类 3 /// </summary> 4 public static class ILoggingBuilderExtensions 5 { 6 7 /// <summary> 8 /// 添加文件日志 9 /// </summary> 10 /// <param name="loggingBuilder">日志构建</param> 11 public static ILoggingBuilder AddFileLog(this ILoggingBuilder loggingBuilder) 12 { 13 loggingBuilder.Services.AddSingleton<FileLoggerProvider>(); 14 var sevices = loggingBuilder.Services.BuildServiceProvider(); 15 return loggingBuilder.AddProvider(sevices.GetService<FileLoggerProvider>()); 16 } 17 18 }
五、使用方式 .NET6.0为例
var builder = WebApplication.CreateBuilder(args); builder.Logging.AddFileLog();//添加文件日志
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK