Util 应用框架基础(六)- 日志记录 - Exceptionless
Exceptionless
日志记录到 Exceptionless 日志系统
本节介绍 Util 应用框架将日志记录到 Exceptionless 的配置方法.
概述
Exceptionless 是基于 Asp.Net Core 开发的日志系统.
相比 Seq ,它的搜索能力较弱.
日志配置
引用 Nuget 包
Nuget 包名: Util.Logging.Serilog.Exceptionless
AddExceptionless
使用 AddExceptionless 扩展方法启用 Exceptionless 日志操作.
默认不带参数,你可以在配置文件中指定 Exceptionless 的配置信息.
var builder = WebApplication.CreateBuilder( args );
builder.AsBuild().AddExceptionless();
如果要清除默认设置的日志提供程序,传入 true.
Asp.Net Core 默认日志提供程序会把消息输出到控制台,你可以清除它们.
builder.AsBuild().AddExceptionless( true );
设置应用程序名称.
对于微服务应用,记录产生日志的应用名称,能方便排查问题.
builder.AsBuild().AddExceptionless( "权限服务" );
指定 Api 密钥和服务地址.
builder.AsBuild().AddExceptionless( t => {
t.ApiKey = "";
t.ServerUrl = "";
} );
指定 Api 密钥和服务地址并清除默认设置的日志提供程序.
builder.AsBuild().AddExceptionless( t => {
t.ApiKey = "";
t.ServerUrl = "";
}, true );
添加 appsettings 配置节
在 appsettings.json 配置文件添加 Exceptionless 配置节.
{
"Logging": {
"LogLevel": {
"Default": "Trace"
}
},
"Exceptionless": {
"ApiKey": "8JtknZBV1NRC7bdsv6SF5cbBFrMZipWMkARZxkxo",
"ServerUrl": "http://localhost:5480"
}
}
ApiKey 指定 API 密钥.
ServerUrl 指定 Exceptionless 服务地址.
最简化配置仅需设置 API 密钥和 Exceptionless 服务地址.
本教程 Exceptionless 安装示例使用 5480 端口.
API 密钥替换成你自己的.
其它参数请参考 Exceptionless 文档.
查看 Exceptionless
配置完成后,可以启动你的项目,查看 Exceptionless 日志界面.
可以看到由 Asp.Net Core 写入的系统日志.
结构化日志支持
下面的示例比较结构化日志与普通日志的差别.
范例使用 ILog 接口写入日志,你也可以使用 ILogger 替代.
先记录普通日志消息,方便后续比较.
查看扩展属性.
结构化日志语法
{}
{} 用来定义日志属性.
范例:
_log.Message( "用户{User}已删除", "admin" ).LogInformation();
{User} 定义了名为 User 的字符串属性.
查看 Exceptionless 扩展属性界面, 可以看到已经添加了 User 属性.
识别出了 User 属性,就可以使用它进行搜索.
搜索框输入下面的表达式.
data.User:"admin"
User 是一个扩展属性,Exceptionless 要求扩展属性前加上 data.
属性名与属性值使用 : 分隔,表示相等,即 属性名=属性值 .
字符串值可以放进双引号中,比如 "admin"
字符串值也可不带双引号,比如 admin.
但是不能放在单引号中, 比如 'admin' .
Exceptionless 的模糊匹配能力有限,只支持头匹配,类似 like 'xx%' ,不能完全模糊搜索.
Exceptionless 使用 * 进行模糊匹配,只能放在参数值右方, 比如 adm* .
带 * 的参数值不能放在双引号中 .
范例:
以 adm 开头模糊搜索 User 扩展属性.
data.User:adm*
注意: 不要将结构化日志 {} 与 .Net 字符串语法 $"{}" 混淆.
var user = "admin";
_log.Message( $"用户{user}已删除" ).LogInformation();
$"" 中的 {user} 将被 user 变量值 'admin' 替换, 等效于.
_log.Message( "用户admin已删除" ).LogInformation();
它仅是普通日志消息,不是结构化日志.
{@}
{@} 用来定义日志属性,并强制序列化对象.
前面的示例使用简单的字符串参数,如果传入对象参数是什么结果?
范例:
定义 User 对象.
namespace Demo;
public class User {
public int Id { get; set; }
public string Name { get; set; }
}
现在传入 User 对象.
var user = new User { Id = 1, Name = "a" };
_log.Message( "用户{User}已删除", user ).LogInformation();
{User} 被替换为字符串 "Demo.User" ,这是通过调用 User 对象的 ToString() 方法得到的.
这与我们的预期不符合,我们希望序列化 User 对象,从而获取对象的结构进行搜索.
Serilog 对 {} 参数的处理有一套内置规则,如果传入对象,有些结构能序列化,比如字典 Dictionary<string,int> ,有些则不能.
不应该依赖 Serilog 自动序列化的能力,而是应明确指定是否序列化.
{@} 强制序列化对象,从而保留对象结构,以方便日志系统展示和搜索.
var user = new User { Id = 1, Name = "a" };
_log.Message( "用户{@User}已删除", user ).LogInformation();
遗憾的是, Exceptionless 无法搜索对象属性.
虽然 Exceptionless 对扩展属性的搜索支持有限,但它内置了很多搜索语法,可以参考官方文档.
{$}
{$} 用来定义日志属性,并强制字符串化.
{$} 让 Serilog 以明确的方式显示对象的字符串表示.
var user = new User { Id = 1, Name = "a" };
_log.Message( "用户{$User}已删除", user ).LogInformation();
{$User} 调用 user 对象的 ToString() 方法显示为字符串.
评论