一、定义一个静态类 声明一个ReaderWriterLockSlim 对象 用于并发控制 1 /// summary 2 /// IO锁 3 /// /summary 4 public static class Lock 5 { 6 7 /// summary 8 /// 文件读写锁 9 /// /summary 10 public static readonly Re
一、定义一个静态类 声明一个 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();//添加文件日志
六、json文件配置格式 原生格式 一如既往喜欢微软原生实现的方式
"Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information" }, "FileLog": { "LogLevel": { "Default": "Information" }, "BaseDirectory": "app_log", "FileName": "yyyy-MM-dd", "ExtensionName": ".log", "Template": "LogTime:{LogTime} CatetoryName:{CatetoryName} LogLevel:{LogLevel}\r\n{Message}\r\n{StackTrace}\r\n", "MaxFileCount": 10, "MaxFileSize": 2048, "DateTimeFormat": "yyyy-MM-dd HH:mm:ss.fff" } }