Log sua aplicação

Solli M. Honório
Publicado em 01/01/2010

Log sua aplicação

Log é um assunto largamente neglicenciado em todos os níveis responsável pelo design e desenvolvimento da aplicação. São raras as aplicações com sistema de log consistente e eficaz. Geralmente, as que possuem algum tipo de log, os possuem para sanar alguma necessidade específica do desenvolvedor (normalmente para auxiliar na depuração do código), e utiliza soluções caseiras.

A falta de planejamento para um sistema de log consistente, disperdiça uma excelente oportunidade de utilizar uma ferramenta que trará a aplicação mas segurança (através da auditoria) e estabilidade (através de monitoramento do comportamento da aplição).

O objetivo deste artigo é incentivar o planejamento de log na aplicação e a utilização de um framework em detrimento de qualquer solução caseira.

Desafios para um sistema de log

Ao habilitar um log na aplicação, estaremos gerando uma quantidade considerável (quando não enorme) de dados que dificilmente será consumido por algum ser humano com frequência. Normalmente os maiores consumidores de log são os sysadmin, e somente o faz durante a análize de algum problema na aplicação.

O primeiro desafio de um sistema de log consistente na aplicação é encontrar o verdadeiro consumidor (ou consumidor primário) dos logs. Por mais estranho que possa parecer num primeiro instante, o ambiente ideial é aquele que utiliza sistema de monitoramento e análise de log como consumidor primário dos logs de uma aplicação.

Estes sistemas de monitoramento e análise, quando bem configurados e aplicados, permite transformar dados em informações (tal como o log do apache em estatisticas de acesso) para usuários finais, dados em alerta de segurança e de anormalidade do funcionamento dos sistemas para os analistas de segurança e sysadmins.

E o quê é mais importante para um sistema de monitoramento e análise de log ? A padronização da mensagem. Estes sistemas só conseguem extrair informações úteis de logs padronizados.

Analisando uma solução de log caseira

Normalmente a habilitação de log na aplicação é uma iniciativa solitária do desenvolvendor e para consumo próprio, optando por um solução caseira, tal como no código abaixo:

    sub log {
        my $msg = shift;

        open my $log, '>>', $log_file or die q/Couldn't open file/;

        print $log $msg;

    }

    ...

    sub foo {

        ...

        log($error_msg);

        ...

    }

Este tipo de solução, suficiente ao propósito exclusivo de um desenvolvendor, apresentará sérias restrições para o crescimento da aplicação. Com dito no FAQ do Log::Log4perl, "... após o básico, novas funções serão requisitadas. Será necessário atribuir horário às mensagens. As mensagens deverão ser escritas em arquivo ou na tela ... restringir o log a apenas alguma parte do sistema", entre outras funções.

A melhor alternativa a um sistema caseira de log é a utilização de um framework especializada nesta atividade, e o Log::Log4perl é a opção mais recomenda atualmente no mundo Perl.

Algumas das vantagens na utilização do Log::Log4perl são :

* Configuração centralizada e flexível

Pelo arquivo de configuração, ou pelo código de inicialização do programa, é possível personalizar e definir cada aspecto do sistema de log. Definir formatação e o nível de detalhe da mensagem, ativar/desativar logs.

* Vasta, e em expanção, lista de plugin para appender

É possível definir para qual mídia será direcionado o log apenas habilitando/desabilitando o plugin do appender. Desta maneira é possível enviar o log para um banco de dados ou para algum email.

* Otimizações

A inclusão de um sistema de log invariávelmente adicionará alguma penalidade no código, já que estamos incluíndo mais código e verificações. O Log::Log4perl está otimizado para permitir o máximo de desempenho tanto com os logs habilitados, ou não (mais informações na documentação do Log::Log4perl, na seção Penalties).

Utilizando Log::Log4perl na aplicação

Uma das principais vantagens do Log::Log4perl sobre a maioria (senão a totalidade) dos sistemas caseiro é o controle centralizado das ações de todo o sistemas de log. Este controle ocorre no arquivo de configuração onde é possível definir o formato e o nível de detalhamento das informações no log; para onde o log será direcionado; políticas de retenção dos logs; etc.

A documentção do Log::Log4perl é vasta e detalhada sobre como habilitar e configurar todas estas facilidades do Log::Log4perl, por isto não entraremos neste detalhes, mas apenas para demostração segue um exemplo de como ficaria uma aplicação com o Log::Log4perl.

Abaixo temmos um exemplo de arquivo de configuração que utiliza um plugin para rotacionar o arquivo de log.




    log4perl.rootLogger=ERROR, Logfile

    log4perl.appender.Logfile             = Log::Dispatch::FileRotate
    log4perl.appender.Logfile.filename    = /var/log/myapp/error.log
    log4perl.appender.Logfile.max         = 7
    log4perl.appender.Logfile.DatePattern = yyyy-MM-dd

    log4perl.appender.Logfile.layout                   = PatternLayout
    log4perl.appender.Logfile.layout.ConversionPattern = [%d] %F %L %C - %m%n

Já neste ponto é possível observar a flexibilidade do Log::Log4perl, na primeira linha acima, eu consigo definir o nível log que está habilitado (neste caso somente severidade igual ou maior que ERROR). Na linha seguinte definimos o appender que será utilizado e log em seguida as configurações referente ao appender. Fica claro neste ponto o quanto é fácil alterar para onde o log será direcionado. No PatterLayout você define as informações que fará parte da linha de mensagem, neste caso estou incluindo um timestamp, o nome do arquivo, a linha e a classe onde a mensagem foi gerada, além da mensagem fornecida.

Este arquivo de configuração dever ser iniciado no teu código da seguinte maneira:

    use Log::Log4perl;
    Log::Log4perl->init(q[/etc/myapp/log.conf]);

E em algum ponto do seu código, você pode carregar o Log::Log4perl e reportar o desejado sem preocupar-se sobre qual será o destino da mensagem enviada, da seguinte maneira.

    package My::App::Foo;
    use Log::Log4perl;

    sub method {
        my @params = @_;

        my $log = Log::Log4perl->get_logger("My::App::Foo");

        $log->error("Error message");

        ...
    }




Log::Log4perl no Catalyst

Habilitar o Log::Log4perl no Catalyst é menos complicado, utilizando o mesmo arquivo de configuração acima, carregamos o módulo e a configuração no módulo de startup do sistema.

    use Log::Log4perl::Catalyst;

    # agora inicializamos o Log4perl para ser utilizada no Catalyst.
    __PACKAGE__->log(Log::Log4perl::Catalyst->new(q[/etc/myapp/log.conf]);

Agora é só reportar as informações desejada, tal como :

    sub login : Action {
        my ( $self, $c ) = @_;
        my $params = $c->req->body_params;
        if ( $params->{'username'} && $params->{'password'} ) {
            if ( $c->authenticate({ map { $_ => $params->{$_} } qw/username password/ }) ) {
                $c->log->info(qq[Success login for user $params->{'username'} ]);
                # usuário válido e autenticado
                # redirecione para algum caminho da sua aplicação
            } else {
                $c->log->warn(qq[Invalid access attempt login for user $params->{'username'} ]);
                # mostrar a página com o formulário de autenticação e uma mensagem de erro
            }
        } else {
            # mostrar a página com o formulário de autenticação
        }
    }

    sub requires_user : Action {
        my ( $self, $c ) = @_;
        if ( $c->user_exists ) {
            # usuário "logado"
        } else {
            $c->log->warn(qq[Invalid access.])
            # retorna ao passo de login
        }
    }

Agora você foi informado da necessidade de obter o ip do cliente em todas as mensagens por questão de auditoria, o que fazer ? Alterar cada linha de código que reporta ao log e incluir o $c->request->address() nas mensagens ?

Não para quem estiver utilizando o Log::Log4perl, neste caso vamos atualizar o arquivo de configuração com a informação necessária e automaticamente passar a obter o ip do cliente no formato desejado, da seguinte maneira :

    log4perl.rootLogger=TRACER, Logfile

    log4perl.appender.Logfile             = Log::Dispatch::FileRotate
    log4perl.appender.Logfile.filename    = /var/log/myapp/error.log
    log4perl.appender.Logfile.max         = 7
    log4perl.appender.Logfile.DatePattern = yyyy-MM-dd

    log4perl.appender.Logfile.layout                   = PatternLayout
    log4perl.appender.Logfile.layout.ConversionPattern = [%d] %F %L %C %X{client_ip}- %m%n

E atualize no módulo de startup do sistema o MDC para capturar o ip do cliente

    use Log::Log4perl::Catalyst;

    # agora inicializamos o Log4perl para ser utilizada no Catalyst.
    __PACKAGE__->log(Log::Log4perl::Catalyst->new(q[/etc/myapp/log.conf]);

    Log::Log4perl::MDC->put( "client_ip", $c->req->address() );




CONCLUSÃO

A utilização do Log::Log4perl acrescenta robustez e flexibilidade ao aplicativo, sem penalizar o desenvolvedor com codificação extra e disvirtuada a lógica do negócio. É na simplicidade que encontramos o maior poder do Log::Log4perl, pois desarma qualquer argumento contra a adoção de um sistema de log no aplicativo.

AUTHOR

Solli M. Honório <shonorio at gmail.com>, sysadmin e atualmente trabalha na Ética Tecnologia Ltda. http://www.etica.net

blog comments powered by Disqus