Serilog 中的异常解构
Posted
技术标签:
【中文标题】Serilog 中的异常解构【英文标题】:Exception destructuring in Serilog 【发布时间】:2014-10-09 00:33:24 【问题描述】:Serilog 有一种方便的解构对象的方法,如下例所示:
logger.Debug(exception, "This is an Exception text", exception);
logger.Debug(exception, "This is an @Exception structure", exception);
第一行使记录器将异常记录为纯文本(通过调用 ToString()),第二行使记录器将异常属性写为单独的字段。但是这个重载呢:
logger.Debug(exception, "This is an exception", exception);
这个函数将异常作为它的第一个参数,并且它总是被写成一个字符串。我希望以结构化的方式启用日志记录异常。是否可以配置 Serilog 来实现这一点?
更新。我想这个问题会导致记录异常的另一个方面:如何确保消息丰富了异常属性(因此它们以结构化方式记录到像 Elasticsearch 这样的丰富接收器)而不将所有异常属性写入呈现的文本消息(所以纯文本记录器不会充满大量异常细节)。
【问题讨论】:
【参考方案1】:查看Serilog.Exceptions 记录异常详细信息和未在 Exception.ToString() 中输出的自定义属性。
这个库有自定义代码来处理大多数常见异常类型的额外属性,并且只有在 Serilog.Exceptions 内部不支持异常时才回退到使用反射来获取额外信息。
添加 NuGet 包,然后添加丰富器,如下所示:
using Serilog;
using Serilog.Exceptions;
ILogger logger = new LoggerConfiguration()
.Enrich.WithExceptionDetails()
.WriteTo.Sink(new RollingFileSink(
@"C:\logs",
new JsonFormatter(renderMessage: true))
.CreateLogger();
您的 JSON 日志现在将补充详细的异常信息,甚至自定义异常属性。下面是一个示例,说明当您从 EntityFramework 记录 DbEntityValidationException 时会发生什么(此异常因具有不包含在 .ToString()
中的深度嵌套的自定义属性而臭名昭著)。
try
...
catch (DbEntityValidationException exception)
logger.Error(exception, "Hello World");
上面的代码记录了以下内容:
"Timestamp": "2015-12-07T12:26:24.0557671+00:00",
"Level": "Error",
"MessageTemplate": "Hello World",
"RenderedMessage": "Hello World",
"Exception": "System.Data.Entity.Validation.DbEntityValidationException: Message",
"Properties":
"ExceptionDetail":
"EntityValidationErrors": [
"Entry": null,
"ValidationErrors": [
"PropertyName": "PropertyName",
"ErrorMessage": "PropertyName is Required.",
"Type": "System.Data.Entity.Validation.DbValidationError"
],
"IsValid": false,
"Type": "System.Data.Entity.Validation.DbEntityValidationResult"
],
"Message": "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.",
"Data": ,
"InnerException": null,
"TargetSite": null,
"StackTrace": null,
"HelpLink": null,
"Source": null,
"HResult": -2146232032,
"Type": "System.Data.Entity.Validation.DbEntityValidationException"
,
"Source": "418169ff-e65f-456e-8b0d-42a0973c3577"
Serilog.Exceptions 支持 .NET 标准并支持许多常见的不带反射的异常类型,但我们想添加更多,所以请随时贡献。
重要提示 - 人类可读的堆栈跟踪
如果您使用 Serilog,您可以使用 Ben.Demystifier NuGet 包获取人类可读的异常堆栈跟踪,或者使用 serilog-enrichers-demystify NuGet 包。
【讨论】:
默认的 JsonFormatter 不使用任何格式。我们如何让它在控制台接收器中使用打印良好的 JSON 对象? 我正在使用Serilog.Exceptions
,但不幸的是,它将异常的整个堆栈跟踪记录在日志的“异常”属性中,而不是“异常详细信息”中。我该如何解决这个问题?
@Junaid 这是 Serilogs 的默认行为。 Serilog.Exceptions 只是在 ExceptionDetails 中添加了额外的元数据。如果你真的想要,你可以启用一个选项来将 StackTrace 添加到 ExceptionDetails。
我已经完成了,现在堆栈跟踪位于“ExceptionDetails”部分。我还将“ExceptionDetails”重命名为“Exception”。有没有办法删除原始/默认的“例外”字段?请检查这个问题:***.com/questions/69028506/…
@Muhammad Rehan Saeed 你看到我的问题了吗?【参考方案2】:
有一个forum thread discussing this,其中提供了几个解决方案。 Thomas Bolon 创建了一个“异常解构”扩展,您可以找到 in a Gist。
在这种情况下,您只使用以下语法:
logger.Debug(exception, "This is an exception");
不需要将异常添加到格式字符串中。
要确保将异常打印到文本接收器,只需确保 Exception
包含在输出模板中。标准内置的已经有这个,例如:
outputTemplate: "Timestamp [Level] MessageNewLineException";
【讨论】:
感谢您的回答。我会看看 Thomas 的实现。 请注意,下面的答案是指链接 Gist 中原始想法的完整实现,在 github 中可用,并且作为 Nuget 包 Serilog.Exceptions。【参考方案3】:应该完全避免这种情况。 ElasticSearch 和 Serilog 的设计都没有考虑到您将序列化任意对象。记录具有冲突形状的对象将导致 ElasticSearch 中的映射异常。如果您在 NuGet 中使用 ElasticSearch 接收器,则任何导致映射冲突的内容都将丢失。此外,Serilog 不处理循环关系,因此这将导致深度限制器自记录错误。有一个项目试图通过解构成字典并将其传递给 Serilog 来解决此问题,但您仍然会遇到混乱的日志和映射异常。
Serilog:https://nblumhardt.com/2016/02/serilog-tip-dont-serialize-arbitrary-objects/
我发现最好根据您在异常中发现有用的内容来具体记录异常属性。
【讨论】:
我使用带有 Serilog.Exceptions 的 ElasicSearch 5 来记录整个消息,但我使用的是带有 Filebeat 5 的 RollingFile 接收器,而不是 ElasticSearch 接收器。我没有任何映射异常,但我在 Kibana 中有很长的属性列表。我通过记录所有日志中出现的常见属性列表来处理这个问题。关于序列化,Serilog.Exceptions 提供了一种记录异常属性的方法,而无需对常见异常类型使用反射,您可以创建自己的。也就是说,在不支持异常类型的情况下使用反射没有任何问题。 Serilog ElasticSearch 接收器的一个问题是您不会看到映射异常,它只会丢失,因为这个问题:github.com/serilog/serilog-sinks-elasticsearch/issues/25以上是关于Serilog 中的异常解构的主要内容,如果未能解决你的问题,请参考以下文章