답변:
이 중 일부는 구성 제안을 엄격하게 제시하기보다는 일반적인 NLog (또는 로깅) 팁 범주에 속합니다.
여기 SO의 일반적인 로깅 링크가 있습니다 (이들 중 일부 또는 전부를 이미 보았을 것입니다).
클래스를 기반으로 로거의 이름을 지정하는 일반적인 패턴을 사용하십시오 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>
condition="length('${exception}')=0
(또는 가능 ==
)하여 이를 수정할 수 있습니다 target name="file"
.
분명히 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>
프로그래밍 방식으로 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
이것이 얼마나 강력한 지 잘 모르겠지만이 예제는 이와 같이 구성하려고하는 사람들에게 유용한 시작점을 제공합니다.
<?xml version='1.0' encoding='utf-8' ?><nlog xmlns='http://nlog-project.org/schemas/NLog.xsd' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
이 예제를 사용하면 코드에 오류가있을 때 자세한 정보를 얻을 수 있습니다. 기본적으로 메시지를 버퍼링 하고 특정 조건이 충족 되지 않으면 (예 : 오류가 발생하여 로그 수준이> = 오류) 특정 로그 수준 (예 : 경고)의 메시지 만 출력 하면 더 많은 정보를 출력합니다 (예 : 로그 수준의 모든 메시지> = 추적). 메시지가 버퍼링되므로 오류 또는 오류 예외가 기록 되기 전에 발생한 문제에 대한 추적 정보를 수집 할 수 있습니다 . 매우 유용합니다!
나는 소스 코드의 예제에서 이것을 수정했다 . AspNetBufferingWrapper
(내 것이 ASP 앱이 아니기 때문에) 생략했기 때문에 처음에 던져졌습니다 .PostFilteringWrapper 에는 버퍼링 된 대상이 필요합니다. 있습니다 target-ref
(I는 .NET 4.0 앱 1.0 새로 고침을 사용하고 있습니다) 위의 링크 된 예에서 사용 된 요소가 NLog 1.0에서 사용할 수 없습니다; 래퍼 블록 안에 대상을 넣어야합니다. 또한 논리 구문 (즉,보다 크거나 작은 심볼, <및>)은 해당 심볼 (예 : >
및 <
) 의 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>
fileAsCsv
target-ref는 내 테스트의 인공물입니다. NLog 2에 NLog 1 / Refresh에는 없었던 CsvLayouts에 문제가 있거나 있다고 생각합니다.
이 질문에 대해 합리적으로 흥미로운 몇 가지 답변을 제공했습니다.
헤더 추가 :
질문은 로그 파일에 헤더를 추가하는 방법을 알고 싶었습니다. 이와 같은 구성 항목을 사용하면 나머지 로그 항목의 형식과 별도로 헤더 형식을 정의 할 수 있습니다. 응용 프로그램을 시작할 때 "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>
다시 예외를 다르게 처리하는 것과 매우 유사합니다 .
GlobalDiagnosticsContext
전에 본 적이 없었다 .
트위터에 로그인
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>
누군가 이것을 시도하고 성공하면, 발견 한 내용을 다시 게시하십시오.
응용 프로그램에서 사용자가 자주하지 않기 때문에 간단하고 자동으로 오류를보고하는 방법을 원했습니다. 내가 얻을 수있는 가장 쉬운 해결책은 공용 URL-입력을 받아서 데이터베이스에 저장할 수있는 웹 페이지-응용 프로그램 오류시 데이터를 보내는 것입니다. 그런 다음 개발자 나 스크립트로 데이터베이스를 검사하여 새로운 오류가 있는지 확인할 수 있습니다.
PHP로 웹 페이지를 작성하고 데이터를 저장할 mysql 데이터베이스, 사용자 및 테이블을 만들었습니다. 4 개의 사용자 변수, ID 및 타임 스탬프를 결정했습니다. 가능한 변수 (URL에 포함되거나 POST 데이터로)는 다음과 같습니다.
app
(신청 명)msg
(메시지-예 : 예외가 발생했습니다 ...)dev
(개발자-예 : Pat)src
(소스 -이 앱은 예를 들어, 실행 된 시스템에 관련된 변수에서 올 것 Environment.MachineName
또는 일부 등)log
(로그 파일 또는 자세한 메시지)(모든 변수는 선택 사항이지만 설정되지 않은 경우 아무 것도보고되지 않으므로 웹 사이트 URL을 방문하면 db로 아무것도 전송되지 않습니다.)
데이터를 URL로 보내려면 NLog의 WebService
target을 사용했습니다 . (참고로, 나는 처음 에이 목표에 몇 가지 문제가 url
있었습니다 /
.
대체로 외부 앱에 탭을 유지하는 것은 나쁜 시스템이 아닙니다. (물론, 예의 바르게 행동하는 것은 사용자에게 민감한 데이터를보고 할 것이라고 알리고 옵트 인 / 아웃 할 수있는 방법을 제공하는 것입니다.)
(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;
}
?>
<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 's tail
command ).
url
다음 과 같은 문제가 발생했습니다 . InnerException : System.InvalidCastException Message = 'System.String'에서 'System.Uri'로의 캐스트가 잘못되었습니다. Source = mscorlib StackTrace : System.Convert.ChangeType (Object 값, 형식 변환 형식)의 System.String.System.IConvertible.ToType (Type 형식, IFormatProvider 공급자)의 System.Convert.DefaultToType (IConvertible 값, 형식 targetType, IFormatProvider 공급자)에서 , IFormatProvider 제공 업체)
조건부 레이아웃을 사용하여 다른 레이아웃으로 각 로그 수준을 기록하는 쉬운 방법
<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 를 참조 하십시오.
Silverlight에서 로그
Silverlight와 함께 NLog를 사용하면 제공된 웹 서비스 를 통해 추적을 서버 측으로 보낼 수 있습니다 . 웹 서버를 사용할 수없는 경우 격리 저장소의 로컬 파일에 쓸 수도 있습니다. 자세한 내용은 여기 를 참조 하십시오 . 예를 들어 다음과 같이 자신을 목표로 삼으십시오.
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);
}
}
}
}
}
}