.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日志并进行多维度分析。
目录
相关文章
|
1月前
|
存储 开发框架 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`,优化了内存使用和序列化速度。
|
14天前
|
关系型数据库 C# 数据库
.NET 8.0 开源在线考试系统(支持移动端)
【10月更文挑战第27天】以下是适用于 .NET 8.0 的开源在线考试系统(支持移动端)的简介: 1. **基于 .NET Core**:跨平台,支持多种数据库,前后端分离,适用于多操作系统。 2. **结合 Blazor**:使用 C# 开发 Web 应用,支持响应式设计,优化移动端体验。 3. **基于 .NET MAUI**:跨平台移动应用开发,一套代码多平台运行,提高开发效率。 开发时需关注界面设计、安全性与稳定性。
|
2月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
2月前
|
开发框架 .NET 中间件
ASP.NET Core Web 开发浅谈
本文介绍ASP.NET Core,一个轻量级、开源的跨平台框架,专为构建高性能Web应用设计。通过简单步骤,你将学会创建首个Web应用。文章还深入探讨了路由配置、依赖注入及安全性配置等常见问题,并提供了实用示例代码以助于理解与避免错误,帮助开发者更好地掌握ASP.NET Core的核心概念。
87 3
|
21天前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
2月前
|
JSON 安全 数据安全/隐私保护
从0到1搭建权限管理系统系列三 .net8 JWT创建Token并使用
【9月更文挑战第22天】在.NET 8中,从零开始搭建权限管理系统并使用JWT(JSON Web Tokens)创建Token是关键步骤。JWT是一种开放标准(RFC 7519),用于安全传输信息,由头部、载荷和签名三部分组成。首先需安装`Microsoft.AspNetCore.Authentication.JwtBearer`包,并在`Program.cs`中配置JWT服务。接着,创建一个静态方法`GenerateToken`生成包含用户名和角色的Token。最后,在控制器中使用`[Authorize]`属性验证和解析Token,从而实现身份验证和授权功能。
112 3
|
25天前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
2月前
|
开发框架 NoSQL .NET
利用分布式锁在ASP.NET Core中实现防抖
【9月更文挑战第5天】在 ASP.NET Core 中,可通过分布式锁实现防抖功能,仅处理连续相同请求中的首个请求,其余请求返回 204 No Content,直至锁释放。具体步骤包括:安装分布式锁库如 `StackExchange.Redis`;创建分布式锁服务接口及其实现;构建防抖中间件;并在 `Startup.cs` 中注册相关服务和中间件。这一机制有效避免了短时间内重复操作的问题。
|
3月前
|
设计模式 存储 前端开发
揭秘.NET架构设计模式:如何构建坚不可摧的系统?掌握这些,让你的项目无懈可击!
【8月更文挑战第28天】在软件开发中,设计模式是解决常见问题的经典方案,助力构建可维护、可扩展的系统。本文探讨了.NET中三种关键架构设计模式:MVC、依赖注入与仓储模式,并提供了示例代码。MVC通过模型、视图和控制器分离关注点;依赖注入则通过外部管理组件依赖提升复用性和可测性;仓储模式则统一数据访问接口,分离数据逻辑与业务逻辑。掌握这些模式有助于开发者优化系统架构,提升软件质量。
52 5
|
3月前
|
C# Windows 开发者
超越选择焦虑:深入解析WinForms、WPF与UWP——谁才是打造顶级.NET桌面应用的终极利器?从开发效率到视觉享受,全面解读三大框架优劣,助你精准匹配项目需求,构建完美桌面应用生态系统
【8月更文挑战第31天】.NET框架为开发者提供了多种桌面应用开发选项,包括WinForms、WPF和UWP。WinForms简单易用,适合快速开发基本应用;WPF提供强大的UI设计工具和丰富的视觉体验,支持XAML,易于实现复杂布局;UWP专为Windows 10设计,支持多设备,充分利用现代硬件特性。本文通过示例代码详细介绍这三种框架的特点,帮助读者根据项目需求做出明智选择。以下是各框架的简单示例代码,便于理解其基本用法。
128 0