Você não gosta do Log4J? Eu tb não! Leia para entender o porquê

Nunca consegui entender o Log4J. Por que toda vez que eu preciso logar alguma coisa eu tenho que ir lá em cima da minha classe e colocar:

private static Logger logger = Logger.getLogger(MyclassName.class);

Por que eu tenho que passar uma classe para o meu logger? O que isso significa? Vários logs por classe? Por que a coisa não pode ser simples?

Por que eu preciso de um arquivo de configuracão? Onde eu coloco esse arquivo? Por que quando eu mudo o nível nesse arquivo nada acontece? Será que ele está pegando outro arquivo? Ou minha configuracão está errada?

O que é APPENDER? Será que eu preciso saber o que é um appender para trabalhar com o Log4J? E layout? E root logger? Log da raiz?

O que é isso:

#define the console appender
log4j.appender.consoleAppender = org.apache.log4j.ConsoleAppender
 
# now define the layout for the appender
log4j.appender.consoleAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
 
# now map our console appender as a root logger, means all log messages will go to this appender
log4j.rootLogger = DEBUG, consoleAppender

Mas eu só quero logar. Será que preciso saber sobre appenders, layouts, root logger, para fazer um log?

Claro que serei voto vencido, mas eu sempre achei o Log4J um bom exemplo de como transformar algo simples em complicado.

Então deixo a pergunta aqui? O que vc gosta do Log4J? Vc acha ele simples de usar? O que poderia ser mais simples e menos complicado?

Há tempos tenho uma biblioteca de logging baseada em anos de observacoes. Estou lancando o projeto aqui: http://mentalog.soliveirajr.com

Abaixo um pequeno exemplo. Qual é a opinião do pessoal sobre as vantagens e desvantagens da abordagem abaixo sobre a abordagem do Log4J.

// MentaLog - Non-intrusive, fast, garbage-less, colored and straightforward logging

// To use MentaLog all you have to do is:
import static org.mentalog.Log.*; // and that's it

// Now you can start logging
Warn.log("This is a log message!", "user =", user.getUsername(), "age =", user.getAge());

// You will get the following log written to System.out
21:11:17.263-WARN This is a log message! user = saoj age = 15

// By default, MentaLog logs to the console, but you can easily change that with:
Log.setConsole(false); // or Log.setFile(true);
                                 // or -DlogConsole=false
                                 // or -DlogFile=true

// Now if you log again, your log goes to a file Warn.log
Warn.log("This is a log message!", "user =", user.getUsername(), "age =", user.getAge());

// You will get a file Warn.log with the following contents:
LOG OPENED - 19/09/2011 21:15:42.627
21:15:42.630 This is a log message! user = saoj age = 15

// When you think it is time to roll, simply call roll() and your log file will be rolled:
Warn.roll();

// Let's go back to console
Log.setConsole(true);

// And use some colors now:
Log.setColors(true);

Fatal.log("This is a log message!", "user =", user.getUsername(), "age =", user.getAge());

// if you terminal supports colors, you should see the message below in red:
21:15:42.630-FATAL This is a log message! user = saoj age = 15

// if you see some weird characters in the beginning of the message, that means you terminal does NOT support colors, so turn it off
Log.setColors(false);

// To change the log level, simply do:
Log.setLevel(Debug); // or -DlogLevel=Debug

// To change the color of a level, simply do:
Warn.setColor(32); // or -DwarnColor=32

// To change the log directory of your log files, simply do:
Log.setDir("/var/log/myLogs"); // or -DlogDir=/var/log/myLogs

// To enable/disable any individual level, *bypassing* the current log level, simply do:
Warn.enable(true); // no matter what the level is, it will be logged
Warn.enable(false); // not matter what the level is, it will NOT be logged
Warn.enable(null); // please follow the log level

// To create any logger so you can use for anything, simply do:
Logger myLogger = Log.createLogger("myDir" /* log dir */, "Audit" /* filename */, true /* isSynchronized */);

// and just log as before:
myLogger.log("This is a log message!", "user =", user.getUsername(), "age =", user.getAge());

// will print in the file myDir/Audit.log
21:15:42.630 This is a log message! user = saoj age = 15

// and roll
myLogger.roll();

// and close when you are done:
myLogger.close();

// and remember, your log levels are enumerations, so you can pass then around as arguments:
public void doSomethingWithMyLog(Log level); // only accepts a logger that is a level
public void doSomethingWithMyLog(Logger logger); // accepts any logger: a level one or one you created

// and you can do something like this:
for(Log level: Log.values()) {
   level.log("Hi");
}

// or
Log level = Log.valueOf("Warn");

Sim eu gosto do Log4j, ele ajuda a identificar erros na fase de desenvolvimento.

att.

A tua idéia, saoj, é muito boa. Porém devo alertar que existem vários frameworks de logging que fazem trabalho semelhante.

Com o Java Logging (JUL) você pode configurar programaticamente assim como você fez. A vantagem é não precisar de nada que não tenha no JDK, e ele é muito extensível. Ponto negativo: ele é leeento e tem alguns problemas de performance.

Log4j é uma implementação bem madura, inicialmente implementada pelo Gülcü quando ele estava na Apache. Ele tem vários xmls e properties para flexibilizar a configuração, e para alguns isso é um problema, para outros não. Ele tem uma performance melhor que do JUL, mas atualmente não é o mais rápido. Também dá para configura-lo programaticamente. A vantagem é uma série de appenders já prontos para N coisas, tem até appender para envio de log por email (bizarro sim, mas quem sabe alguém precise). E o melhor: ele tem async log, que para ambientes criticos é fundamental.

Logback para mim é uma das melhores implementações de logging. Ele é feito pelo Gülcü e possui uma configuração bem simples. Em alguns testes que fiz é a mais rápida das implementações de logging, e possui async log. Ele não possui aqueles milhares de appenders como o Log4j, mas se você pensar normalmente precisamos apenas de appenders simples em disco.

Mas você teve uma ótima idéia: usar parametros para evitar concatenar Strings. E é para isso, e outras coisas mais, que existe o SLF4J, que é uma Façade para Logging. Ele trabalha com parametros parecidos com tua idéia, porém ele não faz logging, ele apenas faz o dispatch para a API de logging que você escolher, e ele se integra com praticamente todas APIs de logging existentes. www.slf4j.org

Abraços, e boa sorte com o projeto.

Desculpe o flood, esqueci de falar algo:

Você precisa ter coidado com esses métodos estáticos. Isso porque assim você perde o controle mais fino quando você trabalha com muitas aplicações no mesmo appserver. O pessoal da Apache resolveu isso usando o MDC, e nesse caso você não cria um atributo estático, mas uma instância transiente na classe (transiente para evitar serialização).

O fato de fazer um getLogger(Class.class) é para que você possa ligar ou desligar o logging de determinadas coisas. Assim eu posso em produção deixar o logging de todo para OFF e minha aplicação como WARN, ou algo assim.

Outra vantagem no JUL: em tempo de execução você pode alterar o nível de logging. Muito útil quando você quer fazer um rápido analise em produção.

Boa noite,

Sempre tópicos interessantes. Este é mais um.

Também nunca gostei da forma como alguns framework’s tratam a parte de configuração e estou começando a achar que o assunto que você sempre aborda (“configuração programática”) se encaixe como uma luva em algumas situações. O caso do log acho que seria um deles. Se a gente se perguntar porque tenho que configurar e saber tanta
coisa pra fazer um simples log chegamos a conclusão que realmente é muito complicado usar o Log4J. Fora que ainda ele pode encontrar perdido no classpath um arquivo
de configuração e ai as coisas não saem como a gente imagina. O aspecto dele fazer log por classe é legal o chato é como configurar isso nas propriedades para que ele mostre
o log da classe que vc quer. Pior ainda é quando vc nem sabe quais classes o framework adicionou o log, ai nesse caso o log só serve mesmo pra quem desenvolveu o framework.
Por conta da complexidade que muitos framework’s acabando trazendo é que a curva de aprendizado java se torna grande. Para cada assunto que vamos estudar
precisamos de um livro e de muitas horas de pesquisa.

Assim que der um tempo vou testar o seu framework.

Mesmo que seja voto vencido acho legal você continuar a postar estes questionamentos.

Edson Martins

De forma geral sou a favor da simplicidade.

Mas como atuo também com produtos de terceiros, tenho algumas considerações:

  • Poder mudar o nível de log via um arquivo de configurações muitas vezes é uma mão na roda.
    Pode te ajudar a identificar um problema quando não tem o código-fonte disponível (usando produtos dos outros é uma situação comum).

  • Mandar logs por email também é interessante quando tem um batch rodando num servidor remoto que ninguém se dá o trabalho de ver.

Obrigado pelas excelentes dicas garcia-jj. Muita coisa que vc falou eu não sabia.

O MentaLog busca documentar e exemplificar o meu ponto de vista de como a coisa poderia ser abordada. Resumidamente:

    :arrow: Não-intrusivo, ou seja, se quer logar apenas logue sem precisar fazer qualquer outra coisa além de logar.
    :arrow: Bem rápido, sem alocar memória e sem gerar garbage para o GC, minimizando overhead e latência. Num máquina razoável loga um milhão de linhas em 11 microsegundos por linha, sem alocar nenhuma memória. Resultados aqui.
    :arrow: Uma API bem simples e intuitiva.

Configuração Programática te permite fazer um XML, Annotations, Properties, Configuração Genética, etc. Agora se vc tem somente XML ou Annotations vc está preso a eles. Cuidado com a Annotationmania. :slight_smile:

Lembrando também que o MentaLog suporta configuração via -DlogLevel=warn -DlogFile=blah -DlogDir=blah. Claro que tem default pra tudo isso, então vc só configura se precisar/quiser, via command line ou programaticamente.

Lembrando também que a sua configuração programática, ou seja sua classe, pode estar independente da sua aplicação/jar. Então vc faz assim: -DlogConfiguration=com.myapp.MyLogConfiguration, ou seja, vc pode passar quantas configurações programáticas quiser para dentro da sua aplicaçao. Se não quiser ter que compilar, pode usar jython, jruby ou beanshell para passar um script com a configuração sem precisar compilar. (O MentaLog ainda não suporta isso, mas e plenamente possível)

Imagino que ele só envia um log Alert ou Fatal para o email do cara, não Debug. :slight_smile: O MentaLog também possui interceptors com os quais os cara pode interceptar o bytebuffer com o log e fazer o que ele quiser, enviar para syslog, email, etc. Mas é claro ainda não tem nenhuma implementação, mas a fundação está lá. Mais info aqui.

Isso é muito bom mesmo. É o único thread extra que me permito no meu sistema crítico, para tratar a latência de disk I/O. Sabe de algum benchmark com os tempos de log para esses outros logs?

Tem gente que prefere:

Warn.log("Minha mensagem!", "Meu o1 =", o1, "Meu o2 =", o2);

Outros preferem:

Warn.log("Minha mensagem! Meu o1 = {} Meu o2 = {}", o1, o2);

Dá para agradar a ambos aqui, suportando ambos. O que tem que tomar cuidado é com a geração de garbage nesses parâmetros. O MentaLog faz overloading de varargs, para não gerar arrays, e usa Encoders, para não ter que chamar toString() nos objetos passados ali. Alocar memória e/ou soltá-la para o GC não é permitido. Outro problema é inboxing. Se vc passa um primitivo vai alocar um wrapper. Dá para tentar resolver isso com alguns wrappers mutáveis e penso em tentar algo para as próximas versões.

Realmente. Mas as vantagens de ter isso como estático são muito grandes, nem que para isso ele não suporte várias aplicações dentro da mesma VM. Na verdade os logs do MentaLog são enumerations que implementam uma interface comum chamada Logger. Há uma instancia de Logger dentro deles, mas também cai nesse problema que vc falou. Acho que daria para hackear para tentar solucionar esse caso, mas melhor não comprometer a coisa para atender esse caso específico. Quando vc tenta solucionar todos os problemas, acaba não solucionando nenhum de maneira simplificada.

Pelo que entendi (*Rufino me explicou), mas posso estar equivocado, vc pode ativar o seu log apenas para determinadas classes. Isso é realmente legal e útil. Prentendo tentar implementar, mas só se for não-intrusivo, ou seja, vou tentar pegar o stack-trace e compará-lo com as classes que foram ativadas para saber se devo logar aquilo ou não. O stacktrace deve me falar em que local (classe e linha) o log foi chamado, pelo menos espero.

Isso o MentaLog tb suporta. Vc pode inclusive ligar e desligar um nível específico, bypassando o nível configurado. Se quiser voltar a usar o nível configurado vc faz Warn.enable(null);

Não li ainda… vou ler depois, porque tenho que estudar para uma prova, mas… cara, você não gosta de nada? Já imagino o tópico “Você não gosta do Java? Eu tb não! Leia para entender o porquê…”.

Ha ha ha - você não percebeu que o Saoj (Sergio Oliveira) é na verdade alguém que sempre tenta desafiar os conceitos estabelecidos?

Você pode tentar fazer isso como ele (ou seja, usando alguma coisa até se tornar expert nela, e tentar fazer algo que não tenha todos os problemas que há na coisa original, e seja mais prática - como é o caso do pacote de log que ele criou) ou então como ele mesmo (ou seja, criando alguma coisa nova, como o Mentawai), ou então como alguém que estuda alguma outra linguagem (como Scheme, Erlang ou Occam), entende os paradigmas dessa linguagem. e cria algo que use os paradigmas dela (como o cara que criou o Scala).

Se ele não se importasse com o Java não estaria participando do GUJ ou então escrevendo esses pacotes.

Gosto muito de Java, pode ficar tranquilo em relação a isso. :slight_smile:

Acho que esse esquema de logging da versão 1.0.4 ficou bem legal! Alguma sugestão / crítica ?

// The basic and default
Warn.log(obj1, obj2, obj3);

// MentaLog will write each object separated by a space, for example:
Warn.log("good", "morning", "america");

// prints
"good morning america"

That can be useful to print the values of variables, for example;

Debug.log("Something happened here!", "pos =", pos, "start =", theStart, "end =", theEnd);

// assuming your variables are integers, that will print:
"Something happened here! pos = 3 start = 3 end = 5

Actually, the space above between the equal sign (’=’) and its value bothers me. :slight_smile: So you can call Log.setNoSpaceAfterEqualSign(true) to get rid of it.

Debug.log("Something happened here!", "pos=", pos, "start=", theStart, "end=", theEnd);

// now give:
"Something happened here! pos=3 start=3 end=5

You can also turn off the space between objects all together, but calling Log.setNoSpaceBetweenObjects(true). I don’t like that, because now I have to deal with all spacing myself:

Warn.log("good", "morning", "america");

// prints
"goodmorningamerica"

And you can use [color=blue]placeholders[/color]:

Debug.log("Something happened here! pos={} start={} end={}", pos, theStart, theEnd);

// prints
"Something happened here! pos=3 start=3 end=5

[/quote]