.Net Core Logger 实现log写入本地文件系统
.net core 自带一个基础的logger框架Microsoft.Extensions.Logging。
微软默认实现了Microsoft.Extensions.Logging.Console.dll。控制台的日志输出和Microsoft.Extensions.Logging.Debug.dll调试输出。
下面我们写一个我们自己的本地文件输出模块demo,简单理解一下自带的这个logger系统。
logger框架主要几个类:LoggerFactory,Logger,LoggerProvider。
看名字就很好理解,都不需要解释。
实现我们自己的file logger只需要实现logger,loggerProvider即可。
第一步:入口。
loggerFactory.AddFile(this.Configuration.GetSection("FileLogging"));
为LoggerFactory扩张一个方法,提供增加日志写文件方式的入口。相关的配置来自appsettings.json
1 public static class FileLoggerExtensions 2 { 3 //add 日志文件创建规则,分割规则,格式化规则,过滤规则 to appsettings.json 4 public static ILoggerFactory AddFile(this ILoggerFactory factory, IConfiguration configuration) 5 { 6 return AddFile(factory, new FileLoggerSettings(configuration)); 7 } 8 public static ILoggerFactory AddFile(this ILoggerFactory factory, FileLoggerSettings fileLoggerSettings) 9 { 10 factory.AddProvider(new FileLoggerProvider(fileLoggerSettings)); 11 return factory; 12 } 13 }
第二步:实现我们的logger提供程序,实现ILoggerProvider接口
public class FileLoggerProvider : ILoggerProvider, Idisposable
关键方法CreateLogger,创建真正写日志的logger。对当前的logger可以做适当的缓存,配置logger
1 public class FileLoggerProvider : ILoggerProvider, IDisposable 2 { 3 FileLoggerSettings _configuration; 4 readonly ConcurrentDictionary<string, InitLoggerModel> _loggerKeys = new ConcurrentDictionary<string, InitLoggerModel>(); 5 readonly ConcurrentDictionary<string, FileLogger> _loggers = new ConcurrentDictionary<string, FileLogger>(); 6 7 public FileLoggerProvider(FileLoggerSettings configuration) 8 { 9 _configuration = configuration; 10 _configuration.ChangeToken.RegisterChangeCallback(p => 11 { 12 //appsettings.json changed. reload settings. 13 _configuration.Reload(); 14 15 //update loggers settings form new settings 16 foreach (var item in this._loggers.Values) 17 { 18 InitLoggerModel model = new InitLoggerModel(); 19 InitLoggerSettings(item.Name, model); 20 InitLogger(model, item); 21 } 22 23 }, null); 24 } 25 public ILogger CreateLogger(string categoryName) 26 { 27 var loggerKey = this._loggerKeys.GetOrAdd(categoryName, p => 28 { 29 InitLoggerModel model = new InitLoggerModel(); 30 InitLoggerSettings(categoryName, model); 31 return model; 32 }); 33 var key = loggerKey.FileDiretoryPath + loggerKey.FileNameTemplate; 34 return this._loggers.GetOrAdd(key, p => 35 { 36 var logger = new FileLogger(categoryName); 37 InitLogger(loggerKey, logger); 38 return logger; 39 }); 40 } 41 42 private static void InitLogger(InitLoggerModel model, FileLogger logger) 43 { 44 logger.FileNameTemplate = model.FileNameTemplate; 45 logger.FileDiretoryPath = model.FileDiretoryPath; 46 logger.MinLevel = model.MinLevel; 47 } 48 49 class InitLoggerModel 50 { 51 public LogLevel MinLevel { get; set; } 52 public string FileDiretoryPath { get; set; } 53 public string FileNameTemplate { get; set; } 54 55 public override int GetHashCode() 56 { 57 return this.MinLevel.GetHashCode() + this.FileDiretoryPath.GetHashCode() + this.FileNameTemplate.GetHashCode(); 58 } 59 public override bool Equals(object obj) 60 { 61 var b = obj as InitLoggerModel; 62 if (b == null) 63 return false; 64 return this.MinLevel == b.MinLevel && this.FileDiretoryPath == b.FileDiretoryPath && this.FileNameTemplate == b.FileNameTemplate; 65 } 66 67 } 68 private void InitLoggerSettings(string categoryName, InitLoggerModel model) 69 { 70 model.MinLevel = LogLevel.Debug; 71 var keys = this.GetKeys(categoryName); 72 foreach (var item in keys) 73 { 74 var switchV = _configuration.GetSwitch(item); 75 if (switchV.Item1) 76 { 77 model.MinLevel = switchV.Item2; 78 break; 79 } 80 } 81 model.FileDiretoryPath = this._configuration.DefaultPath; 82 foreach (var item in keys) 83 { 84 var switchV = _configuration.GetDiretoryPath(item); 85 if (switchV.Item1) 86 { 87 model.FileDiretoryPath = switchV.Item2; 88 break; 89 } 90 } 91 model.FileNameTemplate = this._configuration.DefaultFileName; 92 foreach (var item in keys) 93 { 94 var switchV = _configuration.GetFileName(item); 95 if (switchV.Item1) 96 { 97 model.FileNameTemplate = switchV.Item2; 98 break; 99 } 100 } 101 } 102 103 IEnumerable<string> GetKeys(string categoryName) 104 { 105 while (!String.IsNullOrEmpty(categoryName)) 106 { 107 // a.b.c 108 //--result 109 // a.b.c,a.b,a,Default 110 yield return categoryName; 111 var last = categoryName.LastIndexOf('.'); 112 if (last <= 0) 113 { 114 yield return "Default"; 115 yield break; 116 } 117 System.Diagnostics.Debug.WriteLine(categoryName + "--" + last); 118 categoryName = categoryName.Substring(0, last); 119 } 120 yield break; 121 122 } 123 public void Dispose() 124 { 125 } 126 }
第三步:实现我们的logger,实现ILogger接口。真正将log写入file
public class FileLogger : Ilogger
1 public class FileLogger : ILogger 2 { 3 static protected string delimiter = new string(new char[] { (char)1 }); 4 public FileLogger(string categoryName) 5 { 6 this.Name = categoryName; 7 } 8 class Disposable : IDisposable 9 { 10 public void Dispose() 11 { 12 } 13 } 14 Disposable _DisposableInstance = new Disposable(); 15 public IDisposable BeginScope<TState>(TState state) 16 { 17 return _DisposableInstance; 18 } 19 public bool IsEnabled(LogLevel logLevel) 20 { 21 return this.MinLevel <= logLevel; 22 } 23 public void Reload() 24 { 25 _Expires = true; 26 } 27 28 public string Name { get; private set; } 29 30 public LogLevel MinLevel { get; set; } 31 public string FileDiretoryPath { get; set; } 32 public string FileNameTemplate { get; set; } 33 public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) 34 { 35 if (!this.IsEnabled(logLevel)) 36 return; 37 var msg = formatter(state, exception); 38 this.Write(logLevel, eventId, msg, exception); 39 } 40 void Write(LogLevel logLevel, EventId eventId, string message, Exception ex) 41 { 42 EnsureInitFile(); 43 44 //TODO 提高效率 队列写!!! 45 var log = String.Concat(DateTime.Now.ToString("HH:mm:ss"), '[', logLevel.ToString(), ']', '[', 46 Thread.CurrentThread.ManagedThreadId.ToString(), ',', eventId.Id.ToString(), ',', eventId.Name, ']', 47 delimiter, message, delimiter, ex?.ToString()); 48 lock (this) 49 { 50 this._sw.WriteLine(log); 51 } 52 } 53 54 bool _Expires = true; 55 string _FileName; 56 protected StreamWriter _sw; 57 void EnsureInitFile() 58 { 59 if (CheckNeedCreateNewFile()) 60 { 61 lock (this) 62 { 63 if (CheckNeedCreateNewFile()) 64 { 65 InitFile(); 66 _Expires = false; 67 } 68 } 69 } 70 } 71 bool CheckNeedCreateNewFile() 72 { 73 if (_Expires) 74 { 75 return true; 76 } 77 //TODO 使用 RollingType判断是否需要创建文件。提高效率!!! 78 if (_FileName != DateTime.Now.ToString(this.FileNameTemplate)) 79 { 80 return true; 81 } 82 return false; 83 } 84 void InitFile() 85 { 86 if (!Directory.Exists(this.FileDiretoryPath)) 87 { 88 Directory.CreateDirectory(this.FileDiretoryPath); 89 } 90 var path = ""; 91 int i = 0; 92 do 93 { 94 _FileName = DateTime.Now.ToString(this.FileNameTemplate); 95 path = Path.Combine(this.FileDiretoryPath, _FileName + "_" + i + ".log"); 96 i++; 97 } while (System.IO.File.Exists(path)); 98 var oldsw = _sw; 99 _sw = new StreamWriter(new FileStream(path, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read), Encoding.UTF8); 100 _sw.AutoFlush = true; 101 if (oldsw != null) 102 { 103 try 104 { 105 _sw.Flush(); 106 _sw.Dispose(); 107 } 108 catch 109 { 110 } 111 } 112 } 113 }
代码:https://github.com/czd890/NetCoreWebApp