11 Mar, 2014 17:18

Logging no Android com logback

A instalação é bem simples:

  1. Baixe a última versão da logback-android e da slf4j-api;
  2. Configure o comportamento do log através do arquivo ${project-root}/assets/logback.xml ou diretamente via código;
  3. Use a slf4j-api para escrever no log da aplicação.

Exemplo:

package com.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // SLF4J
    Logger log = LoggerFactory.getLogger(MainActivity.class);
    log.info("hello world");
  }
}

No exemplo acima, obtermos uma instância de org.slf4j.Logger através de LoggerFactory.getLogger(this.getClass());. Com essa instância podemos usar os métodos info(...), debug(...), error(...) e warn(...) - semelhante ao que havia no android.util.Log.

Configurando o logback via código

Eu prefiro configurar usando código Java, pois é mais fácil de encontrar erros e descobrir funcionalidades com o code complete da IDE. Se você preferir configurar via XML, pode olhar o site do logback-android.

No exemplo abaixo, o método configureLogger() prepara o logger da seguinte maneira:

  1. Arquivos de log genéricos para todos os logs com nível maiores que DEBUG (LevelFilter filter1), nomeados de acordo com o dia (RollingFileAppender, TimeBasedRollingPolicy);
  2. Saída para o LogCat (LogcatAppender) sem qualquer filtro;
  3. Arquivo de log exclusivo para erros de conexão(FileAppender<ILoggingEvent> conexaoFileAppender) com nível somente DEBUG (LevelFilter filter2).

Com essas configurações, qualquer chamada a um dos métodos de log (ex. logger.info(...)) faz o conteúdo ser escrito automatica e simultaneamente em todos os arquivos de log que atendam os filtros especificados.

Eis o código:

private void configureLogger() {
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    lc.reset();

    PatternLayout.defaultConverterMap.put("user",
            LoggerUserConverter.class.getName());


    /*
        1: Configuração do log genérico rotacionado por dia
    */

    // setup FileAppender
    PatternLayoutEncoder encoder1 = new PatternLayoutEncoder();
    encoder1.setContext(lc);
    encoder1.setPattern("%date %-5level %logger{36}:%line - [%user] %msg%n");
    encoder1.start();

    LevelFilter filter1 = new LevelFilter();
    filter1.setContext(lc);
    filter1.setLevel(Level.DEBUG);
    filter1.setOnMatch(FilterReply.DENY);
    filter1.setOnMismatch(FilterReply.ACCEPT);
    filter1.start();

    String logDirectory = Environment
            .getExternalStoragePublicDirectory("MinhaApp/logs")
            .getAbsolutePath();

    String logFileName = logDirectory + "/minha-app-%d.log.gz";

    RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<ILoggingEvent>();
    fileAppender.setContext(lc);
    fileAppender.setAppend(true);
    fileAppender.addFilter(filter1);

    // setup time based rolling
    TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<ILoggingEvent>();
    rollingPolicy.setContext(lc);
    rollingPolicy.setFileNamePattern(logFileName);
    rollingPolicy.setParent(fileAppender);
    rollingPolicy.setCleanHistoryOnStart(true);
    rollingPolicy.start();

    fileAppender.setRollingPolicy(rollingPolicy);
    fileAppender.setEncoder(encoder1);
    fileAppender.start();


    /*
        2: Configuração de saída para o Logcat
    */

    // setup LogcatAppender
    PatternLayoutEncoder encoder2 = new PatternLayoutEncoder();
    encoder2.setContext(lc);
    encoder2.setPattern("%msg%n");
    encoder2.start();

    LogcatAppender logcatAppender = new LogcatAppender();
    logcatAppender.setContext(lc);
    logcatAppender.setEncoder(encoder2);
    logcatAppender.start();


    /*
        3: Configuração do log de DEBUG para erros de conexão
    */

    // setup FileAppender para erros de conexao
    PatternLayoutEncoder encoder3 = new PatternLayoutEncoder();
    encoder3.setContext(lc);
    encoder3.setPattern("%date %-5level %logger{36}:%line - [%user] %msg%n");
    encoder3.start();

    String logConexaoFileName = logDirectory + "/minha-app-conexoes-errors.log";

    LevelFilter filter2 = new LevelFilter();
    filter2.setContext(lc);
    filter2.setLevel(Level.ERROR);
    filter2.setOnMatch(FilterReply.ACCEPT);
    filter2.setOnMismatch(FilterReply.DENY);
    filter2.start();

    FileAppender<ILoggingEvent> conexaoFileAppender = new FileAppender<ILoggingEvent>();
    conexaoFileAppender.setContext(lc);
    conexaoFileAppender.setAppend(true);
    conexaoFileAppender.setEncoder(encoder3);
    conexaoFileAppender.setFile(logConexaoFileName);
    conexaoFileAppender.addFilter(filter2);
    conexaoFileAppender.start();

    // add the newly created appenders to the root logger;
    // qualify Logger to disambiguate from org.slf4j.Logger
    ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory
            .getLogger(Logger.ROOT_LOGGER_NAME);
    root.addAppender(conexaoFileAppender);
    root.addAppender(fileAppender);
    root.addAppender(logcatAppender);
}

Como pode ver ao final do método, as chamadas consecutivas a root.addAppender(...) registram cada uma das regras de log definidas logo acima.

Além disso, nas primeiras linhas, adicionei um "pattern converter":

PatternLayout.defaultConverterMap.put("user",
            LoggerUserConverter.class.getName());

Esse LoggerUserConverter (abaixo) permite que eu coloque "%user" no pattern dos logs e seja substituído automaticamente pelo nome do usuário que está logado no aplicativo no momento. Eis o código do LoggerUserConverter:

public class LoggerUserConverter extends ClassicConverter {
    @Override
    public String convert(ILoggingEvent arg0) {
        Usuario usuario = MinhaApplication.getUsuario();

        if (usuario != null && usuario.getLogin() != null) {
            return usuario.getLogin();
        }

        return "anonimo";
    }
}

Tentei montar um exemplo mais complexo, mas com recursos usados normalmente quando precisamos de um log mais robusto do que o android.util.Log. Você pode ver mais exemplos a partir do manual do logback.

Ao navegar neste site, você consente o uso de cookies nossos e de terceiros, que coletam informações anônimas e são essenciais para melhorar sua experiência em nosso site.