原生实现.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服务层的调用过 ...
随机推荐
- NodeJS学习day2
今天还是接着学习IO,主要方面是文件路径相关操作 练习代码如下: const fs = require('fs') // 执行C:\CS\Node.js>node .\day2\fsRoad.j ...
- jsp第六次作业
1. <%@ page language="java" import="java.util.*" pageEncoding="utf-8&quo ...
- 用 GraphScope 像 NetworkX 一样做图分析
NetworkX 是 Python 上最常用的图分析包,GraphScoep 兼容 NetworkX 接口.本文中我们将分享如何用 GraphScope 像 NetworkX 一样在(大)图上进行分析 ...
- .NET 6上的WebView2体验
上次说为了不想在web端登录博客园,我想着还是继续使用MarkWord编写博客,不过在使用的过程中,如果markdown文件的目录中有中文的话,Markdown预览就不能够显示粘贴的图片了,原因是之前 ...
- Ajax简单运用(JavaScript-----jQuery-------)
首先引用 jQuery <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.4.1.min.js"> ...
- 基于Koa与umi实现服务端(SSR)渲染
工具: umijs:react前端应用框架. koa:基于 Node.js 平台的web 开发框架. 介绍: 本文主要是简单介绍,利用umi开发前端页面,打包成服务端渲染工程包.由Koa实现服务端渲染 ...
- 团队Arpha6
队名:观光队 链接 组长博客 作业博客 组员实践情况 黄恒杰 - **过去两天完成了哪些任务 ** - 文字/口头描述 地图功能增加.博客 - 展示GitHub当日代码/文档签入记录 - 接下来的计划 ...
- 【原创】ShellCode免杀的骚姿势
ShellCode免杀的骚姿势 常见的免杀手法: shellcode(攻击代码)和加载程序的分离: Lolbins白利用加载shellcode(白名单利用): shellcode混淆.编码解码: sh ...
- 561. Array Partition I - LeetCode
Question 561. Array Partition I Solution 题目大意是,给的数组大小是2n,把数组分成n组,每组2个元素,每个组取最小值,这样就能得到n个值,怎样分组才能使这n个 ...
- Java获取特定区间随机数及产生不重复随机数
问题 有这样一种需求,在这样一个数组中String[] arr = new String[]{"电商", "互联网", "小程序", &qu ...