segunda-feira, setembro 02, 2013

Log4Net

Outro requisito não funcional das minhas novas aplicações, agora em .Net, que eu tive que aprender como implementar foi uma API de log.

E como no post sobre Spring.NET, fui a procura de uma velha conhecida API de log conhecida dos tempos de Java, a Log4J que na sua versão para .Net chamasse Log4Net.

Então criei um projeto do tipo Console Application e com o Manage NuGet Packages adicionei o Log4Net 1.2.11 ao projeto.

A primeira coisa é configurar o App.config. Nele temos, entre outras coisas, que configurar a forma de saída desse log (se em banco de dados, e-mail, arquivo, etc) e o formato (que informações sobre o erro gravar).

Eu configurei o Log4Net para gravar em disco o arquivo de log e guardar as informações de:

Data da ocorrência;
Thead;
Nível da ocorrência;
Onde ocorreu a ocorrência;
Mensagem da ocorrência.


Esse é o arquivo de configuração da minha aplicação (App.config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  
  <log4net>
    <appender name="FileAppender" type="log4net.Appender.FileAppender">
      <file value="C:\\LogginWithLog4net\\log.txt" />
      <appendToFile value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
      </layout>
    </appender>
    
    <root>
      <level value="DEBUG" />
      <appender-ref ref="FileAppender" />
    </root>
  </log4net>
</configuration>


Então criei uma classe para lançar uma exceção qualquer além de ter duas informações também sendo gravadas no log. A minha classe main do projeto (Program.cs) ficou assim:

using System;
using log4net;
using log4net.Config;
using System.Reflection;
 
namespace LogginWithLog4net
{
    class Program
    {
        private static readonly ILog log =
            LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

 
        static void Main(string[] args)
        {
            XmlConfigurator.Configure(
                new System.IO.FileInfo("C:\\LogginWithLog4net\\App.config"));

 
            log.Info("Starting the process.");
 
            try
            {
                throw new Exception("Fatal error");
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message);
                log.Error("Exception: " + e.Message);
                log.Error("Stack trace: " + e.StackTrace);
            }
 
            log.Info("Process terminated.");
 
            Console.ReadKey();
        }
    }
}


Em primeiro lugar eu recupero uma instância do log e atribuo ao campo estático log da classe Program:

private static readonly ILog log =
            LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);



Depois disto feito, dentro do main eu tenho que informar a instância do log onde ele deve buscar as definições e configurações para funcionar como eu quero que ele funcione. Então tenho uma classe XmlConfigurator recebendo o arquivo App.config:

XmlConfigurator.Configure(
                new System.IO.FileInfo("C:\\LogginWithLog4net\\App.config"));


Feito isso estamos prontos para logar qualquer coisa. Estou gravando uma linha de informação (nível INFO) no log: “Starting the process.” e depois lançando uma exceção em um bloco try/catch:

            log.Info("Starting the process.");
 
            try
            {
                throw new Exception("Fatal error");
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message);
                log.Error("Exception: " + e.Message);
                log.Error("Stack trace: " + e.StackTrace);
            }


Essa exceção está sendo tratada no catch onde eu estou mostrando no console da aplicação a mensagem do erro recuperada da exceção lançada e logo depois eu gravo essa mesma informação no log junto com o stack trace completo da exceção.

A saída no arquivo de log ficou:

2013-09-02 11:37:54,890 [10] INFO  LogginWithLog4net.Program - Starting the process.
2013-09-02 11:37:54,937 [10] ERROR LogginWithLog4net.Program - Exception: Fatal error
2013-09-02 11:37:54,937 [10] ERROR LogginWithLog4net.Program -
   Stack trace: em LogginWithLog4net.Program.Main(String[] args)
                na C:\LogginWithLog4net\Program.cs:linha 20

2013-09-02 11:37:54,953 [10] INFO  LogginWithLog4net.Program - Process terminated.

E explorando as outras formas de gravar os logs ainda coloquei para gravar em banco de dados essas mesmas informações.

O nível de log está configurado para DEBUG, ou seja, vai gravar tudo. Claro que na hora de ir para produção o projeto deve filtrar por um nível mais alto, como WARM, que só exibirá o que estiver além de INFO. Ou seja, o que for warm do framework .Net e das demais libraries inclusas no projeto e tudo que for erro que acontecer na execução do projeto.

Nenhum comentário: