32

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 等丰富的接收器)而不将所有异常属性写入呈现的文本消息(所以纯文本记录器不会充满大量异常细节)。

4

3 回答 3

37

看一下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 标准并支持许多常见的异常类型而无需反射,但我们想添加更多,所以请随时贡献。

重要提示 - 人类可读的堆栈跟踪

您可以使用Ben.Demystifier NuGet 包来获取人类可读的异常堆栈跟踪,如果您使用的是 Serilog,则可以使用 serilog -enrichers-demystify NuGet 包。

于 2015-12-07T12:37:52.730 回答
25

有一个讨论这个的论坛帖子,其中提出了几个解决方案。Thomas Bolon 创建了一个“异常解构”扩展,您可以在 Gist中找到。

在这种情况下,您只使用以下语法:

logger.Debug(exception, "This is an exception");

无需将异常添加到格式字符串中。

要确保将异常打印到文本接收器,只需确保{Exception}包含在输出模板中。标准内置的已经有这个,例如:

outputTemplate: "{Timestamp} [{Level}] {Message}{NewLine}{Exception}";
于 2014-08-15T19:45:36.187 回答
3

这应该完全避免。ElasticSearch 和 Serilog 的设计都没有考虑到您将序列化任意对象。记录具有冲突形状的对象将导致 ElasticSearch 中的映射异常。如果您在 NuGet 中使用 ElasticSearch 接收器,则会丢失任何导致映射冲突的内容。Serilog 也不处理循环关系,因此这将导致深度限制器自记录错误。有一个项目试图通过解构成字典并将其传递给 Serilog 来解决这个问题,但您仍然会遇到混乱的日志和映射异常。

Serilog:https ://nblumhardt.com/2016/02/serilog-tip-dont-serialize-arbitrary-objects/

我发现最好根据您在异常中发现有用的内容来具体记录异常属性。

于 2016-07-20T00:44:58.210 回答