一、定义一个静态类 声明一个 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+ 自定义日志的更多相关文章

  1. c#自定义日志记录

    废话不多说,直接上代码: 很简单:将类复制到项目中,最后在配置文件上配置一下:logUrl即可. 默认保存在:项目/temp/log /// <summary> /// 日志类 /// & ...

  2. vue.js2.0 自定义组件初体验

    理解 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能.在有些情况 ...

  3. Python3自定义日志类教程

    一.说明 Python3的logging功能是比较丰富的支持不同层次的日志输出,但或是我们想在日志前输出时间.或是我们想要将日志输入到文件,我们还是想要自定义日志类. 之前自己也尝试写过但感觉文档太乱 ...

  4. ELK收集Nginx自定义日志格式输出

    1.ELK收集日志的有两种常用的方式: 1.1:不修改源日志格式,简单的说就是在logstash中转通过 grok方式进行过滤处理,将原始无规则的日志转换为规则日志(Logstash自定义日志格式) ...

  5. 【SpringBoot】SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener

    =================6.SpringBoot拦截器实战和 Servlet3.0自定义Filter.Listener ============ 1.深入SpringBoot2.x过滤器Fi ...

  6. 涨姿势:Java 分业务、分级别实现自定义日志打印

    自定义日志级别 通常的日志框架都有以下几个级别,从低到高TRACE,DEBUG,INFO,WARN,ERROR,FATAL. 默认情况,假如我们定义日志打印级别INFO,它会把大于等于INFO级别的日 ...

  7. Windows server2012 IIs 8 自定义日志记录

    问题: 通过CDN加速的网站,记录日志时无法追踪源IP,日志的IP都为CDN节点ip. 分析: 1.在解析记录header时,CDN实际会把源IP以其它header的形式回传,如网宿为[Cdn-Src ...

  8. shell脚本中自定义日志记录到文件

    自定义日志函数和前期变量 # adirname - return absolute dirname of given file adirname() { odir=`pwd`; cd `dirname ...

  9. Dubbo自定义日志拦截器

    前言 上一篇文章 Spring aop+自定义注解统一记录用户行为日志 记录了 web层中通过自定义注解配合Spring aop自动记录用户行为日志的过程.那么按照分布式架构中Dubbo服务层的调用过 ...

随机推荐

  1. Postman+newman+jenkins+git实战

    一.接口分类,流程,用例设计 接口分类: 外部接口:被测系统与外部其他系统之间的接口. 承保系统(被测系统),核算系统. 内部接口:被测系统内部各个子模块之间的接口. 承保系统(A模块,B模块) 测试 ...

  2. @Resource与构造函数踩坑

    (虽然解决了需求,但我还是没搞懂为什么构造函数结束后,调用userMapper注入还是为空!) 首先,我有一个没有问题的userMapper类,用于处理user的数据库处理. 其次,我在另一个类里面使 ...

  3. 数据结构篇(3)ts 实现双向链表

    如今终于到了双向链表了,此前在Node结构中的prev指针终于派上了用场.由于双向链表多了一个前向指针,所以有些操作和单向链表比较起来反而更加的简单. class DbList extends Cir ...

  4. 《Mybatis 手撸专栏》第6章:数据源池化技术实现

    作者:小傅哥 博客:https://bugstack.cn - 手写Mybatis系列文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 码农,只会做不会说? 你有发现吗,其实很大一部分码农 ...

  5. 团队Beta5

    队名:观光队 链接 组长博客 作业博客 组员实践情况 王耀鑫 **过去两天完成了哪些任务 ** 文字/口头描述 学习 展示GitHub当日代码/文档签入记录 无 接下来的计划 无 **还剩下哪些任务 ...

  6. 一文看懂:网址,URL,域名,IP地址,DNS,域名解析

    一个执着于技术的公众号 前言 今天给大家梳理一篇关于网址.URL.IP地址.域名.DNS.域名解析的白话长文,并以简单的提问-解答 形式让读者更加深刻理解,希望有助于读者的学习,面试和工作! 1.一个 ...

  7. Apache Struts 2 漏洞汇总

    Apache Struts2 是一个基于MVC设计模式的Web应用框架,会对某些标签属性(比如 id)的属性值进行二次表达式解析,因此在某些场景下将可能导致远程代码执行. Struts2特征: 通过页 ...

  8. HamsterBear Linux Low Res ADC按键驱动的适配 + LVGL button移植

    HamsterBear lradc按键驱动的适配 平台 - F1C200s Linux版本 - 5.17.2 ADC按键 - 4 KEY tablet 驱动程序位于主线内核: drivers/inpu ...

  9. 手动验证 TLS 证书

    证书结构 我们现在使用的 TLS 证书的标准是 X.509,版本号为 V3.版本号可从证书的 Version 字段看到. 根据 RFC 3280 定义的证书结构,证书由三个部分组成: 证书主体(TBS ...

  10. zabbix 线路质量监控自定义python模块,集成ICMP/TCP/UDP探测,批量监控线路质量自定义阈值联动mtr保存线路故障日志并发送至noc邮箱

    互联网故障一般表现为丢包和时延增大,持续性故障不难排查,难的是间歇性或凌晨故障,后者往往来不及等我们测试就已经恢复正常,得不到异常时的mtr无法判断故障点在哪里 故此有了根据丢包率和时延变换联动mtr ...