原生实现.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服务层的调用过 ...
随机推荐
- k8s面试1-27
目录 1.k8s常用命令有哪些? 2.报错查看各种日志方法? 3.k8s的组建有哪些? 4.k8s中安全机制是什么? 5.常用的控制器有哪些? 6.service类型有哪些? 7.ingress-Ng ...
- 【虚拟机】VMware-VMware Tool安装
1.安装 在VMware Workstation主界面点击菜单"虚拟机".会弹出提示框,点击是. 保持网络状态,等待后台下载,下载后会出现一个压缩包(如果没有出现压缩包请点击这里查 ...
- k8s入门之Service(六)
将一组pod公开为网络服务,通过service代理,可以实现负载均衡 一.ClusterIP 此方式只能在集群内访问 1.使用命令暴露已存在的pod (1)继续使用前面章节的案例,查看名称为nginx ...
- javaScript深拷贝和浅拷贝简单梳理
在了解深拷贝和浅拷贝之前,我们先梳理一下: JavaScript中,分为基本数据类型(原始值)和复杂类型(对象),同时它们各自的数据类型细分下又有好几种数据类型 基本数据类型 数字Number 字符串 ...
- 关于VR(虚拟现实)的探讨
从外部来看:一个完整的系统由输入和输出组成,人体也不例外.人的输入系统一般称为感官系统,主要由口耳眼鼻舌和皮肤组成,它们对应于味觉.听觉.视觉.嗅觉和触觉.生而为人,我们对于外部世界的感知主要来自于上 ...
- GDB调试小白教程
1.GDB是什么? 想必很多人都用过windows下各种编译器软件的调试功能,例如Visio Studio里面"断点"."开始调试"."逐语句&quo ...
- UDP协议,多道技术,进程,同步与异步,阻塞与非阻塞
UDP协议 简介 UDP叫做用户数据报协议,是OSI七层参考模型中传输层使用的协议,他提供的是不可靠传输,既它在传输过程 中不保证数据的完整性! 端口号 UDP使用IP地址和端口号进行标识,以此将数据 ...
- Linux中文件/文件系统的压缩、打包和备份总结(基于rhel7)
文件/文件系统的压缩.打包 Linux有哪些压缩工具可供选择 按压缩比:xz>bzip2>gzip,按压缩时长:gzip>bzip2>xz,另外还有zip可以选择. gzip只 ...
- Vulnhub-earth
1.信息收集 1.1找到目标主机ip vulnhub靶场通用的技巧 这里我们靶场是nat模式的 所以肯定就是在我们自己设置的一个网段范围内,我这里nat本机的地址是10.1.1.1 所处的网段就是10 ...
- 143. Reorder List - LeetCode
Question 143. Reorder List Solution 题目大意:给一个链表,将这个列表分成前后两部分,后半部分反转,再将这两分链表的节点交替连接成一个新的链表 思路 :先将链表分成前 ...