.Net Core Logger 实现log写入本地文件系统

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: .net core 自带一个基础的logger框架Microsoft.Extensions.Logging。 微软默认实现了Microsoft.Extensions.Logging.Console.dll。

.net core 自带一个基础的logger框架Microsoft.Extensions.Logging

微软默认实现了Microsoft.Extensions.Logging.Console.dll。控制台的日志输出和Microsoft.Extensions.Logging.Debug.dll调试输出。

下面我们写一个我们自己的本地文件输出模块demo,简单理解一下自带的这个logger系统。

logger框架主要几个类:LoggerFactoryLoggerLoggerProvider

看名字就很好理解,都不需要解释。

实现我们自己的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     }
View Code

第二步:实现我们的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     }
View Code

第三步:实现我们的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     }
View Code

代码:https://github.com/czd890/NetCoreWebApp

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
12天前
|
开发框架 .NET 开发者
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
35 5
|
2月前
|
存储 开发框架 JSON
ASP.NET Core OData 9 正式发布
【10月更文挑战第8天】Microsoft 在 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9,此版本与 .NET 8 的 OData 库保持一致,改进了数据编码以符合 OData 规范,并放弃了对旧版 .NET Framework 的支持,仅支持 .NET 8 及更高版本。新版本引入了更快的 JSON 编写器 `System.Text.UTF8JsonWriter`,优化了内存使用和序列化速度。
|
1月前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
40 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
20天前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
24 3
|
1月前
|
开发框架 安全 Java
.NET技术的独特魅力与优势,涵盖高效的开发体验、强大的性能表现、高度的可扩展性及丰富的生态系统等方面,展示了其在软件开发领域的核心竞争力
本文深入探讨了.NET技术的独特魅力与优势,涵盖高效的开发体验、强大的性能表现、高度的可扩展性及丰富的生态系统等方面,展示了其在软件开发领域的核心竞争力。.NET不仅支持跨平台开发,具备出色的安全性和稳定性,还能与多种技术无缝集成,为企业级应用提供全面支持。
34 3
|
2月前
|
关系型数据库 C# 数据库
.NET 8.0 开源在线考试系统(支持移动端)
【10月更文挑战第27天】以下是适用于 .NET 8.0 的开源在线考试系统(支持移动端)的简介: 1. **基于 .NET Core**:跨平台,支持多种数据库,前后端分离,适用于多操作系统。 2. **结合 Blazor**:使用 C# 开发 Web 应用,支持响应式设计,优化移动端体验。 3. **基于 .NET MAUI**:跨平台移动应用开发,一套代码多平台运行,提高开发效率。 开发时需关注界面设计、安全性与稳定性。
|
3月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
2月前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
2月前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
1月前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
315 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板