dotnet 定制 ILogger 实现
时间:2022-07-24
本文章向大家介绍dotnet 定制 ILogger 实现,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
默认在 dotnet 里面框架提供了 Microsoft.Extensions.Logging 可以和依赖注入做日志框架,而有些业务,如需要自己定制日志行为,此时就需要定制日志
当初写一个类继承 ILogger 是做不到定制,需要再写一个类继承 ILoggerProvider 才好做定制
如以下的方法
public class CCloudConsoleLogProvider : ILoggerProvider
{
/// <inheritdoc />
public void Dispose()
{
}
/// <inheritdoc />
public ILogger CreateLogger(string categoryName)
{
return new CCloudConsoleLogger();
}
class CCloudConsoleLogger : ILogger
{
// 忽略代码
}
}
通过 DI 的注入,在注入之前先干掉其他的 ILoggerProvider 实例
services.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddProvider(new CCloudConsoleLogProvider());
});
现在所有拿到的 ILogger 都是从 CCloudConsoleLogProvider 创建的
下面是我定制的符合 honeycomb log 输出格式的日志,输出内容如下
[2099-10-19 19:07:45.456][threadName][INFO][类:行数][_traceId:realTraceId][_userId:realUserId][tag:custom] 业务的日志/异常堆栈
全部代码
public class CCloudConsoleLogProvider : ILoggerProvider
{
/// <inheritdoc />
public void Dispose()
{
}
/// <inheritdoc />
public ILogger CreateLogger(string categoryName)
{
return new CCloudConsoleLogger(categoryName);
}
class CCloudConsoleLogger : ILogger
{
public CCloudConsoleLogger(string categoryName)
{
_categoryName = categoryName;
}
class Empty : IDisposable
{
/// <inheritdoc />
public void Dispose()
{
}
}
/// <inheritdoc />
public IDisposable BeginScope<TState>(TState state)
{
return new Empty();
}
/// <inheritdoc />
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
private readonly string _categoryName;
/// <inheritdoc />
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter)
{
// [2099-10-19 19:07:45.456][threadName][INFO][类:行数][_traceId:realTraceId][_userId:realUserId][tag:custom] 业务的日志/异常堆栈
string message;
if (typeof(TState) == typeof(CCloudLogInfo))
{
var logInfo = state as CCloudLogInfo;
Debug.Assert(logInfo != null, nameof(logInfo) + " != null");
logInfo.CategoryName = _categoryName;
message = formatter(state, exception);
}
else
{
// [2099-10-19 19:07:45.456][threadName][INFO][类:行数][_traceId:realTraceId][_userId:realUserId][tag:custom] 业务的日志/异常堆栈
message =
$"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}][{Thread.CurrentThread.Name}:{Thread.CurrentThread.ManagedThreadId}][{CCloudLogExtension.LogLevelToString(logLevel)}][{_categoryName}][-][-][-][EventId={eventId.Id}:{eventId.Name}] {formatter(state, exception)}";
}
Console.WriteLine(message);
}
}
}
internal class CCloudLogInfo
{
public string CategoryName { set; get; }
public string ThreadName { get; set; }
public int ThreadId { get; set; }
public string ClassFile { set; get; }
public int LineNumber { get; set; }
public string Message { set; get; }
public string TraceId { set; get; }
public string UserId { set; get; }
public string MemberName { set; get; }
public string[] Tags { set; get; }
public LogLevel LogLevel { set; get; }
}
public static class CCloudLogExtension
{
public static void Error(this ILogger logger,
string message,
Exception exception = null,
string traceId = null,
string userId = null,
[System.Runtime.CompilerServices.CallerMemberName]
string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath]
string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber]
int sourceLineNumber = 0,
params string[] tags)
{
const LogLevel logLevel = LogLevel.Error;
var logInfo = new CCloudLogInfo()
{
ClassFile = Path.GetFileName(sourceFilePath),
ThreadId = Thread.CurrentThread.ManagedThreadId,
ThreadName = Thread.CurrentThread.Name,
LineNumber = sourceLineNumber,
Message = message,
TraceId = traceId,
UserId = userId,
MemberName = memberName,
Tags = tags,
LogLevel = logLevel
};
logger.Log(logLevel, eventId: EmptyEventId, logInfo, exception, Formatter);
}
public static void Warning(this ILogger logger,
string message,
Exception exception = null,
string traceId = null,
string userId = null,
[System.Runtime.CompilerServices.CallerMemberName]
string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath]
string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber]
int sourceLineNumber = 0,
params string[] tags)
{
const LogLevel logLevel = LogLevel.Warning;
var logInfo = new CCloudLogInfo()
{
ClassFile = Path.GetFileNameWithoutExtension(sourceFilePath),
ThreadId = Thread.CurrentThread.ManagedThreadId,
ThreadName = Thread.CurrentThread.Name,
LineNumber = sourceLineNumber,
Message = message,
TraceId = traceId,
UserId = userId,
MemberName = memberName,
Tags = tags,
LogLevel = logLevel
};
logger.Log(logLevel, eventId: EmptyEventId, logInfo, exception, Formatter);
}
// 为什么只有 Info 可以添加 Exception 不添加信息,因为如果是 Warning 和 Error 推荐写是哪个模块
public static void Info(this ILogger logger,
Exception exception = null,
string traceId = null,
string userId = null,
[System.Runtime.CompilerServices.CallerMemberName]
string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath]
string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber]
int sourceLineNumber = 0,
params string[] tags)
{
// ReSharper disable ExplicitCallerInfoArgument
Info(logger, null, exception, traceId, userId, memberName, sourceFilePath, sourceLineNumber, tags);
// ReSharper restore ExplicitCallerInfoArgument
}
public static void Info(this ILogger logger,
string message,
Exception exception = null,
string traceId = null,
string userId = null,
[System.Runtime.CompilerServices.CallerMemberName]
string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath]
string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber]
int sourceLineNumber = 0,
params string[] tags)
{
// 刚好在 Linux 下构建的在 Linux 下运行,而在 Windows 构建的库在 Windows 下执行。此时使用 GetFileNameWithoutExtension 能保持输入路径和解析相同
// 假定在 Windows 下构建而在 Linux 下构建,只是让路径变长而已,我相信咱的日志系统炸不了…… 或者说,炸了再说
// 炸了的解决方法是在 dotnet runtimesrclibrariesSystem.Private.CoreLibsrcSystemIOPath.cs 的 GetFileName 方法里面将 `PathInternal.IsDirectorySeparator(path[i])` 替换为实际需要的 或 / 符号
const LogLevel logLevel = LogLevel.Information;
var logInfo = new CCloudLogInfo()
{
ClassFile = Path.GetFileName(sourceFilePath),
ThreadId = Thread.CurrentThread.ManagedThreadId,
ThreadName = Thread.CurrentThread.Name,
LineNumber = sourceLineNumber,
Message = message,
TraceId = traceId,
UserId = userId,
MemberName = memberName,
Tags = tags,
LogLevel = logLevel
};
logger.Log(logLevel, eventId: EmptyEventId, logInfo, exception, Formatter);
}
public static void Debug(this ILogger logger,
string message,
Exception exception = null,
string traceId = null,
string userId = null,
[System.Runtime.CompilerServices.CallerMemberName]
string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath]
string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber]
int sourceLineNumber = 0,
params string[] tags)
{
var logInfo = new CCloudLogInfo()
{
ClassFile = Path.GetFileName(sourceFilePath),
ThreadId = Thread.CurrentThread.ManagedThreadId,
ThreadName = Thread.CurrentThread.Name,
LineNumber = sourceLineNumber,
Message = message,
TraceId = traceId,
UserId = userId,
MemberName = memberName,
Tags = tags,
LogLevel = LogLevel.Debug
};
logger.Log(logInfo.LogLevel, eventId: EmptyEventId, logInfo, exception, Formatter);
}
private static string Formatter(CCloudLogInfo logInfo, Exception exception)
{
// honeycomb-log
// [2099-10-19 19:07:45.456][threadName][INFO][类:行数][_traceId:realTraceId][_userId:realUserId][tag:custom] 业务的日志/异常堆栈
const string empty = "-";
var traceMessage = string.IsNullOrEmpty(logInfo.TraceId) ? empty : $"_traceId:{logInfo.TraceId}";
var userMessage = string.IsNullOrEmpty(logInfo.UserId) ? empty : $"_userId:{logInfo.UserId}";
var logLevelMessage = LogLevelToString(logInfo.LogLevel);
var logInfoMessage = string.IsNullOrEmpty(logInfo.Message) ? exception?.Message : logInfo.Message;
return $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.sss}][{logInfo.ThreadName}:{logInfo.ThreadId}][{logLevelMessage}][{logInfo.ClassFile}:{logInfo.CategoryName}.{logInfo.MemberName}:{logInfo.LineNumber}][{traceMessage}][{userMessage}][tags:{string.Join(";", logInfo.Tags)}] {logInfoMessage} {exception?.ToString()}";
}
public static string LogLevelToString(LogLevel logLevel)
=> logLevel switch
{
LogLevel.Trace => "TRACE",
LogLevel.Debug => "DEBUG",
LogLevel.Information => "INFO",
LogLevel.Warning => "WARNING",
LogLevel.Error => "ERROR",
LogLevel.Critical => "CRITICAL",
LogLevel.None => "NONE",
_ => "NONE"
};
private static readonly EventId EmptyEventId = new EventId();
}
本文会经常更新,请阅读原文: https://blog.lindexi.com/post/dotnet-%E5%AE%9A%E5%88%B6-ILogger-%E5%AE%9E%E7%8E%B0.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。
- Ajax jsonp 跨域请求实例
- asp.net动态解析用户控件(UserControl)
- 找到多个与名为“Home”的控制器匹配的类型的解决方案
- 关于sequence问题的紧急处理(r2第26天)
- 使用动态变量进行动态数据比较(r2笔记25天)
- MVC分部视图@Html.Partial
- 基于马尔科夫链的产品评估预测
- MVC 控制器中传递dynamic(对象) 给视图
- 使用copy命令解决LONG类型的困扰(r2第24天)
- MVC 获取路由的 URL 参数值和默认值的集合。
- 用libsvm进行回归预测
- mvc路由配置.html结尾的伪静态
- 【编程基础】Java的接口和抽象类
- 循序渐进调优union相关的sql(r2笔记23天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Redis的各种数据类型实践-ZSet
- Python 技术篇-将python项目打包成exe独立运行程序,pyinstaller库打包python代码
- Spring MVC 工作流程简介
- 8个尽量优化你的SQL
- 记一次 Kafka Producer 性能调优实战
- Kafka 独立消费者
- 【iOS】记录iOS14以及xcode12 遇到的问题
- 如果你还在犹豫要不要入行,请先看看我的IT入坑记【技术创作101训练营】
- Session、Cookie、Token 【浅谈三者之间的那点事】
- Python 微信机器人-20行代码实现斗图功能,简单易懂,全是干货!斗图啦API调用方法
- 浅谈布隆过滤器
- Python 技术篇-获取图片GPS信息,锁定追踪图片拍摄地点、拍摄时间
- 测试工具 - Postman接口测试入门使用手册,Postman如何进行数据关联、自动更新cookies、简单编程
- 白盒测试工具 - sonar的安装、配置与使用入门手册,用sonar检查代码质量实战演示
- Chmod -R 777 / 误操作恢复教程