.net 最有用的 NLog 配置
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4091606/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Most useful NLog configurations
提问by Pat
What are the best or most useful configurations for logging with NLog? (These can be simple or complex, as long as they're useful.)
使用 NLog 进行日志记录的最佳或最有用的配置是什么?(这些可以是简单的也可以是复杂的,只要它们有用。)
I'm thinking of examples like automatically rolling over log files at a certain size, changing the layout (log message) whether or not there is an exception, escalating the log level once an error has occurred, etc.
我正在考虑一些示例,例如自动滚动特定大小的日志文件、更改布局(日志消息)是否有异常、一旦发生错误就升级日志级别等。
Here are some links:
以下是一些链接:
回答by wageoghe
Some of these fall into the category of general NLog (or logging) tips rather than strictly configuration suggestions.
其中一些属于一般 NLog(或日志记录)提示的类别,而不是严格的配置建议。
Here are some general logging links from here at SO (you might have seen some or all of these already):
以下是 SO 上的一些通用日志记录链接(您可能已经看过其中的一些或全部):
What's the point of a logging facade?
Why do loggers recommend using a logger per class?
Use the common pattern of naming your logger based on the class Logger logger = LogManager.GetCurrentClassLogger(). This gives you a high degree of granularity in your loggers and gives you great flexibility in the configuration of the loggers (control globally, by namespace, by specific logger name, etc).
使用基于类命名记录器的常见模式Logger logger = LogManager.GetCurrentClassLogger()。这为您的记录器提供了高度的粒度,并为您配置记录器提供了极大的灵活性(全局控制、命名空间控制、特定记录器名称等)。
Use non-classname-based loggers where appropriate. Maybe you have one function for which you really want to control the logging separately. Maybe you have some cross-cutting logging concerns (performance logging).
在适当的地方使用非基于类名的记录器。也许您有一个真正想要单独控制日志记录的功能。也许您有一些横切日志问题(性能日志)。
If you don't use classname-based logging, consider naming your loggers in some kind of hierarchical structure (maybe by functional area) so that you can maintain greater flexibility in your configuration. For example, you might have a "database" functional area, an "analysis" FA, and a "ui" FA. Each of these might have sub-areas. So, you might request loggers like this:
如果您不使用基于类名的日志记录,请考虑以某种层次结构(可能按功能区域)命名您的记录器,以便您可以在配置中保持更大的灵活性。例如,您可能有一个“数据库”功能区、一个“分析”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");
And so on. With hierarchical loggers, you can configure logging globally (the "*" or root logger), by FA (Database, Analysis, UI), or by subarea (Database.Connect, etc).
等等。使用分层记录器,您可以全局(“*”或根记录器)、FA(数据库、分析、UI)或子区域(Database.Connect 等)配置日志记录。
Loggers have many configuration options:
记录器有许多配置选项:
<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" />
See the NLog helpfor more info on exactly what each of the options means. Probably the most notable items here are the ability to wildcard logger rules, the concept that multiple logger rules can "execute" for a single logging statement, and that a logger rule can be marked as "final" so subsequent rules will not execute for a given logging statement.
有关每个选项的确切含义的更多信息,请参阅NLog 帮助。这里最值得注意的项目可能是通配符记录器规则的能力,多个记录器规则可以为单个记录语句“执行”的概念,并且记录器规则可以标记为“最终”,因此后续规则不会执行给定的日志记录语句。
Use the GlobalDiagnosticContext, MappedDiagnosticContext, and NestedDiagnosticContext to add additional context to your output.
使用 GlobalDiagnosticContext、MappedDiagnosticContext 和 NestedDiagnosticContext 向输出添加额外的上下文。
Use "variable" in your config file to simplify. For example, you might define variables for your layouts and then reference the variable in the target configuration rather than specify the layout directly.
在配置文件中使用“变量”来简化。例如,您可以为布局定义变量,然后在目标配置中引用该变量,而不是直接指定布局。
<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>
Or, you could create a "custom" set of properties to add to a layout.
或者,您可以创建一组“自定义”属性以添加到布局中。
<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}"/>
Or, you can do stuff like create "day" or "month" layout renderers strictly via configuration:
或者,您可以严格通过配置来执行诸如创建“日”或“月”布局渲染器之类的操作:
<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>
You can also use layout renders to define your filename:
您还可以使用布局渲染来定义您的文件名:
<variable name="day" value="${date:format=dddd}"/>
<targets>
<target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${day}.log" />
</targets>
If you roll your file daily, each file could be named "Monday.log", "Tuesday.log", etc.
如果您每天滚动文件,则每个文件都可以命名为“Monday.log”、“Tuesday.log”等。
Don't be afraid to write your own layout renderer. It is easy and allows you to add your own context information to the log file via configuration. For example, here is a layout renderer (based on NLog 1.x, not 2.0) that can add the Trace.CorrelationManager.ActivityId to the log:
不要害怕编写自己的布局渲染器。它很简单,允许您通过配置将自己的上下文信息添加到日志文件中。例如,这里是一个布局渲染器(基于 NLog 1.x,而不是 2.0),可以将 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;
}
}
Tell NLog where your NLog extensions (what assembly) like this:
像这样告诉 NLog 你的 NLog 扩展在哪里(什么程序集):
<extensions>
<add assembly="MyNLogExtensions"/>
</extensions>
Use the custom layout renderer like this:
像这样使用自定义布局渲染器:
<variable name="fmt" value="${longdate} | ${ActivityId} | ${message}"/>
Use async targets:
使用异步目标:
<nlog>
<targets async="true">
<!-- all targets in this section will automatically be asynchronous -->
</targets>
</nlog>
And default target wrappers:
和默认目标包装器:
<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>
where appropriate. See the NLog docs for more info on those.
在适当情况下。有关这些的更多信息,请参阅 NLog 文档。
Tell NLog to watch and automatically reload the configuration if it changes:
告诉 NLog 监视并在配置发生变化时自动重新加载配置:
<nlog autoReload="true" />
There are several configuration options to help with troubleshooting NLog
有几个配置选项可帮助排除 NLog 故障
<nlog throwExceptions="true" />
<nlog internalLogFile="file.txt" />
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" />
<nlog internalLogToConsole="false|true" />
<nlog internalLogToConsoleError="false|true" />
See NLog Help for more info.
有关更多信息,请参阅 NLog 帮助。
NLog 2.0 adds LayoutRenderer wrappers that allow additional processing to be performed on the output of a layout renderer (such as trimming whitespace, uppercasing, lowercasing, etc).
NLog 2.0 添加了 LayoutRenderer 包装器,允许对布局渲染器的输出执行额外的处理(例如修剪空格、大写、小写等)。
Don't be afraid to wrap the logger if you want insulate your code from a hard dependency on NLog, but wrap correctly. There are examples of how to wrap in the NLog's github repository. Another reason to wrap might be that you want to automatically add specific context information to each logged message (by putting it into LogEventInfo.Context).
如果您想将代码与 NLog 的硬依赖隔离,请不要害怕包装记录器,但要正确包装。在 NLog 的 github 存储库中有如何包装的示例。包装的另一个原因可能是您希望自动向每个记录的消息添加特定的上下文信息(通过将其放入 LogEventInfo.Context)。
There are pros and cons to wrapping (or abstracting) NLog (or any other logging framework for that matter). With a little effort, you can find plenty of info here on SO presenting both sides.
包装(或抽象)NLog(或任何其他与此相关的日志记录框架)有利有弊。只需稍加努力,您就可以在此处找到大量关于 SO 呈现双方的信息。
If you are considering wrapping, consider using Common.Logging. It works pretty well and allows you to easily switch to another logging framework if you desire to do so. Also if you are considering wrapping, think about how you will handle the context objects (GDC, MDC, NDC). Common.Logging does not currently support an abstraction for them, but it is supposedly in the queue of capabilities to add.
如果您正在考虑包装,请考虑使用Common.Logging。它运行良好,如果您愿意,可以轻松切换到另一个日志记录框架。此外,如果您正在考虑包装,请考虑如何处理上下文对象(GDC、MDC、NDC)。Common.Logging 目前不支持对它们的抽象,但据说它在添加功能队列中。
回答by Pat
Treating exceptions differently
以不同的方式处理异常
We often want to get more information when there is an exception. The following configuration has two targets, a file and the console, which filter on whether or not there is any exception info. (EDIT: Jarek has posted about a new method of doing this in vNext.)
我们经常希望在出现异常时获得更多信息。以下配置有两个目标,一个文件和一个控制台,用于过滤是否有任何异常信息。(编辑:Jarek 已经发布了关于在 vNext 中执行此操作的新方法。)
The key is to have a wrapper target with xsi:type="FilteringWrapper" condition="length('${exception}')>0"
关键是要有一个包装目标 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>
回答by ?a?da? Tekin
Apparently, you can now use NLog with Growl for Windows.
显然,您现在可以将NLog 与 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>














回答by wageoghe
Configure NLog via XML, but Programmatically
通过 XML 配置 NLog,但以编程方式
What? Did you know that you can specify the NLog XML directly to NLog from your app, as opposed to having NLog read it from the config file? Well, you can. Let's say that you have a distributed app and you want to use the same configuration everywhere. You could keep a config file in each location and maintain it separately, you could maintain one in a central location and push it out to the satellite locations, or you could probably do a lot of other things. Or, you could store your XML in a database, get it at app startup, and configure NLog directly with that XML (maybe checking back periodically to see if it had changed).
什么?您知道吗,您可以将 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
I'm not sure how robust this is, but this example provides a useful starting point for people that might want to try configuring like this.
我不确定这有多健壮,但这个例子为可能想要尝试这样配置的人提供了一个有用的起点。
回答by wageoghe
I provided a couple of reasonably interesting answers to this question:
我为这个问题提供了几个相当有趣的答案:
Nlog - Generating Header Section for a log file
Adding a Header:
添加标题:
The question wanted to know how to add a header to the log file. Using config entries like this allow you to define the header format separately from the format of the rest of the log entries. Use a single logger, perhaps called "headerlogger" to log a single message at the start of the application and you get your header:
问题想知道如何向日志文件添加标题。使用这样的配置条目允许您将标头格式与其余日志条目的格式分开定义。使用单个记录器(可能称为“headerlogger”)在应用程序开始时记录一条消息,然后您将获得标题:
Define the header and file layouts:
定义标题和文件布局:
<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}" />
Define the targets using the layouts:
使用布局定义目标:
<target name="fileHeader" xsi:type="File" fileName="xxx.log" layout="${HeaderLayout}" />
<target name="file" xsi:type="File" fileName="xxx.log" layout="${InfoLayout}" />
Define the loggers:
定义记录器:
<rules>
<logger name="headerlogger" minlevel="Trace" writeTo="fileHeader" final="true" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
Write the header, probably early in the program:
写标题,可能在程序的早期:
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");
This is largely just another version of the "Treating exceptions differently" idea.
这在很大程度上只是“以不同方式处理异常”想法的另一个版本。
Log each log level with a different layout
使用不同的布局记录每个日志级别
Similarly, the poster wanted to know how to change the format per logging level. It wasn't clear to me what the end goal was (and whether it could be achieved in a "better" way), but I was able to provide a configuration that did what he asked:
同样,发布者想知道如何更改每个日志级别的格式。我不清楚最终目标是什么(以及它是否可以以“更好”的方式实现),但我能够提供一个配置来满足他的要求:
<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>
Again, very similar to Treating exceptions differently.
同样,非常类似于以不同方式处理异常。
回答by Pat
Logging different levels depending on whether or not there is an error
根据是否有错误记录不同的级别
This example allows you to get more information when there is an error in your code. Basically, it buffers messages and only outputs those at a certain log level (e.g. Warn) unlessa certain condition is met (e.g. there has been an error, so the log level is >= Error), then it will output more info (e.g. all messages from log levels >= Trace). Because the messages are buffered, this lets you gather trace information about what happened beforean Error or ErrorException was logged - very useful!
此示例允许您在代码中出现错误时获取更多信息。基本上,它缓冲消息并且只输出特定日志级别(例如警告)的消息,除非满足特定条件(例如出现错误,因此日志级别 >= 错误),然后它将输出更多信息(例如来自日志级别 >= Trace 的所有消息)。因为消息是缓冲的,这使您可以收集有关记录 Error 或 ErrorException之前发生的事情的跟踪信息- 非常有用!
I adapted this one from an example in the source code. I was thrown at first because I left out the AspNetBufferingWrapper(since mine isn't an ASP app) - it turns out that the PostFilteringWrapperrequires some buffered target. Note that the target-refelement used in the above-linked example cannot be used in NLog 1.0 (I am using 1.0 Refresh for a .NET 4.0 app); it is necessary to put your target inside the wrapper block. Also note that the logic syntax (i.e. greater-than or less-than symbols, < and >) has to use the symbols, not the XML escapes for those symbols (i.e. >and <) or else NLog will error.
我从源代码中的一个例子改编了这个。起初我被抛出是因为我遗漏了AspNetBufferingWrapper(因为我的不是 ASP 应用程序) - 结果证明PostFilteringWrapper需要一些缓冲目标。请注意,target-ref上面链接示例中使用的元素不能在 NLog 1.0 中使用(我正在为 .NET 4.0 应用程序使用 1.0 Refresh);有必要将您的目标放在包装器块中。还要注意逻辑语法(即大于或小于符号,< 和 >)必须使用这些符号,而不是这些符号(即>和<)的 XML 转义,否则 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>
回答by wageoghe
Log to Twitter
登录推特
Based on this post about a log4net Twitter Appender, I thought I would try my hand at writing a NLog Twitter Target (using NLog 1.0 refresh, not 2.0). Alas, so far I have not been able to get a Tweet to actually post successfully. I don't know if it is something wrong in my code, Twitter, our company's internet connection/firewall, or what. I am posting the code here in case someone is interested in trying it out. Note that there are three different "Post" methods. The first one that I tried is PostMessageToTwitter. PostMessageToTwitter is essentially the same as PostLoggingEvent in the orignal post. If I use that I get a 401 exception. PostMessageBasic gets the same exception. PostMessage runs with no errors, but the message still does not make it up to Twitter. PostMessage and PostMessageBasic are based on examples that I found here on SO.
基于这篇关于 log4net Twitter Appender 的帖子,我想我会尝试编写一个 NLog Twitter 目标(使用 NLog 1.0 刷新,而不是 2.0)。唉,到目前为止,我还没有能够成功发布推文。我不知道是不是我的代码、Twitter、我们公司的互联网连接/防火墙有问题,还是什么。我在这里发布代码以防有人有兴趣尝试。请注意,有三种不同的“发布”方法。我尝试的第一个是 PostMessageToTwitter。PostMessageToTwitter 本质上与原始帖子中的 PostLoggingEvent 相同。如果我使用它,我会收到 401 异常。PostMessageBasic 得到相同的异常。PostMessage 运行时没有出现错误,但该消息仍然无法发送到 Twitter。PostMessage 和 PostMessageBasic 基于我在 SO 上找到的示例。
FYI- I just now found a comment by @Jason Diller to an answer in this postthat says that twitter is going to turn off basic authentication "next month". This was back in May 2010 and it is now December 2010, so I guess that could be why this is not working.
仅供参考- 我刚刚发现@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) + "...";
}
}
}
Configure it like this:
像这样配置它:
Tell NLog the assembly containing the target:
告诉 NLog 包含目标的程序集:
<extensions>
<add assembly="NLogExtensions"/>
</extensions>
Configure the target:
配置目标:
<targets>
<target name="twitter" type="TwitterTarget" TwitterUserName="yourtwittername" TwitterPassword="yourtwitterpassword" layout="${longdate} ${logger} ${level} ${message}" />
</targets>
If someone tries this out and has success, post back with your findings.
如果有人尝试并取得了成功,请将您的发现发回。
回答by Pat
Reporting to an external website/database
向外部网站/数据库报告
I wanted a way to simply and automatically report errors (since users often don't) from our applications. The easiest solution I could come up with was a public URL - a web page which could take input and store it to a database - that is sent data upon an application error. (The database could then be checked by a dev or a script to know if there are new errors.)
我想要一种方法来从我们的应用程序中简单地自动报告错误(因为用户通常不报告)。我能想到的最简单的解决方案是一个公共 URL - 一个可以接受输入并将其存储到数据库的网页 - 在应用程序错误时发送数据。(然后可以由开发人员或脚本检查数据库以了解是否有新错误。)
I wrote the web page in PHP and created a mysql database, user, and table to store the data. I decided on four user variables, an id, and a timestamp. The possible variables (either included in the URL or as POST data) are:
我用PHP编写了网页并创建了一个mysql数据库、用户和表来存储数据。我决定了四个用户变量、一个 id 和一个时间戳。可能的变量(包含在 URL 中或作为 POST 数据)是:
app(application name)msg(message - e.g. Exception occurred ...)dev(developer - e.g. Pat)src(source - this would come from a variable pertaining to the machine on which the app was running, e.g.Environment.MachineNameor some such)log(a log file or verbose message)
app(应用名称)msg(消息 - 例如发生异常...)dev(开发人员 - 例如 Pat)src(来源 - 这将来自与运行应用程序的机器有关的变量,例如Environment.MachineName或类似的)log(日志文件或详细消息)
(All of the variables are optional, but nothing is reported if none of them are set - so if you just visit the website URL nothing is sent to the db.)
(所有变量都是可选的,但如果没有设置它们,则不会报告任何内容 - 因此,如果您只是访问网站 URL,则不会向数据库发送任何内容。)
To send the data to the URL, I used NLog's WebServicetarget. (Note, I had a few problems with this target at first. It wasn't until I looked at the source that I figured out that my urlcould not end with a /.)
为了将数据发送到 URL,我使用了 NLog 的WebServicetarget。(注意,一开始我对这个目标有一些问题。直到我查看源代码才发现我url不能以/.结尾。)
All in all, it's not a bad system for keeping tabs on external apps. (Of course, the polite thing to do is to inform your usersthat you will be reporting possibly sensitive data and to give them a way to opt in/out.)
总而言之,它是一个不错的系统,可以密切关注外部应用程序。(当然,礼貌的做法是通知您的用户您将报告可能的敏感数据,并为他们提供选择加入/退出的方式。)
MySQL stuff
MySQL的东西
(The db user has only INSERTprivileges on this one table in its own database.)
(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'
Website code
网站代码
(PHP 5.3 or 5.2 with PDOenabled, file is index.phpin /reportfolder)
(启用PDO 的PHP 5.3 或 5.2 ,文件在文件夹中)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 code (NLog config file)
应用程序代码(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>
Note: there may be some issues with the size of the log file, but I haven't figured out a simple way to truncate it (e.g. a la *nix's tailcommand).
注意:日志文件的大小可能存在一些问题,但我还没有想出一种简单的方法来截断它(例如 la *nix's tailcommand)。
回答by Lukie
Easier Way To Log each log level with a different layout using Conditional Layouts
使用条件布局以不同的布局记录每个日志级别的更简单方法
<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}" />
See https://github.com/NLog/NLog/wiki/When-Filterfor syntax
回答by BaBu
Log from Silverlight
从 Silverlight 登录
When using NLog with Silverlight you can send the trace to the server side via the providedweb service. You can also write to a local file in the Isolated Storage, which come in handy if the web server is unavailable. See herefor details, i.e. use something like this to make yourself a target:
在 Silverlight 中使用 NLog 时,您可以通过提供的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);
}
}
}
}
}
}

