NLog: regras e filtros

NLog: regras e filtros


Na Confirmit , usamos a biblioteca NLog para efetuar logon em nossos aplicativos .NET. Embora exista documentação para esta biblioteca, foi difícil para mim entender como tudo funciona. Neste artigo, tentarei explicar como regras e filtros são aplicados no NLog. Vamos começar.


Como configurar o NLog


E começaremos com um pequeno lembrete do que podemos fazer com a configuração do NLog. No caso mais simples, essa configuração é um arquivo XML (por exemplo, NLog.config):


<?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"> <targets> <target name="target1" xsi:type="ColoredConsole" layout="Access Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="red"/> </target> <target name="target2" xsi:type="ColoredConsole" layout="Common Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="green"/> </target> <target name="target3" xsi:type="ColoredConsole" layout="Yellow Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="yellow"/> </target> </targets> <rules> <logger name="*" minlevel="Warn" writeTo="target1,target2,target3" /> </rules> </nlog> 

Você pode baixar este arquivo com uma linha de código:


 LogManager.Configuration = new XmlLoggingConfiguration("NLog.config"); 

O que podemos fazer com isso? Podemos definir vários receptores de mensagens (destino) para a regra:


 <rules> <logger name="*" minlevel="Warn" writeTo="target1,target2,target3" /> </rules> 

Podemos determinar para quais níveis de registro essa regra é aplicada:


 <rules> <logger name="*" minlevel="Warn" writeTo="target1" /> <logger name="*" levels="Debug,Warn,Info" writeTo="target2" /> </rules> 

Podemos definir filtros para cada regra:


 <rules> <logger name="*" minlevel="Info" writeTo="target1"> <filters defaultAction='Log'> <when condition="contains('${message}','Common')" action="Ignore" /> </filters> </logger> </rules> 

E, finalmente, podemos definir regras aninhadas:


 <rules> <logger name="*" minlevel="Info" writeTo="target1"> <logger name="*" minlevel="Warn" writeTo="target2" /> </logger> </rules> 

É hora de descobrir como tudo funciona.


Criando uma configuração do criador de logs


Quando você solicita uma instância do criador de logs,


 var commonLogger = LogManager.GetLogger("Common"); 

O NLog pega um existente no cache ou cria um novo (veja aqui ). Neste último caso, também é criada uma configuração para o criador de logs com o nome fornecido. Vejamos o processo de criá-lo.


Em resumo, a configuração do criador de logs é uma cadeia separada de receptores e filtros correspondentes para cada nível de registro ( Trace , Debug , Info , Warn , Error , Fatal ) (veja aqui ). Agora vou mostrar como essas cadeias são construídas.


O principal método responsável pela criação dessas cadeias é GetTargetsByLevelForLogger da classe LogFactory . É assim que funciona. Todas as regras especificadas na configuração do NLog são selecionadas por sua vez. Primeiro, ele verifica se o nome da regra corresponde ao nome do criador de logs. Os nomes de regras podem conter curingas, como aqueles que usamos para objetos do sistema de arquivos:


  • * - uma sequência arbitrária de caracteres
  • ? - qualquer caractere único

Portanto, o nome da regra ' * ' corresponde a qualquer nome do criador de logs e ' Common* ' corresponde a todos os criadores de logs cujos nomes começam com ' Common '.


Se o nome da regra não corresponder ao nome do criador de logs, essa regra será descartada com todas as regras incorporadas. Caso contrário, o método GetTargetsByLevelForLogger obtém todos os níveis de log para os quais essa regra está ativada. Para cada nível, o NLog adiciona todos os receptores de mensagens especificados na regra às cadeias de receptores correspondentes, juntamente com os filtros para esta regra.


Há outra característica importante na construção de cadeias de receptores. Se a regra atual estiver marcada como final e seu nome corresponder ao nome do criador de logs, o NLog concluirá a construção de cadeias para todos os níveis de criação de log incluídos para esta regra. Isso significa que nem as regras aninhadas nem as regras subseqüentes adicionam nada a essas cadeias de receptores. Sua criação está totalmente concluída e eles não mudarão. Daqui resulta que não faz sentido escrever algo como isto:


 <rules> <logger name="*" minlevel="Info" writeTo="target1" final="true"> <logger name="*" minlevel="Warn" writeTo="target2" /> </logger> </rules> 

Nenhuma mensagem chegará ao target2 . Mas é possível escrever algo como isto:


 <rules> <logger name="*" minlevel="Warn" writeTo="target1" final="true"> <logger name="*" minlevel="Info" writeTo="target2" /> </logger> </rules> 

Como a regra externa não está ativada para o nível Info , a cadeia de receptores desse nível não terminará na regra externa. Portanto, todas as mensagens com o nível Info caem no target2 .


Depois que todos os receptores desta regra são adicionados às cadeias correspondentes, o método processa recursivamente todas as regras aninhadas da regra atual de acordo com o mesmo algoritmo. Isso acontece independentemente dos níveis de log ativados para a regra pai.


No total, a configuração do criador de logs está pronta. Ele contém cadeias de receptores com filtros para cada nível possível de registro:


Cadeia do receptor


É hora de ver como essa configuração é usada.


Usando a configuração do criador de logs


Vamos começar com coisas simples. A classe Logger possui um método IsEnabled e propriedades IsXXXEnabled associadas ( IsDebugEnabled , IsInfoEnabled , ...). Como eles funcionam? De fato, eles simplesmente verificam se as cadeias receptoras de um determinado nível de registro contêm pelo menos um link (veja aqui ). Isso significa que os filtros nunca afetam os valores dessas propriedades.


Em seguida, deixe-me explicar o que acontece quando você tenta proteger uma mensagem. Como você deve ter adivinhado, o criador de logs usa uma cadeia de receptores para o nível de registro desta mensagem. Então ele começa a processar os elos dessa cadeia, um após o outro. Para cada link, o criador de logs decide se deve gravar a mensagem no destinatário especificado no link e se deve continuar processando a cadeia depois disso. Essas decisões são tomadas usando filtros. Deixe-me mostrar como os filtros funcionam no NLog.


Veja como os filtros são configurados:


 <rules> <logger name="*" minlevel="Info" writeTo="target1"> <filters defaultAction='Log'> <when condition="contains('${message}','Common')" action="Ignore" /> </filters> </logger> </rules> 

Normalmente, um filtro contém alguma condição booleana. Aqui você pode decidir se o filtro retorna true ou false para cada mensagem. Mas isso não é verdade. O resultado do trabalho deles é um valor do tipo FilterResult . Se a condição do filtro retornar true , o resultado do filtro se tornará o valor especificado no atributo action (no nosso exemplo, isso é Ignore ). Se a condição retornar false , o resultado do filtro será Neutral . Isso significa que o filtro não deseja decidir o que fazer com a mensagem.


Você pode ver como a cadeia de receptores é processada aqui . Para cada receptor, o resultado dos filtros correspondentes no método GetFilterResult é GetFilterResult . É igual ao resultado do primeiro filtro que não retornou Neutral . Isso significa que, se algum filtro retornar um valor diferente de Neutral , todos os filtros subsequentes não serão executados.


Mas o que acontece se todos os filtros retornarem Neutral ? Nesse caso, o valor padrão será usado. Este valor é configurado usando o atributo defaultAction do elemento de filters para a regra. Qual você acha que é o valor padrão para defaultAction ? Você está certo se você acha que isso é Neutral . Ou seja, toda a cadeia de filtros pode retornar Neutral como resultado. Nesse caso, o NLog se comporta da mesma forma que o Log recebimento. A mensagem será gravada no destinatário (veja aqui ).


Como você deve ter adivinhado, se um filtro retornar Ignore ou IgnoreFinal , a mensagem não será gravada no destinatário. Se o resultado do filtro for Log ou LogFinal , a mensagem será gravada. Mas qual é a diferença entre Ignore e IgnoreFinal e entre Log ou LogFinal ? É simples No caso de IgnoreFinal e LogFinal NLog para de processar a cadeia de receptores e não grava nada nos receptores contidos nos links subsequentes.


Conclusão


Analisar o código NLog me ajudou a entender como as regras e os filtros funcionam. Espero que este artigo seja útil para você. Boa sorte

Source: https://habr.com/ru/post/pt450728/


All Articles