原生实现.NET5.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();//添加文件日志
原生实现.NET5.0+ 自定义日志的更多相关文章
- c#自定义日志记录
废话不多说,直接上代码: 很简单:将类复制到项目中,最后在配置文件上配置一下:logUrl即可. 默认保存在:项目/temp/log /// <summary> /// 日志类 /// & ...
- vue.js2.0 自定义组件初体验
理解 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能.在有些情况 ...
- Python3自定义日志类教程
一.说明 Python3的logging功能是比较丰富的支持不同层次的日志输出,但或是我们想在日志前输出时间.或是我们想要将日志输入到文件,我们还是想要自定义日志类. 之前自己也尝试写过但感觉文档太乱 ...
- ELK收集Nginx自定义日志格式输出
1.ELK收集日志的有两种常用的方式: 1.1:不修改源日志格式,简单的说就是在logstash中转通过 grok方式进行过滤处理,将原始无规则的日志转换为规则日志(Logstash自定义日志格式) ...
- 【SpringBoot】SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener
=================6.SpringBoot拦截器实战和 Servlet3.0自定义Filter.Listener ============ 1.深入SpringBoot2.x过滤器Fi ...
- 涨姿势:Java 分业务、分级别实现自定义日志打印
自定义日志级别 通常的日志框架都有以下几个级别,从低到高TRACE,DEBUG,INFO,WARN,ERROR,FATAL. 默认情况,假如我们定义日志打印级别INFO,它会把大于等于INFO级别的日 ...
- Windows server2012 IIs 8 自定义日志记录
问题: 通过CDN加速的网站,记录日志时无法追踪源IP,日志的IP都为CDN节点ip. 分析: 1.在解析记录header时,CDN实际会把源IP以其它header的形式回传,如网宿为[Cdn-Src ...
- shell脚本中自定义日志记录到文件
自定义日志函数和前期变量 # adirname - return absolute dirname of given file adirname() { odir=`pwd`; cd `dirname ...
- Dubbo自定义日志拦截器
前言 上一篇文章 Spring aop+自定义注解统一记录用户行为日志 记录了 web层中通过自定义注解配合Spring aop自动记录用户行为日志的过程.那么按照分布式架构中Dubbo服务层的调用过 ...
随机推荐
- 从零搭建Pytorch模型教程(三)搭建Transformer网络
前言 本文介绍了Transformer的基本流程,分块的两种实现方式,Position Emebdding的几种实现方式,Encoder的实现方式,最后分类的两种方式,以及最重要的数据格式的介绍. ...
- python3-认识内置对象,运算符,表达式
1 . 认识内置对象 在python中一切皆对象, 整数,实数,复数,字符串,列表,元组,字典,集合,zip, map, enumerate, filter , 函数 ,类 , 分类:内置对象,标准 ...
- Docker安装 Ubuntu Centos
Ubuntu 安装Dokcer 1. 删除旧版本Docker安装包和依赖项 sudo apt-get remove docker docker-engine docker.io containerd ...
- 尤娜故事-迷雾-springboot扮酷小技巧
前情回顾 从前,有一个简单的通道系统叫尤娜-- 尤娜系统的第一次飞行中换引擎的架构垂直拆分改造 四种常用的微服务架构拆分方式 尤娜,我去面试了 正文 我回到日常的尤娜系统建设中,最近事情比较少,总有一 ...
- FreeRTOS --(9)任务管理之启动调度器
转载自 https://blog.csdn.net/zhoutaopower/article/details/107057528 在使用 FreeRTOS 的时候,一般的,先创建若干任务,但此刻任务并 ...
- 借助ADB冻结与卸载Android系统应用(免ROOT)
背景: 我妈的手机饱受系统应用广告推送之苦,每天都能在通知栏里收到好几条广告.为了给她个清净,本篇博文应运而生. 目标: 卸载安卓系统应用 所用工具: 硬件:我妈的手机(魅蓝5) PC端:Minima ...
- c++:-5
上一节学习C++中的继承和派生:c++:-4,本节学习C++的多态. 运算符重载 思考:用"+"."-"能够实现复数的加减运算吗? 实现复数加减运算的方法 -- ...
- SpringBoot如何优雅关闭(SpringBoot2.3&Spring Boot2.2)
SpringBoot如何优雅关闭(SpringBoot2.3&Spring Boot2.2) 优雅停止&暴力停止 暴力停止:像日常开发过程中,测试区或者本地开发时,我们并不会考虑项目关 ...
- 一个程序的自我修养「GitHub 热点速览 v.22.19」
一个程序要诞生涉及前后端技术,比如,你可以用可视化网页搭建工具 tmagic-editor 完成前端部分,而后端部分的数据库以及数据处理可能就要用到 jsonhero-web 和 directus.知 ...
- 通过CSS让图片变的清楚
image { width: 100%; height: 100%; border-radius: 10upx; //让图片变清楚 image-rendering: -moz-crisp-edges; ...