首页 文章

最有用的NLog配置[关闭]

提问于
浏览
330

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

我正在考虑一些示例,例如自动滚动一定大小的日志文件,更改布局(日志消息)是否存在异常,一旦发生错误就升级日志级别等 .

以下是一些链接:

10 回答

  • 22

    其中一些属于一般NLog(或日志记录)提示的类别,而不是严格的配置建议 .

    以下是SO的一些常规日志记录链接(您可能已经看过其中的部分或全部内容):

    log4net vs. Nlog

    Logging best practices

    What's the point of a logging facade?

    Why do loggers recommend using a logger per class?

    使用基于类 Logger logger = LogManager.GetCurrentClassLogger() 命名 Logger 的通用模式 . 这使您在 Logger 中具有高度的粒度,并为 Logger 的配置提供了极大的灵活性(全局控制,命名空间,特定 Logger 名称等) .

    在适当的地方使用非基于类名的 Logger . 也许您有一个功能,您真正想要分别控制日志记录 . 也许你有一些跨领域的日志问题(性能日志记录) .

    如果不使用基于类名的日志记录,请考虑以某种层次结构(可能是功能区域)命名 Logger ,以便您可以在配置中保持更大的灵活性 . 例如,您可能具有“数据库”功能区域,“分析”FA和“ui”FA . 每个都可能有子区域 . 所以,您可以请求这样的 Logger :

    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");
    

    等等 . 使用分层 Logger ,您可以通过FA(数据库,分析,UI)或子区域(Database.Connect等)全局配置日志记录(“*”或根 Logger ) .

    Logger 有许多配置选项:

    <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 help . 这里最值得注意的项目可能是通配符 Logger 规则的能力,多个 Logger 规则对单个日志记录语句的概念,以及 Logger 规则可以标记为"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.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;
        }
      }
    

    告诉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的硬依赖性隔离,并且正确包装,请不要害怕包装 Logger . 有一些如何包装NLog的github存储库的例子 . 换行的另一个原因可能是您希望自动为每条记录的消息添加特定的上下文信息(通过将其放入LogEventInfo.Context) .

    包装(或抽象)NLog(或任何其他日志框架)的优点和缺点 . 只需稍加努力,您就可以在这里找到关于SO双方的大量信息 .

    如果您正在考虑包装,请考虑使用Common.Logging . 它运行良好,并允许您轻松切换到另一个日志框架,如果您希望这样做 . 此外,如果您正在考虑包装,请考虑如何处理上下文对象(GDC,MDC,NDC) . Common.Logging目前不支持对它们进行抽象,但它应该在要添加的功能队列中 .

  • 10

    以不同方式处理异常

    我们经常希望在出现异常时获取更多信息 . 以下配置有两个目标,一个文件和一个控制台,它们会过滤是否有任何目标异常信息 . (编辑:Jarek发布了一个new method of doing this in 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>
    
  • 7

    显然,你现在可以使用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

  • 3

    Configure NLog via XML, but Programmatically

    什么?您是否知道可以从应用程序直接向NLog指定NLog XML,而不是让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
    

    我不确定这是多么强大,但是这个示例为可能想要尝试这样配置的人提供了一个有用的起点 .

  • 22

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

    此示例允许您在代码中出现错误时获取更多信息 . 基本上,它缓冲消息并仅输出特定日志级别的消息(例如警告) unless 满足某个条件(例如,出现错误,因此日志级别> =错误),然后它将输出更多信息(例如来自日志级别的所有消息> =跟踪) . 因为消息是缓冲的,所以这可以让你收集有关发生了什么的跟踪信息 before 记录了错误或ErrorException - 非常有用!

    我从an example in the source code改编了这个 . 我最初被抛出是因为我遗漏了 AspNetBufferingWrapper (因为我的不是ASP应用程序) - 事实证明PostFilteringWrapper需要一些缓冲目标 . 请注意,上面链接的示例中使用的 target-ref 元素不能在NLog 1.0中使用(我在.NET 4.0应用程序中使用1.0 Refresh);有必要将目标放在包装块中 . 还要注意逻辑语法(即大于或小于符号,<和>)必须使用符号,而不是那些符号的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>
    
  • 28

    我为这个问题提供了几个相当有趣的答案:

    Nlog - Generating Header Section for a log file

    Adding a Header:

    这个问题想知道如何在日志文件中添加 Headers . 使用这样的配置条目允许您与其余日志条目的格式分开定义标头格式 . 使用单个 Logger (可能称为“headerlogger”)在应用程序开始时记录单个消息,并获得 Headers :

    定义 Headers 和文件布局:

    <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}" />
    

    定义 Logger :

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

    可能在程序的早期写下 Headers :

    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");
    

    这主要是“不同处理异常”理念的另一个版本 .

    Log each log level with a different layout

    同样,海报想知道如何更改每个日志记录级别的格式 . 我不清楚最终目标是什么(以及是否可以以“更好”的方式实现),但我能够提供一个完成他所要求的配置:

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

    再次,非常类似于 Treating exceptions differently .

  • 376

    Log to Twitter

    基于this post about a log4net Twitter Appender,我想我会尝试编写NLog Twitter目标(使用NLog 1.0刷新,而不是2.0) . 唉,到目前为止,我还没有能够成功发布推文 . 我没有't know if it is something wrong in my code, Twitter, our company'的互联网连接/防火墙,或者什么 . 我在这里发布代码,以防有人有兴趣尝试它 . 请注意,有三种不同的"Post"方法 . 我尝试的第一个是PostMessageToTwitter . PostMessageToTwitter与orignal帖子中的PostLoggingEvent基本相同 . 如果我使用它,我会得到401异常 . PostMessageBasic获得相同的异常 . PostMessage在没有错误的情况下运行,但该消息仍然无法在Twitter上运行 . PostMessage和PostMessageBasic基于我在SO上找到的示例 .

    FYI - 我刚刚发现@Jason Diller对this post中的答案发表评论说,Twitter将关闭基本身份验证"next month" . 这是在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>
    

    如果有人尝试这一点并取得成功,请回复您的发现 .

  • 62

    向外部网站/数据库报告

    我想从我们的应用程序中简单地自动报告错误(因为用户通常不会报告) . 我能想到的最简单的解决方案是一个公共URL - 一个可以接收输入并将其存储到数据库的网页 - 在发生应用程序错误时发送数据 . (然后可以通过开发人员或脚本检查数据库,以了解是否存在新错误 . )

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

    • app (申请名称)

    • msg (消息 - 例如异常发生......)

    • dev (开发者 - 例如Pat)

    • src (来源 - 这将来自与运行应用程序的计算机有关的变量,例如 Environment.MachineName 或某些此类)

    • log (日志文件或详细消息)

    (所有变量都是可选的,但如果没有设置任何变量,则不报告任何变量 - 因此,如果您只访问网站URL,则不会向数据库发送任何内容 . )

    为了将数据发送到URL,我使用了NLog的WebService target . (注意,我最初对这个目标有一些问题 . 直到我查看源代码,我才发现我的 url 无法以 / 结束 . )

    总而言之,它对于密切关注外部应用程序并不是一个糟糕的系统 . (当然,礼貌的事情是 inform your users 你将报告可能的敏感数据,并给他们一种选择加入/退出的方法 . )

    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 enabled,文件是 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() . "
    "; die("PDO error"); } function isEmpty($array = array()) { foreach ($array as $element) { if (!empty($element)) { return false; } } return true; } ?>

    应用代码(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>
    

    注意:日志文件的大小可能存在一些问题,但我没有't figured out a simple way to truncate it (e.g. a la *nix' s tail command) .

  • 59

    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}" />
    

    有关语法,请参见https://github.com/NLog/NLog/wiki/When-Filter

  • 4

    Log from Silverlight

    将NLog与Silverlight一起使用时,可以通过provided Web服务将跟踪发送到服务器端 . 您还可以写入隔离存储中的本地文件,如果Web服务器不可用,它将派上用场 . 有关详细信息,请参阅here,即使用类似的内容将自己定位为目标:

    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);
                        }
                    }
                }
            }
        } 
    }
    

相关问题