最有用的NLog配置

使用NLog进行日志记录的最佳或最有用的配置是什么?(只要有用,这些可以简单也可以复杂。)

我正在考虑一些例子,比如自动滚动一定大小的日志文件,无论是否有异常,都会改变布局(日志消息),一旦发生错误,就会升级日志级别,等等。

以下是一些链接:

251197 次浏览

其中一些属于一般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呈现双方的信息。

如果你正在考虑包装,可以考虑使用常见的。日志记录。它工作得非常好,并且允许您轻松地切换到另一个日志框架,如果您想这样做的话。另外,如果您正在考虑包装,请考虑如何处理上下文对象(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>

根据是否有错误记录不同的级别

此示例允许您在代码中出现错误时获取更多信息。基本上,它缓冲消息,只输出那些在一定的日志级别(例如警告)除非满足一定的条件(例如已经有一个错误,所以日志级别是>=错误),然后它将输出更多的信息(例如所有消息从日志级别>= Trace)。因为消息是缓冲的,这让你收集关于发生了什么的跟踪信息之前一个错误或errrorexception被记录-非常有用!

我从示例在源代码中改编了这个。我一开始被扔了,因为我遗漏了AspNetBufferingWrapper(因为我的不是一个ASP应用程序)-事实证明,PostFilteringWrapper需要一些缓冲目标。注意,在上面链接的例子中使用的target-ref元素不能在NLog 1.0中使用(我使用1.0刷新用于.NET 4.0应用程序);有必要将目标放在包装器块中。还要注意逻辑语法(即大于或小于符号,<和>)必须使用这些符号,而不是这些符号的XML转义(即&gt;&lt;),否则NLog将出错。

app.config:

<?xml version="1.0"?>
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>


<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
<variable name="appTitle" value="My app"/>
<variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>


<targets async="true">
<!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
<wrapper-target xsi:type="BufferingWrapper" name="smartLog">
<wrapper-target xsi:type="PostFilteringWrapper">
<!--<target-ref name="fileAsCsv"/>-->
<target xsi:type="File" fileName="${csvPath}"
archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
>
<layout xsi:type="CsvLayout" delimiter="Tab" withHeader="false">
<column name="time" layout="${longdate}" />
<column name="level" layout="${level:upperCase=true}"/>
<column name="message" layout="${message}" />
<column name="callsite" layout="${callsite:includeSourcePath=true}" />
<column name="stacktrace" layout="${stacktrace:topFrames=10}" />
<column name="exception" layout="${exception:format=ToString}"/>
<!--<column name="logger" layout="${logger}"/>-->
</layout>
</target>


<!--during normal execution only log certain messages-->
<defaultFilter>level >= LogLevel.Warn</defaultFilter>


<!--if there is at least one error, log everything from trace level-->
<when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
</wrapper-target>
</wrapper-target>


</targets>


<rules>
<logger name="*" minlevel="Trace" writeTo="smartLog"/>
</rules>
</nlog>
</configuration>

向外部网站/数据库报告

我想要一种简单而自动地报告应用程序错误的方法(因为用户通常不会这样做)。我能想到的最简单的解决方案是一个公共URL——一个可以接受输入并将其存储到数据库的网页——在应用程序出现错误时发送数据。(然后,开发人员或脚本可以检查数据库,以了解是否有新的错误。)

我用PHP编写了网页,并创建了mysql数据库、用户和表来存储数据。我决定使用四个用户变量、一个id和一个时间戳。可能的变量(包括在URL中或作为POST数据)是:

  • app(应用程序名称)
  • msg (message -例如Exception occurred…)
  • dev(开发人员-例如Pat)
  • src(源-这将来自与应用程序正在运行的机器相关的变量,例如Environment.MachineName或其他类似的变量)
  • log(一个日志文件或详细消息)

(所有的变量都是可选的,但是如果它们都没有设置,什么都不会报告-所以如果你只是访问网站URL,什么都不会发送到db。)

为了将数据发送到URL,我使用了NLog的WebService目标。(注意,一开始我在这个目标上遇到了一些问题。直到我查看源代码,我才发现我的url不能以/结尾。)

总而言之,对于监控外部应用程序来说,这是个不错的系统。(当然,礼貌的做法是通知用户,你将报告可能敏感的数据,并给他们一个选择加入/退出的方法。)

MySQL的东西

(db用户在自己的数据库中对这个表只有INSERT权限。)

CREATE TABLE `reports` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ts` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`applicationName` text,
`message` text,
`developer` text,
`source` text,
`logData` longtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='storage place for reports from external applications'

网站代码

(PHP 5.3或5.2带有PDO 启用,文件是index.php/report文件夹中)

<?php
$app = $_REQUEST['app'];
$msg = $_REQUEST['msg'];
$dev = $_REQUEST['dev'];
$src = $_REQUEST['src'];
$log = $_REQUEST['log'];


$dbData =
array(  ':app' => $app,
':msg' => $msg,
':dev' => $dev,
':src' => $src,
':log' => $log
);
//print_r($dbData); // For debugging only! This could allow XSS attacks.
if(isEmpty($dbData)) die("No data provided");


try {
$db = new PDO("mysql:host=$host;dbname=reporting", "reporter", $pass, array(
PDO::ATTR_PERSISTENT => true
));
$s = $db->prepare("INSERT INTO reporting.reports
(
applicationName,
message,
developer,
source,
logData
)
VALUES
(
:app,
:msg,
:dev,
:src,
:log
);"
);
$s->execute($dbData);
print "Added report to database";
} catch (PDOException $e) {
// Sensitive information can be displayed if this exception isn't handled
//print "Error!: " . $e->getMessage() . "<br/>";
die("PDO error");
}


function isEmpty($array = array()) {
foreach ($array as $element) {
if (!empty($element)) {
return false;
}
}
return true;
}
?>

App代码(NLog配置文件)

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
throwExceptions="true" internalLogToConsole="true" internalLogLevel="Warn" internalLogFile="nlog.log">
<variable name="appTitle" value="My External App"/>
<variable name="csvPath" value="${specialfolder:folder=Desktop:file=${appTitle} log.csv}"/>
<variable name="developer" value="Pat"/>


<targets async="true">
<!--The following will keep the default number of log messages in a buffer and write out certain levels if there is an error and other levels if there is not. Messages that appeared before the error (in code) will be included, since they are buffered.-->
<wrapper-target xsi:type="BufferingWrapper" name="smartLog">
<wrapper-target xsi:type="PostFilteringWrapper">
<target xsi:type="File" fileName="${csvPath}"
archiveAboveSize="4194304" concurrentWrites="false" maxArchiveFiles="1" archiveNumbering="Sequence"
>
<layout xsi:type="CsvLayout" delimiter="Comma" withHeader="false">
<column name="time" layout="${longdate}" />
<column name="level" layout="${level:upperCase=true}"/>
<column name="message" layout="${message}" />
<column name="callsite" layout="${callsite:includeSourcePath=true}" />
<column name="stacktrace" layout="${stacktrace:topFrames=10}" />
<column name="exception" layout="${exception:format=ToString}"/>
<!--<column name="logger" layout="${logger}"/>-->
</layout>
</target>


<!--during normal execution only log certain messages-->
<defaultFilter>level >= LogLevel.Warn</defaultFilter>


<!--if there is at least one error, log everything from trace level-->
<when exists="level >= LogLevel.Error" filter="level >= LogLevel.Trace" />
</wrapper-target>
</wrapper-target>


<target xsi:type="WebService" name="web"
url="http://example.com/report"
methodName=""
namespace=""
protocol="HttpPost"
>
<parameter name="app" layout="${appTitle}"/>
<parameter name="msg" layout="${message}"/>
<parameter name="dev" layout="${developer}"/>
<parameter name="src" layout="${environment:variable=UserName} (${windows-identity}) on ${machinename} running os ${environment:variable=OSVersion} with CLR v${environment:variable=Version}"/>
<parameter name="log" layout="${file-contents:fileName=${csvPath}}"/>
</target>


</targets>


<rules>
<logger name="*" minlevel="Trace" writeTo="smartLog"/>
<logger name="*" minlevel="Error" writeTo="web"/>
</rules>
</nlog>

注意:日志文件的大小可能会有一些问题,但我还没有想出一个简单的方法来截断它(例如la *nix的tail命令)。

对于这个问题,我给出了几个相当有趣的答案:

Nlog -日志文件的生成头

添加标题:

问题想知道如何向日志文件添加头文件。使用这样的配置条目允许您将头部格式与其余日志条目的格式分开定义。在应用程序开始时使用一个单独的日志记录器,可能称为“headerlogger”来记录单个消息,你就会得到你的头:

定义头文件和文件布局:

  <variable name="HeaderLayout" value="This is the header.  Start time = ${longdate} Machine = ${machinename} Product version = ${gdc:item=version}"/>
<variable name="FileLayout" value="${longdate} | ${logger} | ${level} | ${message}" />

使用布局定义目标:

<target name="fileHeader" xsi:type="File" fileName="xxx.log" layout="${HeaderLayout}" />
<target name="file" xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />

定义记录器:

<rules>
<logger name="headerlogger" minlevel="Trace" writeTo="fileHeader" final="true" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>

写头文件,可能在程序的早期:

  GlobalDiagnosticsContext.Set("version", "01.00.00.25");


LogManager.GetLogger("headerlogger").Info("It doesn't matter what this is because the header format does not include the message, although it could");

这在很大程度上只是“区别对待异常”思想的另一个版本。

用不同的布局记录每个日志级别

类似地,发布者想知道如何在每个日志级别更改格式。我不清楚最终目标是什么(以及是否可以以一种“更好”的方式实现),但我能够提供一个符合他要求的配置:

  <variable name="TraceLayout" value="This is a TRACE - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="DebugLayout" value="This is a DEBUG - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="InfoLayout" value="This is an INFO - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="WarnLayout" value="This is a WARN - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="ErrorLayout" value="This is an ERROR - ${longdate} | ${logger} | ${level} | ${message}"/>
<variable name="FatalLayout" value="This is a FATAL - ${longdate} | ${logger} | ${level} | ${message}"/>
<targets>
<target name="fileAsTrace" xsi:type="FilteringWrapper" condition="level==LogLevel.Trace">
<target xsi:type="File" fileName="xxx.log" layout="${TraceLayout}" />
</target>
<target name="fileAsDebug" xsi:type="FilteringWrapper" condition="level==LogLevel.Debug">
<target xsi:type="File" fileName="xxx.log" layout="${DebugLayout}" />
</target>
<target name="fileAsInfo" xsi:type="FilteringWrapper" condition="level==LogLevel.Info">
<target xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />
</target>
<target name="fileAsWarn" xsi:type="FilteringWrapper" condition="level==LogLevel.Warn">
<target xsi:type="File" fileName="xxx.log" layout="${WarnLayout}" />
</target>
<target name="fileAsError" xsi:type="FilteringWrapper" condition="level==LogLevel.Error">
<target xsi:type="File" fileName="xxx.log" layout="${ErrorLayout}" />
</target>
<target name="fileAsFatal" xsi:type="FilteringWrapper" condition="level==LogLevel.Fatal">
<target xsi:type="File" fileName="xxx.log" layout="${FatalLayout}" />
</target>
</targets>




<rules>
<logger name="*" minlevel="Trace" writeTo="fileAsTrace,fileAsDebug,fileAsInfo,fileAsWarn,fileAsError,fileAsFatal" />
<logger name="*" minlevel="Info" writeTo="dbg" />
</rules>

同样,与区别对待异常非常相似。

通过XML配置NLog,但以编程方式配置

什么?你知道你可以从你的应用程序直接指定NLog XML到NLog,而不是让NLog从配置文件中读取它吗?嗯,你可以。假设你有一个分布式应用程序,你想在所有地方使用相同的配置。您可以在每个位置保存一个配置文件并分别维护它,您可以在中心位置维护一个配置文件并将其推到卫星位置,或者您可能还可以做许多其他事情。或者,您可以将XML存储在数据库中,在应用程序启动时获取它,并直接使用该XML配置NLog(可能会定期检查它是否已更改)。

  string xml = @"<nlog>
<targets>
<target name='console' type='Console' layout='${message}' />
</targets>


<rules>
<logger name='*' minlevel='Error' writeTo='console' />
</rules>
</nlog>";


StringReader sr = new StringReader(xml);
XmlReader xr = XmlReader.Create(sr);
XmlLoggingConfiguration config = new XmlLoggingConfiguration(xr, null);
LogManager.Configuration = config;
//NLog is now configured just as if the XML above had been in NLog.config or app.config


logger.Trace("Hello - Trace"); //Won't log
logger.Debug("Hello - Debug"); //Won't log
logger.Info("Hello - Info");   //Won't log
logger.Warn("Hello - Warn");   //Won't log
logger.Error("Hello - Error"); //Will log
logger.Fatal("Hello - Fatal"); //Will log


//Now let's change the config (the root logging level) ...
string xml2 = @"<nlog>
<targets>
<target name='console' type='Console' layout='${message}' />
</targets>


<rules>
<logger name='*' minlevel='Trace' writeTo='console' />
</rules>
</nlog>";


StringReader sr2 = new StringReader(xml2);
XmlReader xr2 = XmlReader.Create(sr2);
XmlLoggingConfiguration config2 = new XmlLoggingConfiguration(xr2, null);
LogManager.Configuration = config2;


logger.Trace("Hello - Trace"); //Will log
logger.Debug("Hello - Debug"); //Will log
logger.Info("Hello - Info");   //Will log
logger.Warn("Hello - Warn");   //Will log
logger.Error("Hello - Error"); //Will log
logger.Fatal("Hello - Fatal"); //Will log

我不确定这有多健壮,但是这个例子为那些想尝试这样配置的人提供了一个有用的起点。

登录Twitter

基于这篇关于log4net Twitter Appender的文章,我想我会尝试写一个NLog Twitter目标(使用NLog 1.0刷新,而不是2.0)。唉,到目前为止,我还没有能够获得一个推文实际上成功发布。我不知道是我的代码、Twitter、我们公司的网络连接/防火墙出了问题,还是其他什么问题。我把代码贴在这里,以防有人有兴趣尝试一下。注意,有三种不同的“Post”方法。我尝试的第一个是PostMessageToTwitter。PostMessageToTwitter本质上与原始帖子中的PostLoggingEvent相同。如果我用它,我就有401例外。PostMessageBasic得到相同的异常。PostMessage运行时没有任何错误,但是消息仍然没有发送到Twitter。PostMessage和PostMessageBasic是基于我在SO上找到的例子。

仅供参考 -我刚刚发现@Jason Diller对这篇文章中的一个答案的评论,说twitter将在下个月关闭基本身份验证。这是在2010年5月,现在是2010年12月,所以我想这可能就是为什么它不起作用的原因。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.IO;


using NLog;
using NLog.Targets;
using NLog.Config;


namespace NLogExtensions
{
[Target("TwitterTarget")]
public class TwitterTarget : TargetWithLayout
{
private const string REQUEST_CONTENT_TYPE = "application/x-www-form-urlencoded";


private const string REQUEST_METHOD = "POST";


// The source attribute has been removed from the Twitter API,
// unless you're using OAuth.
// Even if you are using OAuth, there's still an approval process.
// Not worth it; "API" will work for now!
// private const string TWITTER_SOURCE_NAME = "Log4Net";
private const string TWITTER_UPDATE_URL_FORMAT = "http://twitter.com/statuses/update.xml?status={0}";


[RequiredParameter]
public string TwitterUserName { get; set; }


[RequiredParameter]
public string TwitterPassword { get; set; }


protected override void Write(LogEventInfo logEvent)
{
if (string.IsNullOrWhiteSpace(TwitterUserName) || string.IsNullOrWhiteSpace(TwitterPassword)) return;


string msg = this.CompiledLayout.GetFormattedMessage(logEvent);


if (string.IsNullOrWhiteSpace(msg)) return;


try
{
//PostMessageToTwitter(msg);
PostMessageBasic(msg);
}
catch (Exception ex)
{
//Should probably do something here ...
}
}


private void PostMessageBasic(string msg)
{
// Create a webclient with the twitter account credentials, which will be used to set the HTTP header for basic authentication
WebClient client = new WebClient { Credentials = new NetworkCredential { UserName = TwitterUserName, Password = TwitterPassword } };


// Don't wait to receive a 100 Continue HTTP response from the server before sending out the message body
ServicePointManager.Expect100Continue = false;


// Construct the message body
byte[] messageBody = Encoding.ASCII.GetBytes("status=" + msg);


// Send the HTTP headers and message body (a.k.a. Post the data)
client.UploadData(@"http://twitter.com/statuses/update.xml", messageBody);
}


private void PostMessage(string msg)
{
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(TwitterUserName + ":" + TwitterPassword));
byte [] bytes = System.Text.Encoding.UTF8.GetBytes("status=" + msg.ToTweet());
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://twitter.com/statuses/update.xml");
request.Method = "POST";
request.ServicePoint.Expect100Continue = false;
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = bytes.Length;
Stream reqStream = request.GetRequestStream();
reqStream.Write(bytes, 0, bytes.Length);
reqStream.Close();
}


private void PostMessageToTwitter(string msg)
{
var updateRequest = HttpWebRequest.Create(string.Format(TWITTER_UPDATE_URL_FORMAT,
HttpUtility.UrlEncode(msg.ToTweet()))) as HttpWebRequest;
updateRequest.ContentLength = 0;
updateRequest.ContentType = REQUEST_CONTENT_TYPE;
updateRequest.Credentials = new NetworkCredential(TwitterUserName, TwitterPassword);
updateRequest.Method = REQUEST_METHOD;


updateRequest.ServicePoint.Expect100Continue = false;


var updateResponse = updateRequest.GetResponse() as HttpWebResponse;


if (updateResponse.StatusCode != HttpStatusCode.OK && updateResponse.StatusCode != HttpStatusCode.Continue)
{
throw new Exception(string.Format("An error occurred while invoking the Twitter REST API [Response Code: {0}]", updateResponse.StatusCode));
}
}
}


public static class Extensions
{
public static string ToTweet(this string s)
{
if (string.IsNullOrEmpty(s) || s.Length < 140)
{
return s;
}


return s.Substring(0, 137) + "...";
}
}
}

这样配置:

告诉NLog包含目标的程序集:

<extensions>
<add assembly="NLogExtensions"/>
</extensions>

配置目标:

<targets>
<target name="twitter" type="TwitterTarget" TwitterUserName="yourtwittername" TwitterPassword="yourtwitterpassword" layout="${longdate} ${logger} ${level} ${message}" />
</targets>

如果有人尝试了这个方法并取得了成功,请将你的发现反馈给我们。

显然,你现在可以使用NLog with Growl for Windows

<?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 with Growl for Windows NLog trace message with Growl for Windows NLog debug message with Growl for Windows NLog info message with Growl for Windows NLog warn message with Growl for Windows NLog error message with Growl for Windows NLog fatal message with Growl for Windows

.

来自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