从消息和异常中删除换行符。

12

我正在一个Asp dot net core应用程序中使用Serilog。我希望我的日志文件既易于阅读,又易于解析。

我遇到的问题是异常记录时会有换行符。一些Microsoft事件的消息包含换行符。

我希望能够每行解析一个事件的日志文件。

我可以编写自己的ITextFormatter实现,将换行符替换为\r\n,但这意味着我需要复制MessageTemplateTextFormatter和其他类中的大部分逻辑。

4个回答

8

经过一段时间的研究,我终于得到了答案。Andy West的回答给了我正确的方向。

这里有两个不同的问题:消息中的CRLF以及异常中的CRLF。

我通过将输出模板中的"{Message}"更改为"{Message:j}"来解决了消息问题。

更改异常则有点棘手。我不得不添加一个丰富器:

class ExceptionEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        if (logEvent.Exception == null)
            return;

        var logEventProperty = propertyFactory.CreateProperty("EscapedException", logEvent.Exception.ToString().Replace("\r\n", "\\r\\n"));
        logEvent.AddPropertyIfAbsent(logEventProperty);
    }        
}

这里添加了一个名为EscapedException的新属性。需要在配置中使用.Enrich.With()进行添加。

然后我在outputTemplate中用"{EscapedException}"替换了"{Exception}"。


2
消息:j没有移除换行符.. 这个答案应该被删除! - bet
非常好。你可以使用像@Html.Raw(line.Replace(@"\r\n", "<br />"))这样的代码在 Razor 视图中显示行,例如在表格单元格中。 - GerardF
我尝试过这个,在Windows环境下,它运行得非常好。但是当我的应用程序在Docker Linux容器中运行时,它没有使用转义序列。@AndrewRadford - Saqib Shehzad
我已经解决了这个问题,并在下面发布了答案。谢谢。 - Saqib Shehzad
"{Message:j}" 对我也不起作用。在控制台和文件日志中仍保留换行符。 - Alek Davis
显示剩余2条评论

5

这个技术将会移除所有的CRLF。首先需要创建一个新的ITextFormatter。

    public class RemoveCrLf : ITextFormatter
    {
        private const int DefaultWriteBuffer = 256;

        private readonly ITextFormatter _textFormatter;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="textFormatter"></param>
        public RemoveCrLf(ITextFormatter textFormatter)
        {
            _textFormatter = textFormatter;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="logEvent"></param>
        /// <param name="output"></param>
        public void Format(LogEvent logEvent, TextWriter output)
        {
            var buffer = new StringWriter(new StringBuilder(DefaultWriteBuffer));

            _textFormatter.Format(logEvent, buffer);

            var logText = buffer.ToString();

            output.WriteLine(logText.Trim().Replace("\n","\\n").Replace("\r","\\r"));
            output.Flush();
        }
    }

使用方法如下:

configuration.WriteTo.Console(new RemoveCrLf(new MessageTemplateTextFormatter("[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Exception}")));

当然,根据需要自定义输出模板。


3

关于@Andrew Radford的解决方案,该解决方案在Windows上对我有效,但是在Linux Docker环境下并没有按预期工作。因此,我增强了消息和异常的解决方案。

我在正则表达式中使用Environment.NewLine进行大小写匹配,这将根据托管环境选择大小写。

添加以下类用于消息。

public class MessageEnricher : ILogEventEnricher
{
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        if (logEvent.MessageTemplate == null)
            return;

        var logEventProperty = propertyFactory.CreateProperty("EscapedMessage", Regex.Replace(logEvent.MessageTemplate.ToString(), Environment.NewLine, "[Newline]"));
        logEvent.AddPropertyIfAbsent(logEventProperty);
    }
}

使用以下类来处理异常:
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        if (logEvent.Exception == null)
            return;

        var logEventProperty = propertyFactory.CreateProperty("EscapedException", Regex.Replace(logEvent.Exception.ToString(), Environment.NewLine, "[Newline]"));
        logEvent.AddPropertyIfAbsent(logEventProperty);
    }

然后将这两个辅助类添加到program.cs中

           Log.Logger = new LoggerConfiguration()
          .ReadFrom.Configuration(Configuration)
          .Enrich.With(new ExceptionEnricher())
          .Enrich.With(new MessageEnricher())
          .Enrich.FromLogContext()
          .CreateLogger();

更新appsettings输出模板

 "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level:u4} {SourceContext:l} : {EscapedMessage}{NewLine}{EscapedException}{NewLine}"

EscapedMessage解决方案对我无效,因为logEvent.MessageTemplate.ToString()不会替换消息模板占位符。 因此,它打印出类似于“现在正在侦听:https://localhost:7086”而不是“现在正在侦听:{address}”的内容。 - Alek Davis

-2

您没有说明如何记录异常,但是假设您正在使用类似以下的方式:

Log.Error("An error occurred: {Exception}", exception);

那么异常将使用ToString()呈现。在这种情况下,你不可以简单地这样做吗:

Log.Error("An error occurred: {Exception}", exception.ToString().Replace(System.Environment.NewLine, "\\r\\n"));

当然,如果你需要在多个地方执行此操作,你可以将其重构为一个方法(可能是扩展方法)。


1
我希望像这样记录异常:logger.LogError(ex, "调用 {url} 失败", httpContext.Request.Path)。此外,有些事情是我无法控制的,它们会记录一些东西。例如,Microsoft.EntityFrameworkCore 会将异常记录为消息的一部分。理想情况下,我希望调用日志方法的代码不必关心它如何被序列化。 - Andrew Radford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接