[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#源代码
最新的自定义布局源代码版本可以
在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
外部异常和内部异常被记录两次。第一次捕获外部异常,第二次将它们记录为内部异常。
但是,由于日志已包含堆栈跟踪,因此仅记录异常类型和消息。
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=
	}}" />
</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=	: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);
}
}
}
}