[.net] 가장 유용한 NLog 구성 [닫기]

NLog로 로깅하는 데 가장 적합하거나 유용한 구성은 무엇입니까? (유용한 한 간단하거나 복잡 할 수 있습니다.)

특정 크기의 로그 파일을 자동으로 롤오버하거나 예외가 있는지 여부에 따라 레이아웃 (로그 메시지)을 변경하거나 오류가 발생하면 로그 수준을 올리는 등의 예제를 생각하고 있습니다.

다음은 몇 가지 링크입니다.



답변

이 중 일부는 구성 제안을 엄격하게 제시하기보다는 일반적인 NLog (또는 로깅) 팁 범주에 속합니다.

여기 SO의 일반적인 로깅 링크가 있습니다 (이들 중 일부 또는 전부를 이미 보았을 것입니다).

log4net 대 Nlog

모범 사례 로깅

벌목 정면의 요점은 무엇입니까?

로거는 왜 수업마다 로거를 사용하도록 권장합니까?

클래스를 기반으로 로거의 이름을 지정하는 일반적인 패턴을 사용하십시오 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) 또는 하위 영역 (Database.Connect 등)별로 로깅을 구성 할 수 있습니다.

로거에는 많은 구성 옵션이 있습니다.

<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 도움말 을 참조하십시오 . 아마도 가장 주목할만한 항목은 로거 규칙 와일드 카드 기능, 단일 로거 명령문에 대해 여러 로거 규칙이 “실행”될 수 있으며 로거 규칙이 “최종”으로 표시 될 수 있다는 개념 일 것입니다. 주어진 로깅 문.

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”등으로 지정할 수 있습니다.

자신 만의 레이아웃 렌더러를 작성하는 것을 두려워하지 마십시오. 구성을 통해 쉽고 컨텍스트 정보를 로그 파일에 추가 할 수 있습니다. 예를 들어 Trace.CorrelationManager.ActivityId를 로그에 추가 할 수있는 레이아웃 렌더러 (2.0이 아닌 NLog 1.x 기반)는 다음과 같습니다.

  [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 (또는 그 문제에 대한 다른 로깅 프레임 워크)를 래핑 (또는 추상화)하는 장단점이 있습니다. 약간의 노력만으로도 양면을 제시하는 데 대한 많은 정보를 찾을 수 있습니다.

줄 바꿈을 고려중인 경우 Common.Logging 사용을 고려 하십시오 . 그것은 잘 작동하며 원하는 경우 다른 로깅 프레임 워크로 쉽게 전환 할 수 있습니다. 랩핑을 고려중인 경우 컨텍스트 오브젝트 (GDC, MDC, NDC)를 처리하는 방법을 고려하십시오. Common.Logging은 현재 이들에 대한 추상화를 지원하지 않지만 추가 할 수있는 대기열에있을 것입니다.


답변

예외를 다르게 취급

우리는 종종 예외가있을 때 더 많은 정보를 얻고 싶어합니다. 다음 구성에는 예외 정보가 있는지 여부를 필터링하는 두 개의 대상 (파일과 콘솔)이 있습니다. (편집 : Jarek은 vNext 에서이 작업을 수행 하는 새로운 방법에 대해 게시 했습니다 .)

핵심은 래퍼 대상을 갖는 것입니다. xsi:type="FilteringWrapper" condition="length('${exception}')>0"

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.mono2.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Warn"
      internalLogFile="nlog log.log"
      >
    <variable name="VerboseLayout"
              value="${longdate} ${level:upperCase=true} ${message}
                    (${callsite:includSourcePath=true})"            />
    <variable name="ExceptionVerboseLayout"
              value="${VerboseLayout} (${stacktrace:topFrames=10})
                     ${exception:format=ToString}"                  />

    <targets async="true">
        <target name="file" xsi:type="File" fileName="log.log"
                layout="${VerboseLayout}">
        </target>

        <target name="fileAsException"
                xsi:type="FilteringWrapper"
                condition="length('${exception}')>0">
            <target xsi:type="File"
                    fileName="log.log"
                    layout="${ExceptionVerboseLayout}" />
        </target>

        <target xsi:type="ColoredConsole"
                name="console"
                layout="${NormalLayout}"/>

        <target xsi:type="FilteringWrapper"
                condition="length('${exception}')>0"
                name="consoleException">
            <target xsi:type="ColoredConsole"
                    layout="${ExceptionVerboseLayout}" />
        </target>
    </targets>

    <rules>
        <logger name="*" minlevel="Trace" writeTo="console,consoleException" />
        <logger name="*" minlevel="Warn" writeTo="file,fileAsException" />
    </rules>

</nlog>


답변

분명히 Windows 용 Growl과 함께 NLog를 사용할 수 있습니다 .

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

Growl for Windows 용 NLog
Growl for Windows가있는 NLog 추적 메시지
Windows 용 Growl을 사용한 NLog 디버그 메시지
Growl for Windows와 함께 NLog 정보 메시지
Growl for Windows에서 NLog 경고 메시지
Growl for Windows의 NLog 오류 메시지
Growl for Windows와 함께 NLog 치명적인 메시지


답변

프로그래밍 방식으로 XML을 통해 NLog 구성

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

이것이 얼마나 강력한 지 잘 모르겠지만이 예제는 이와 같이 구성하려고하는 사람들에게 유용한 시작점을 제공합니다.


답변

오류 유무에 따라 다른 레벨 로깅

이 예제를 사용하면 코드에 오류가있을 때 자세한 정보를 얻을 수 있습니다. 기본적으로 메시지를 버퍼링 하고 특정 조건이 충족 되지 않으면 (예 : 오류가 발생하여 로그 수준이> = 오류) 특정 로그 수준 (예 : 경고)의 메시지 만 출력 하면 더 많은 정보를 출력합니다 (예 : 로그 수준의 모든 메시지> = 추적). 메시지가 버퍼링되므로 오류 또는 오류 예외가 기록 되기 전에 발생한 문제에 대한 추적 정보를 수집 할 수 있습니다 . 매우 유용합니다!

나는 소스 코드의 예제에서 이것을 수정했다 . AspNetBufferingWrapper(내 것이 ASP 앱이 아니기 때문에) 생략했기 때문에 처음에 던져졌습니다 .PostFilteringWrapper 에는 버퍼링 된 대상이 필요합니다. 있습니다 target-ref(I는 .NET 4.0 앱 1.0 새로 고침을 사용하고 있습니다) 위의 링크 된 예에서 사용 된 요소가 NLog 1.0에서 사용할 수 없습니다; 래퍼 블록 안에 대상을 넣어야합니다. 또한 논리 구문 (즉,보다 크거나 작은 심볼, <및>)은 해당 심볼 (예 : &gt;&lt;) 의 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>


답변

이 질문에 대해 합리적으로 흥미로운 몇 가지 답변을 제공했습니다.

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> 

다시 예외를 다르게 처리하는 것과 매우 유사합니다 .


답변

트위터에 로그인

log4net Twitter Appender에 대한이 게시물을 기반으로, 나는 NLog Twitter Target (2.0이 아닌 NLog 1.0 새로 고침 사용)을 작성하려고 노력할 것이라고 생각했습니다. 아아, 지금까지 실제로 성공적으로 게시 할 트윗을 얻을 수 없었습니다. 내 코드, 트위터, 회사의 인터넷 연결 / 방화벽 또는 무엇에 문제가 있는지 모르겠습니다. 누군가 코드를 시험해 보는 데 관심이있는 경우 여기에 코드를 게시하고 있습니다. 세 가지 다른 “포스트”방법이 있습니다. 내가 시도한 첫 번째는 PostMessageToTwitter입니다. PostMessageToTwitter는 본질적으로 orignal post의 PostLoggingEvent와 동일합니다. 그것을 사용하면 401 예외가 발생합니다. PostMessageBasic은 동일한 예외를받습니다. PostMessage는 오류없이 실행되지만 메시지는 여전히 Twitter에 적용되지 않습니다. PostMessage와 PostMessageBasic은 SO에서 찾은 예제를 기반으로합니다.

참고 로, @Jason Diller 가 트위터에서 “다음 달”기본 인증을 해제 할 것이라는 이 게시물 의 답변에 대한 의견을 찾았 습니다 . 이것은 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>

누군가 이것을 시도하고 성공하면, 발견 한 내용을 다시 게시하십시오.