nlog 异常布局以格式化异常类型、消息和堆栈跟踪

问题描述 投票:0回答:1
请帮助我格式化使用文件记录器记录的异常 我希望在内部异常(如果有)之间使用新行分隔符记录异常,异常类型用某种特殊类型的符号包围,例如

[ArgumentException]

 以使其与错误文本不同,并且异常要使用选项卡只是为了方便阅读堆栈跟踪

我已经检查了最新的 NLog 4.4.12 包,但问题是参数化默认布局以获得类似的东西并不容易

2017-10-04 15:13:22.5823 NLogTest.Program starting 2017-10-04 15:13:22.5823 NLogTest.Program failed to start NLogTest [ArgumentException] bad try at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 62 at NLogTest.Program.Main(String[] args) in d:\projects\NLogTest\Program.cs:line 19 [ArgumentException] outer exception at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 49 at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 57 [KeyNotFoundException] innerException at NLogTest.Classes.UnitOfWork.innerException() in d:\projects\NLogTest\Program.cs:line 38 at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 45 2017-10-04 15:13:22.5823 NLogTest.Program the end

这是我想要获取的异常文件日志。 我尝试过类似的布局

layout="${longdate} ${logger} ${message}${onexception:${newline}${exception:format=tostring}}"

它使用标准的 tostring 异常方法,但结果不一样。我不喜欢这样

2017-10-04 15:28:52.6881 NLogTest.Program failed to start NLogTest System.ArgumentException: bad try ---> System.ArgumentException: outer exception ---> System.Collections.Generic.KeyNotFoundException: innerException at NLogTest.Classes.UnitOfWork.innerException() in d:\projects\NLogTest\Program.cs:line 40 at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 47 --- End of inner exception stack trace --- at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 51 at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 59 --- End of inner exception stack trace --- at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 64 at NLogTest.Program.Main(String[] args) in d:\projects\NLogTest\Program.cs:line 20

内部异常消息堆栈跟踪在日志中分离(因此以后很难读取日志),以防异常重新抛出两次以上 并且异常类型名称之前没有制表符(空格)。

我得到的最好结果是下面的布局

layout="${longdate} ${logger} ${message}${onexception:${newline}${exception:maxInnerExceptionLevel=10:format=shortType,message,stacktrace:separator=*:innerExceptionSeparator=
	}}"

它是

2017-10-04 15:49:02.6188 NLogTest.Program failed to start NLogTest ArgumentException*bad try* at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 64 at NLogTest.Program.Main(String[] args) in d:\projects\NLogTest\Program.cs:line 20 ArgumentException*outer exception* at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 51 at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 59 KeyNotFoundException*innerException* at NLogTest.Classes.UnitOfWork.innerException() in d:\projects\NLogTest\Program.cs:line 40 at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 47

但是它很难读。第一个堆栈跟踪行与异常消息位于同一行。内部错误消息是有意的,但内部堆栈跟踪 - 没有。

布局中有什么我遗漏的吗? 我应该

创建自定义异常布局渲染器吗? 这里是我用来引发异常的c#源代码

c# exception nlog
1个回答
9
投票
编写 NLog 布局渲染器非常简单,因此我成功创建了一个异常布局渲染器,它完全按照请求格式化异常。

最新的自定义布局源代码版本可以

在gist上找到 控制台和文件记录器的示例配置也可用。还有 NLog.IndentException NuGet 包。请参阅此答案的末尾,以获取链接失效情况下的代码快照。

此外,我还添加了一个堆栈跟踪过滤器。有时异常会被记录多次。可能是因为糟糕的设计(异常应该只记录一次)或者可能是因为可以从外部代码使用相同的方法(因此异常应该在抛出之前在本地记录)或者由同一解决方案中的另一个方法使用。在这种情况下,可以多次记录内部异常。为了减少堆栈跟踪的大小,我添加了一个配置,如果之前记录了异常,则跳过堆栈跟踪日志记录 (

:LogStack=false

)。在这种情况下,仅记录异常类型和消息。如下:

[1] 2017-10-04 16:55:17.2227 Debug NLogTest.Program starting [1] 2017-10-04 16:55:17.2527 Error NLogTest.Classes.UnitOfWork tryException failure [ArgumentException] outer exception at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 48 at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 56 [KeyNotFoundException] innerException at NLogTest.Classes.UnitOfWork.innerException() in d:\projects\NLogTest\Program.cs:line 37 at NLogTest.Classes.UnitOfWork.outerException() in d:\projects\NLogTest\Program.cs:line 44 [1] 2017-10-04 16:55:17.2697 Error NLogTest.Program failed to start NLogTest [ArgumentException] bad try at NLogTest.Classes.UnitOfWork.tryException() in d:\projects\NLogTest\Program.cs:line 61 at NLogTest.Program.Main(String[] args) in d:\projects\NLogTest\Program.cs:line 17 [ArgumentException] outer exception [KeyNotFoundException] innerException
外部异常和内部异常被记录两次。第一次捕获外部异常,第二次将它们记录为内部异常。
但是,由于日志已包含堆栈跟踪,因此仅记录异常类型和消息。


代码

IndentExceptionLayoutRenderer.cs

using NLog; using NLog.LayoutRenderers; using System; using System.Linq; using System.Text; using System.Collections.Concurrent; namespace NLogTest { /// <summary> /// renders exception starting from new line /// with short type exception name followed by message /// and stacktrace (optionally) /// if exception is logged more than once (catched, logged and re-thrown as inner), stack trace is not written /// </summary> [LayoutRenderer("IndentException")] public class IndentExceptionLayoutRenderer : LayoutRenderer { /// <summary> /// indent before exception type (default is tab) /// </summary> public string Indent { get; set; } /// <summary> /// indent between each stack trace line (default is two tab characters) /// </summary> public string StackTraceIndent { get; set; } /// <summary> /// is written before exception type name (default [) /// </summary> public string BeforeType { get; set; } /// <summary> /// is written after exception type name (default ]) /// </summary> public string AfterType { get; set; } /// <summary> /// separator between exception type and message /// </summary> public string Separator { get; set; } /// <summary> /// log stack trace or not (for console logger e.g.) /// </summary> public bool LogStack { get; set; } /// <summary> /// holds logged already exceptions just to skip surplus stack logging /// </summary> static ConcurrentQueue<Exception> _loggedErrors = new ConcurrentQueue<Exception>(); public IndentExceptionLayoutRenderer() { Indent = "\t"; StackTraceIndent = "\t\t"; BeforeType = "["; AfterType = "]"; LogStack = true; Separator = " "; } protected override void Append(StringBuilder builder, LogEventInfo logEvent) { var e = logEvent.Exception; while (e != null) { builder.AppendFormat("{1}{2}{0}{3}{4}", e.GetType().Name, Indent, BeforeType, AfterType, Separator); builder.Append(e.Message); if (LogStack) { var stackTraceWasLogged = _loggedErrors.Contains(e); if (!stackTraceWasLogged) { builder.AppendLine(); _loggedErrors.Enqueue(e); builder.AppendFormat("{0}", e.StackTrace.Replace(" ", StackTraceIndent)); } if (_loggedErrors.Count > 50) { _loggedErrors.TryDequeue(out Exception ex1); _loggedErrors.TryDequeue(out Exception ex2); } } e = e.InnerException; if (e != null) builder.AppendLine(); } } } }
NLog.config

<?xml version="1.0" encoding="utf-8"?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd" autoReload="true" throwExceptions="false" internalLogLevel="Off" internalLogFile="%temp%\nlog-internal.log"> <!-- optional, add some variables https://github.com/nlog/NLog/wiki/Configuration-file#variables --> <variable name="logfilename" value="NLogTest" /> <targets> <target xsi:type="ColoredConsole" name="console" useDefaultRowHighlightingRules="true" detectConsoleAvailable="true" layout="${message}${onexception:${newline}${exception:maxInnerExceptionLevel=10:format=shortType,message}}" /> <target name="file" xsi:type="File" fileName="${basedir}/${logfilename}.log" keepFileOpen="false" layout="${longdate} ${logger} ${message}${onexception:${newline}${exception:maxInnerExceptionLevel=10:format=shortType,message,stacktrace:separator=*:innerExceptionSeparator=&#xD;&#xA;&#x9;}}" /> </targets> <rules> <logger name="*" minlevel="Debug" writeTo="file" /> <logger name="*" minlevel="Trace" writeTo="Console" /> </rules> </nlog>
NLog.exceptionIndent.config

<target xsi:type="ColoredConsole" name="console" useDefaultRowHighlightingRules="true" detectConsoleAvailable="true" layout="${level} ${message}${onexception:${newline}${IndentException:LogStack=false:separator=&#x9;:beforeType=:aftertype=}}" /> <target name="file" xsi:type="File" fileName="${basedir}/${logfilename}.log" keepFileOpen="false" layout="[${threadid}] ${longdate} ${level} ${logger} ${message}${onexception:${newline}${IndentException}}" />
NLogTest.cs

using NLog; using NLogTest.Classes; namespace NLogTest { class Program { static ILogger Log = LogManager.GetCurrentClassLogger(); static void Main(string[] args) { Log.Debug("starting"); try { new UnitOfWork().TryException(); } catch (Exception e) { Log.Error(e, "failed to start NLogTest"); Console.ReadKey(); } Log.Debug("the end"); } } } namespace NLogTest.Classes { class UnitOfWork { static ILogger Log = LogManager.GetCurrentClassLogger(); public void InnerException() { throw new KeyNotFoundException("innerException"); } public void OuterException() { try { InnerException(); } catch (Exception e) { throw new ArgumentException("outer exception", e); } } public void TryException() { try { OuterException(); } catch (Exception e) { Log.Error(e, "tryException failure"); throw new ArgumentException("bad try", e); } } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.