使用NLog进行日志记录的最佳或最有用的配置是什么?(只要有用,这些可以简单也可以复杂。)
我正在考虑一些例子,比如自动滚动一定大小的日志文件,无论是否有异常,都会改变布局(日志消息),一旦发生错误,就会升级日志级别,等等。
以下是一些链接:
NLog演示 源代码中的示例
使用NLog进行日志记录的最佳或最有用的配置是什么?(只要有用,这些可以简单也可以复杂。)
我正在考虑一些例子,比如自动滚动一定大小的日志文件,无论是否有异常,都会改变布局(日志消息),一旦发生错误,就会升级日志级别,等等。
以下是一些链接:
NLog演示 源代码中的示例
当前回答
显然,你现在可以在Windows上使用NLog和Growl了。
<?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">
<extensions>
<add assembly="NLog.Targets.GrowlNotify" />
</extensions>
<targets>
<target name="growl" type="GrowlNotify" password="" host="" port="" />
</targets>
<rules>
<logger name="*" minLevel="Trace" appendTo="growl"/>
</rules>
</nlog>
其他回答
其中一些属于一般NLog(或日志)提示的类别,而不是严格的配置建议。
这里有一些来自SO的一般日志链接(你可能已经看到了其中的一些或全部):
log4net vs. nlog
日志记录最佳实践
日志facade的意义是什么?
为什么记录器建议每个类使用一个记录器?
使用基于类logger logger = LogManager.GetCurrentClassLogger()命名记录器的通用模式。这为您的日志记录器提供了高度的粒度,并为您在日志记录器的配置方面提供了极大的灵活性(全局控制、按名称空间控制、按特定的日志记录器名称控制等)。
在适当的地方使用非基于类名的记录器。也许您有一个函数,需要单独控制日志记录。也许您有一些横切日志记录问题(性能日志记录)。
如果您不使用基于类名的日志记录,请考虑以某种层次结构(可能按功能区域)命名您的日志记录器,这样您就可以在配置中保持更大的灵活性。例如,您可能有一个“数据库”功能区、一个“分析”FA和一个“ui”FA。每一个都有子区域。所以,你可能会像这样请求记录器:
Logger logger = LogManager.GetLogger("Database.Connect");
Logger logger = LogManager.GetLogger("Database.Query");
Logger logger = LogManager.GetLogger("Database.SQL");
Logger logger = LogManager.GetLogger("Analysis.Financial");
Logger logger = LogManager.GetLogger("Analysis.Personnel");
Logger logger = LogManager.GetLogger("Analysis.Inventory");
等等。使用分层记录器,您可以全局(“*”或根记录器)、按FA(数据库、分析、UI)或按子区域(数据库)配置日志记录。连接等等)。
记录器有许多配置选项:
<logger name="Name.Space.Class1" minlevel="Debug" writeTo="f1" />
<logger name="Name.Space.Class1" levels="Debug,Error" writeTo="f1" />
<logger name="Name.Space.*" writeTo="f3,f4" />
<logger name="Name.Space.*" minlevel="Debug" maxlevel="Error" final="true" />
关于每个选项的确切含义,请参阅NLog帮助以获得更多信息。这里最值得注意的项目可能是通配符记录器规则的能力,多个记录器规则可以对单个日志记录语句“执行”的概念,以及记录器规则可以标记为“final”,因此后续规则将不会对给定的日志记录语句执行。
使用GlobalDiagnosticContext、MappedDiagnosticContext和NestedDiagnosticContext向输出中添加额外的上下文。
在配置文件中使用“variable”来简化。例如,您可以为布局定义变量,然后在目标配置中引用变量,而不是直接指定布局。
<variable name="brief" value="${longdate} | ${level} | ${logger} | ${message}"/>
<variable name="verbose" value="${longdate} | ${machinename} | ${processid} | ${processname} | ${level} | ${logger} | ${message}"/>
<targets>
<target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${shortdate}.log" />
<target name="console" xsi:type="ColoredConsole" layout="${brief}" />
</targets>
或者,您可以创建一组“自定义”属性来添加到布局中。
<variable name="mycontext" value="${gdc:item=appname} , ${mdc:item=threadprop}"/>
<variable name="fmt1withcontext" value="${longdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>
<variable name="fmt2withcontext" value="${shortdate} | ${level} | ${logger} | [${mycontext}] |${message}"/>
或者,你可以做的事情,如创建“日”或“月”布局渲染严格通过配置:
<variable name="day" value="${date:format=dddd}"/>
<variable name="month" value="${date:format=MMMM}"/>
<variable name="fmt" value="${longdate} | ${level} | ${logger} | ${day} | ${month} | ${message}"/>
<targets>
<target name="console" xsi:type="ColoredConsole" layout="${fmt}" />
</targets>
你也可以使用布局渲染来定义你的文件名:
<variable name="day" value="${date:format=dddd}"/>
<targets>
<target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${day}.log" />
</targets>
如果你每天都滚动文件,那么每个文件都可以命名为“Monday.log”,“Tuesday.log”等等。
不要害怕编写自己的布局渲染器。这很简单,并且允许您通过配置将自己的上下文信息添加到日志文件中。例如,这是一个布局渲染器(基于NLog 1)。可以将Trace.CorrelationManager.ActivityId添加到日志中:
[LayoutRenderer("ActivityId")]
class ActivityIdLayoutRenderer : LayoutRenderer
{
int estimatedSize = Guid.Empty.ToString().Length;
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(Trace.CorrelationManager.ActivityId);
}
protected override int GetEstimatedBufferSize(LogEventInfo logEvent)
{
return estimatedSize;
}
}
告诉NLog你的NLog扩展在哪里(什么程序集),像这样:
<extensions>
<add assembly="MyNLogExtensions"/>
</extensions>
使用自定义布局渲染器,如下所示:
<variable name="fmt" value="${longdate} | ${ActivityId} | ${message}"/>
使用异步目标:
<nlog>
<targets async="true">
<!-- all targets in this section will automatically be asynchronous -->
</targets>
</nlog>
和默认的目标包装器:
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target name="f1" xsi:type="File" fileName="f1.txt"/>
<target name="f2" xsi:type="File" fileName="f2.txt"/>
</targets>
<targets>
<default-wrapper xsi:type="AsyncWrapper">
<wrapper xsi:type="RetryingWrapper"/>
</default-wrapper>
<target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>
<target name="n2" xsi:type="Network" address="tcp://localhost:4002"/>
<target name="n3" xsi:type="Network" address="tcp://localhost:4003"/>
</targets>
</nlog>
在适当的地方。有关这些的更多信息,请参阅NLog文档。
告诉NLog观察并在配置发生变化时自动重新加载:
<nlog autoReload="true" />
有几个配置选项可以帮助解决NLog故障
<nlog throwExceptions="true" />
<nlog internalLogFile="file.txt" />
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" />
<nlog internalLogToConsole="false|true" />
<nlog internalLogToConsoleError="false|true" />
更多信息请参见NLog帮助。
NLog 2.0增加了LayoutRenderer包装器,允许在布局渲染器的输出上执行额外的处理(如删除空白,大写,小写等)。
如果您希望将代码与NLog的硬依赖隔离开来,请不要害怕包装记录器,但要正确地包装。这里有一些如何在NLog的github存储库中进行包装的例子。包装的另一个原因可能是您希望自动向每个已记录的消息添加特定的上下文信息(通过将其放入LogEventInfo.Context)。
包装(或抽象)NLog(或任何其他日志框架)有利有弊。只要稍加努力,你可以在这里找到大量关于SO呈现双方的信息。
如果您正在考虑包装,请考虑使用Common.Logging。它工作得非常好,并且允许您轻松地切换到另一个日志框架,如果您想这样做的话。另外,如果您正在考虑包装,请考虑如何处理上下文对象(GDC、MDC、NDC)。常见的。日志记录目前不支持对它们的抽象,但它应该在要添加的功能队列中。
区别对待异常
当出现异常时,我们通常希望获得更多信息。下面的配置有两个目标,一个文件和控制台,它们根据是否有任何异常信息进行筛选。(编辑:Jarek已经在vNext中发布了一种新方法。)
关键是要有一个包装器目标xsi:type="FilteringWrapper" condition="length('${exception}')>0"
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.mono2.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="nlog log.log"
>
<variable name="VerboseLayout"
value="${longdate} ${level:upperCase=true} ${message}
(${callsite:includSourcePath=true})" />
<variable name="ExceptionVerboseLayout"
value="${VerboseLayout} (${stacktrace:topFrames=10})
${exception:format=ToString}" />
<targets async="true">
<target name="file" xsi:type="File" fileName="log.log"
layout="${VerboseLayout}">
</target>
<target name="fileAsException"
xsi:type="FilteringWrapper"
condition="length('${exception}')>0">
<target xsi:type="File"
fileName="log.log"
layout="${ExceptionVerboseLayout}" />
</target>
<target xsi:type="ColoredConsole"
name="console"
layout="${NormalLayout}"/>
<target xsi:type="FilteringWrapper"
condition="length('${exception}')>0"
name="consoleException">
<target xsi:type="ColoredConsole"
layout="${ExceptionVerboseLayout}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="console,consoleException" />
<logger name="*" minlevel="Warn" writeTo="file,fileAsException" />
</rules>
</nlog>
显然,你现在可以在Windows上使用NLog和Growl了。
<?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">
<extensions>
<add assembly="NLog.Targets.GrowlNotify" />
</extensions>
<targets>
<target name="growl" type="GrowlNotify" password="" host="" port="" />
</targets>
<rules>
<logger name="*" minLevel="Trace" appendTo="growl"/>
</rules>
</nlog>
来自Silverlight的日志
当使用NLog和Silverlight一起使用时,你可以通过提供的web服务将跟踪发送到服务器端。你也可以写入隔离存储中的本地文件,如果web服务器不可用,这就派上用场了。请看这里的细节,也就是说,用一些类似的东西来让自己成为一个目标:
namespace NLogTargets
{
[Target("IsolatedStorageTarget")]
public sealed class IsolatedStorageTarget : TargetWithLayout
{
IsolatedStorageFile _storageFile = null;
string _fileName = "Nlog.log"; // Default. Configurable through the 'filename' attribute in nlog.config
public IsolatedStorageTarget()
{
}
~IsolatedStorageTarget()
{
if (_storageFile != null)
{
_storageFile.Dispose();
_storageFile = null;
}
}
public string filename
{
set
{
_fileName = value;
}
get
{
return _fileName;
}
}
protected override void Write(LogEventInfo logEvent)
{
try
{
writeToIsolatedStorage(this.Layout.Render(logEvent));
}
catch (Exception e)
{
// Not much to do about his....
}
}
public void writeToIsolatedStorage(string msg)
{
if (_storageFile == null)
_storageFile = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFile isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
// The isolated storage is limited in size. So, when approaching the limit
// simply purge the log file. (Yeah yeah, the file should be circular, I know...)
if (_storageFile.AvailableFreeSpace < msg.Length * 100)
{
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Truncate, FileAccess.Write, isolatedStorage))
{ }
}
// Write to isolated storage
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_fileName, FileMode.Append, FileAccess.Write, isolatedStorage))
{
using (TextWriter writer = new StreamWriter(stream))
{
writer.WriteLine(msg);
}
}
}
}
}
}
使用条件布局用不同的布局记录每个日志级别的更简单方法
<variable name="VerboseLayout" value="${level:uppercase=true}: ${longdate} | ${logger} :
${when:when=level == LogLevel.Trace:inner=MONITOR_TRACE ${message}}
${when:when=level == LogLevel.Debug:inner=MONITOR_DEBUG ${message}}
${when:when=level == LogLevel.Info:inner=MONITOR_INFO ${message}}
${when:when=level == LogLevel.Warn:inner=MONITOR_WARN ${message}}
${when:when=level == LogLevel.Error:inner=MONITOR_ERROR ${message}}
${when:when=level == LogLevel.Fatal:inner=MONITOR_CRITICAL ${message}} |
${exception:format=tostring} | ${newline} ${newline}" />
参见https://github.com/NLog/NLog/wiki/When-Filter查看语法