Configurações NLog mais úteis [fechado]


348

Quais são as configurações melhores ou mais úteis para fazer logon com o NLog? (Eles podem ser simples ou complexos, desde que sejam úteis.)

Estou pensando em exemplos como rolar automaticamente arquivos de log em um determinado tamanho, alterar o layout (mensagem de log) se há ou não uma exceção, aumentar o nível de log quando ocorrer um erro etc.

Aqui estão alguns links:


3
Aqui estão algumas dicas de ajuste de desempenho com base em testes: deep-depth.blogspot.com/2014/01/...
Neil

Respostas:


391

Algumas delas se enquadram na categoria de dicas gerais sobre NLog (ou log), em vez de sugestões estritamente de configuração.

Aqui estão alguns links gerais de registro aqui no SO (você já deve ter visto alguns ou todos esses):

log4net vs. Nlog

Práticas recomendadas de log

Qual o sentido de uma fachada de madeira?

Por que os registradores recomendam o uso de um registrador por classe?

Use o padrão comum de nomear seu criador de logs com base na classe Logger logger = LogManager.GetCurrentClassLogger(). Isso proporciona um alto grau de granularidade em seus criadores de logs e oferece grande flexibilidade na configuração dos registradores (controle global, por espaço de nome, por nome específico do registrador, etc.).

Use loggers não baseados em nome de classe, quando apropriado. Talvez você tenha uma função para a qual realmente deseja controlar o log separadamente. Talvez você tenha algumas preocupações transversais de log (log de desempenho).

Se você não usar o log baseado em nome de classe, considere nomear seus loggers em algum tipo de estrutura hierárquica (talvez por área funcional), para que você possa manter uma maior flexibilidade em sua configuração. Por exemplo, você pode ter uma área funcional "banco de dados", uma FA "análise" e uma FA "ui". Cada um desses pode ter subáreas. Portanto, você pode solicitar registradores como este:

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

E assim por diante. Com os registradores hierárquicos, você pode configurar o registro globalmente (o "*" ou o registrador raiz), por FA (banco de dados, análise, interface do usuário) ou por subárea (Database.Connect, etc).

Os registradores têm muitas opções de configuração:

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

Consulte a ajuda do NLog para obter mais informações sobre exatamente o que cada uma das opções significa. Provavelmente, os itens mais notáveis ​​aqui são a capacidade de regras do agente curinga, o conceito de que várias regras do agente podem "executar" para uma única instrução de registro e que uma regra do agente pode ser marcada como "final", para que as regras subsequentes não sejam executadas por um dada declaração de log.

Use GlobalDiagnosticContext, MappedDiagnosticContext e NestedDiagnosticContext para adicionar contexto adicional à sua saída.

Use "variável" no seu arquivo de configuração para simplificar. Por exemplo, você pode definir variáveis ​​para seus layouts e depois referenciar a variável na configuração de destino, em vez de especificar o layout diretamente.

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

Ou, você pode criar um conjunto "personalizado" de propriedades para adicionar a um 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}"/>

Ou você pode fazer coisas como criar renderizadores de layout "dia" ou "mês" estritamente via configuração:

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

Você também pode usar renderizações de layout para definir seu nome de arquivo:

  <variable name="day" value="${date:format=dddd}"/>
  <targets>
    <target name="file" xsi:type="File" layout="${verbose}" fileName="${basedir}/${day}.log" />
  </targets>

Se você rolar o arquivo diariamente, cada arquivo poderá ter o nome "Monday.log", "Tuesday.log" etc.

Não tenha medo de escrever seu próprio renderizador de layout. É fácil e permite adicionar suas próprias informações de contexto ao arquivo de log via configuração. Por exemplo, aqui está um renderizador de layout (baseado no NLog 1.x, não 2.0) que pode adicionar Trace.CorrelationManager.ActivityId ao log:

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

Informe ao NLog onde suas extensões do NLog (que montagem) estão assim:

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

Use o renderizador de layout personalizado como este:

  <variable name="fmt" value="${longdate} | ${ActivityId} | ${message}"/>

Use destinos assíncronos:

<nlog>
  <targets async="true">
    <!-- all targets in this section will automatically be asynchronous -->
  </targets>
</nlog>

E wrappers de destino padrão:

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

onde apropriado. Consulte os documentos do NLog para obter mais informações sobre eles.

Diga ao NLog para assistir e recarregar automaticamente a configuração se ela mudar:

<nlog autoReload="true" /> 

Existem várias opções de configuração para ajudar na solução de problemas do NLog

<nlog throwExceptions="true" />
<nlog internalLogFile="file.txt" />
<nlog internalLogLevel="Trace|Debug|Info|Warn|Error|Fatal" />
<nlog internalLogToConsole="false|true" />
<nlog internalLogToConsoleError="false|true" />

Consulte a Ajuda do NLog para obter mais informações.

O NLog 2.0 adiciona wrappers LayoutRenderer que permitem que um processamento adicional seja executado na saída de um renderizador de layout (como aparar espaços em branco, maiúsculas, minúsculas etc.).

Não tenha medo de quebrar o logger se você quiser isolar seu código de uma forte dependência do NLog, mas envolva-o corretamente. Existem exemplos de como agrupar no repositório github do NLog. Outro motivo para quebrar pode ser que você deseja adicionar automaticamente informações de contexto específicas a cada mensagem registrada (colocando-a em LogEventInfo.Context).

Existem prós e contras no empacotamento (ou abstração) do NLog (ou em qualquer outra estrutura de registro para esse assunto). Com um pouco de esforço, você pode encontrar muitas informações aqui, apresentando os dois lados.

Se você estiver pensando em quebrar , considere usar o Common.Logging . Funciona muito bem e permite que você alterne facilmente para outra estrutura de log, se desejar. Além disso, se você estiver pensando em agrupar, pense em como manipulará os objetos de contexto (GDC, MDC, NDC). No momento, o Common.Logging não oferece suporte a uma abstração, mas supostamente está na fila de recursos a serem adicionados.


3
Ótima resposta. Apenas uma coisa a acrescentar: $ {machine} deve ser $ {machinename}. Consulte github.com/nlog/NLog/wiki/Layout-Renderers .
Liang

2
Eu peguei o Common.Logging e adicionei a abstração ausente, consulte Projeto GitHub ou NuGet .
Danny Varod

Não consegui encontrar nada tão informativo sobre o nlog em sua própria documentação, talvez esteja procurando nos exemplos do github da maneira errada? Quem sabe.
JARRRRG

Como usar esse renderizador personalizado com a API (sem arquivo de configuração)? Aqui está o que estou tentando realizar.
InteXX 19/04

OK, entendi. O NewLinelayout realiza a tarefa. Aqui está o que eu criei. Com certeza, é muito mais simples do que eu esperava.
InteXX

65

Tratar exceções de maneira diferente

Muitas vezes, queremos obter mais informações quando há uma exceção. A configuração a seguir possui dois destinos, um arquivo e o console, que filtram se há ou não informações de exceção. (Edição: Jarek postou sobre um novo método para fazer isso no vNext .)

A chave é ter um destino de wrapper com 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>

11
Isso é muito legal com o destino separado e o FilteringWrapper para formatar a exceção. Acabei de responder uma pergunta recentemente de um cara que queria incluir o renderizador de layout {exception} em sua saída, mas ele não queria obter o () que aparentemente está registrado, se NÃO houver uma exceção. Essa técnica provavelmente funcionaria bem para ele.
precisa saber é o seguinte

+1 Muito bom. Eu tenho isso marcado por um longo tempo e me referi ao "comentário de Pat" de outra pergunta do SO em relação a um layout condicional.
eduncan911

11
Se uma exceção for registrada, ela será registrada duas vezes (parte VerboseLayout).
Tien Do

2
Eu apenas tentei amanhã no meu projeto, já que você definiu uma regra minlevel = "Warn" para "file, fileAsException", todos os logs serão registrados primeiro com o destino do arquivo (sem filtro) e se for uma exceção (conforme filtrado por condição) também será registrado com fileAsException.
você

3
@ Tiendq Oh, entendo. Isso faz sentido, embora a exceção em si (em detalhes completos) seja registrada apenas uma vez (mas sua mensagem será registrada duas vezes). Você provavelmente pode consertar isso adicionando condition="length('${exception}')=0(ou talvez seja ==) a target name="file".
Pat

60

Aparentemente, agora você pode usar o NLog com Growl para 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 com Growl para Windows Mensagem de rastreamento NLog com Growl para Windows Mensagem de depuração NLog com Growl para Windows NLog mensagem informativa com Growl para Windows NLog avisar mensagem com Growl para Windows Mensagem de erro NLog com Growl para Windows NLog mensagem fatal com Growl para Windows


você pode me dizer o que fazer para a conexão remort? a coisa funciona para mim para localhost, mas quando eu tiver dado algum endereço IP no host, ele não está funcionando!
Neel

@ Neel, você deve verificar as configurações de "Segurança" em Growl no computador de destino. É necessário ativar explicitamente as notificações "LAN" e convém configurar uma senha (que você precisará adicionar ao seu destino NLog). Mas não gostei que as notificações remotas aparecessem no Growl com uma "Origem" de "Máquina local"; Eu precisaria adicionar o host às entradas de log para saber onde as notificações se originaram.
Kenny Evitt

Posso fazer com que as notificações funcionem na minha máquina local, mas não remotamente. Minhas configurações de segurança não têm senha no rosnado, então tudo o que adicionei foi o IP e a porta. Mas nada é enviado.
Jack Reilly

11
Este projeto está 100% morto
Desenvolvedor

28

Configure o NLog via XML, mas programaticamente

O que? Você sabia que pode especificar o XML do NLog diretamente para o NLog do seu aplicativo, em vez de fazer com que o NLog o leia no arquivo de configuração? Bem, você pode. Digamos que você tenha um aplicativo distribuído e deseje usar a mesma configuração em qualquer lugar. Você pode manter um arquivo de configuração em cada local e mantê-lo separadamente, você pode manter um em um local central e enviá-lo para os locais de satélite, ou você provavelmente poderia fazer muitas outras coisas. Ou então, você pode armazenar seu XML em um banco de dados, obtê-lo na inicialização do aplicativo e configurar o NLog diretamente com esse XML (talvez retorne periodicamente para verificar se ele foi alterado).

  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

Não tenho certeza de quão robusto isso é, mas este exemplo fornece um ponto de partida útil para pessoas que podem querer tentar configurar assim.


funciona muito bem ... exceto que usando isso, não é mais possível reconfigurar dinamicamente o sistema de registro. Isso é especialmente verdade se você link para um arquivo de fora (incluir)
Newtopian

2
Isso funcionou, embora eu tenha que escrever XML "bom" incluindo:<?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'>
Gady

11
Este é um bom segway para a configuração centralizada. Futuros leitores, xml codificado neste exemplo, são apenas para demonstração (IMHO), lê-lo em um banco de dados ou arquivo centralizado pode ser a implementação real.
precisa saber é o seguinte

@wageoghe; Por que recebo erro (o logger não existe)? Eu apenas copio e
colo

22

Registrando níveis diferentes, dependendo se há ou não um erro

Este exemplo permite que você obtenha mais informações quando houver um erro no seu código. Basicamente, ele armazena em buffer as mensagens e apenas as emite em um determinado nível de log (por exemplo, Avisar), a menos que uma determinada condição seja atendida (por exemplo, houve um erro, portanto, o nível de log é> = Erro) e, em seguida, gera mais informações (por exemplo, todas as mensagens dos níveis de log> = Rastreio). Como as mensagens são armazenadas em buffer, isso permite coletar informações de rastreamento sobre o que aconteceu antes do registro de um Error or ErrorException - muito útil!

Eu adaptei este a partir de um exemplo no código fonte . Fui jogado inicialmente porque deixei de fora o AspNetBufferingWrapper(já que o meu não é um aplicativo ASP) - acontece que o PostFilteringWrapper requer algum destino em buffer. Observe que o target-refelemento usado no exemplo vinculado acima não pode ser usado no NLog 1.0 (estou usando o 1.0 Refresh para um aplicativo .NET 4.0); é necessário colocar seu alvo dentro do bloco de invólucro. Observe também que a sintaxe lógica (ou seja, maior ou menor que símbolos, <e>) deve usar os símbolos, e o XML não escapa para esses símbolos (ou seja, &gt;e&lt; ) ou o NLog irá erro.

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>

Em algumas versões do NLog (para mono e acho que 2.0), isso causa uma StackOverflowException, mas não em outras (atualização do NLog 1).
Pat

Em relação ao estouro - parece ser apenas devido ao layout ser do tipo CSV - se eu fizer um layout regular, não haverá problema.
Pat

Para que serve o fileAsCsv target-ref? Estou tentando fazer com que este exemplo funcione contra o NLog v2.0.0.2000, mas até agora está falhando.
Peter Mounce

@PeterMounce O fileAsCsvtarget-ref é apenas um artefato dos meus testes. Acredito que o NLog 2 tenha tido / teve problemas com o CsvLayouts que o NLog 1 / Refresh não teve.
Pat

22

Forneci algumas respostas razoavelmente interessantes para esta pergunta:

Nlog - Gerando Seção de Cabeçalho para um Arquivo de Log

Adicionando um cabeçalho:

A pergunta queria saber como adicionar um cabeçalho ao arquivo de log. O uso de entradas de configuração como essa permite definir o formato do cabeçalho separadamente do formato do restante das entradas de log. Use um único logger, talvez chamado de "headerlogger", para registrar uma única mensagem no início do aplicativo e você obtém seu cabeçalho:

Defina os layouts de cabeçalho e arquivo:

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

Defina os destinos usando os layouts:

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

Defina os registradores:

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

Escreva o cabeçalho, provavelmente no início do programa:

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

Essa é apenas mais uma versão da idéia "Tratar exceções de maneira diferente".

Registrar cada nível de log com um layout diferente

Da mesma forma, o pôster queria saber como alterar o formato por nível de log. Não estava claro para mim qual era o objetivo final (e se poderia ser alcançado de uma maneira "melhor"), mas pude fornecer uma configuração que fizesse o que ele pediu:

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

Novamente, muito parecido com Tratar exceções de maneira diferente .


11
Legal! Eu não tinha visto o GlobalDiagnosticsContextantes.
Pat

10

Entrar no Twitter

Baseado neste post sobre um log4net Twitter Appender, Pensei em tentar escrever um NLog Twitter Target (usando a atualização do NLog 1.0, não a 2.0). Infelizmente, até agora não consegui que um Tweet fosse publicado com êxito. Não sei se há algo errado no meu código, no Twitter, na conexão com a Internet da empresa / firewall ou o quê. Estou postando o código aqui, caso alguém esteja interessado em experimentá-lo. Observe que existem três métodos "Post" diferentes. O primeiro que eu tentei é PostMessageToTwitter. PostMessageToTwitter é essencialmente o mesmo que PostLoggingEvent na postagem original. Se eu usar isso, recebo uma exceção 401. PostMessageBasic recebe a mesma exceção. O PostMessage é executado sem erros, mas a mensagem ainda não chega ao Twitter. PostMessage e PostMessageBasic são baseados em exemplos que encontrei aqui no SO.

FYI - Acabei de encontrar um comentário de @Jason Diller para uma resposta neste post que diz que o twitter desativará a autenticação básica "no próximo mês". Isso foi em maio de 2010 e agora é dezembro de 2010, então acho que pode ser por isso que isso não está funcionando.

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-o assim:

Informe ao NLog o assembly que contém o destino:

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

Configure o destino:

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

Se alguém tentar fazer isso e tiver sucesso, poste de volta com suas descobertas.


O Twitter usa OAuth - O .NET possui um provedor em dotnetopenauth.net
Pat

7

Relatórios para um site / banco de dados externo

Eu queria uma maneira simples e automática de relatar erros (já que os usuários geralmente não o fazem) de nossos aplicativos. A solução mais fácil que eu consegui encontrar foi uma URL pública - uma página da web que poderia receber e armazenar em um banco de dados - que envia dados após um erro de aplicativo. (O banco de dados pode ser verificado por um desenvolvedor ou um script para saber se há novos erros.)

Eu escrevi a página da web em PHP e criei um banco de dados mysql, usuário e tabela para armazenar os dados. Decidi sobre quatro variáveis ​​de usuário, um ID e um carimbo de data e hora. As variáveis ​​possíveis (incluídas na URL ou como dados POST) são:

  • app (Nome da Aplicação)
  • msg (mensagem - por exemplo, ocorreu uma exceção ...)
  • dev (desenvolvedor - por exemplo, Pat)
  • src(origem - isso viria de uma variável pertencente à máquina na qual o aplicativo estava sendo executado, por exemplo, Environment.MachineNameou algo assim)
  • log (um arquivo de log ou mensagem detalhada)

(Todas as variáveis ​​são opcionais, mas nada é relatado se nenhuma delas estiver definida - portanto, se você apenas visitar o URL do site, nada será enviado ao banco de dados.)

Para enviar os dados para o URL, usei o WebServicedestino do NLog . (Observe, no início, tive alguns problemas com esse destino. Foi só depois de olhar para a fonte que descobri que o meu urlnão podia terminar com um /.)

Em suma, não é um sistema ruim para controlar as aplicações externas. (Obviamente, a coisa mais educada a fazer é informar aos usuários que você estará relatando dados possivelmente confidenciais e dar a eles uma maneira de ativar / desativar.)

Coisas do MySQL

(O usuário db possui apenas INSERTprivilégios nessa tabela em seu próprio banco de dados.)

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'

Código do site

(PHP 5.3 ou 5.2 com DOP ativado , o arquivo está index.phpna /reportpasta)

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

Código do aplicativo (arquivo de configuração 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>

Nota: pode haver alguns problemas com o tamanho do arquivo de log, mas não descobri uma maneira simples de truncá-lo (por exemplo, o tailcomando de um la * nix ).


Isso funcionou para um projeto, mas em outros tive problemas com o url: InnerException: System.InvalidCastException Message = conversão inválida de 'System.String' para 'System.Uri'. Origem = mscorlib StackTrace: no System.Convert.DefaultToType (valor IConvertible, Tipo targetType, provedor IFormatProvider) em System.String.System.IConvertible.ToType (Tipo de tipo, provedor IFormatProvider) em System.Convert.ChangeType (Valor do objeto, Tipo conversionType , Provedor IFormatProvider)
Pat

Outra opção, se você deseja monitorar o log e ser notificado em caso de erro, seria um destino do Twitter. Consulte este link para um Twitter Appender escrito para log4net: twitterappender.codeplex.com A postagem original do blog discutindo isso está aqui: caseywatson.com/2009/07/07/log4net-twitter-awesome Deve ser bem fácil escrever algo semelhante para NLog.
precisa saber é o seguinte

Eu brinquei com a redação de um NLog TwitterTarget, mas não obtive sucesso ao publicar um Tweet. Publiquei o código como resposta. Sinta-se livre para experimentá-lo, se quiser.
wageoghe

7

Maneira mais fácil de registrar cada nível de log com um layout diferente usando layouts condicionais

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

Consulte https://github.com/NLog/NLog/wiki/When-Filter para obter sintaxe


4

Log do Silverlight

Ao usar o NLog com o Silverlight, você pode enviar o rastreamento para o lado do servidor por meio do serviço da web fornecido . Você também pode gravar em um arquivo local no Armazenamento Isolado, que será útil se o servidor da Web estiver indisponível. Veja aqui para detalhes, ou seja, use algo como isto para tornar-se um alvo:

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);
                    }
                }
            }
        }
    } 
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.