Pela primeira vez na minha vida, me encontro em uma posição em que estou escrevendo uma API Java que será de código aberto. Espero que seja incluído em muitos outros projetos.
Para o registro, eu (e de fato as pessoas com quem trabalho) sempre usei o JUL (java.util.logging) e nunca tive problemas com ele. No entanto, agora eu preciso entender com mais detalhes o que devo fazer para o desenvolvimento da minha API. Eu fiz algumas pesquisas sobre isso e com as informações que tenho, fico mais confuso. Daí este post.
Desde que eu venho de JUL, estou inclinado a isso. Meu conhecimento do resto não é tão grande.
A partir da pesquisa que fiz, descobri estas razões pelas quais as pessoas não gostam do JUL:
"Comecei a desenvolver em Java muito antes da Sun lançar o JUL e era mais fácil continuar com o logging-framework-X do que aprender algo novo" . Hmm. Não estou brincando, é isso que as pessoas dizem. Com esse argumento, todos nós poderíamos estar fazendo COBOL. (no entanto, eu certamente posso me relacionar com esse ser um cara preguiçoso)
"Não gosto dos nomes dos níveis de registro em JUL" . Ok, sério, isso não é motivo suficiente para introduzir uma nova dependência.
"Não gosto do formato padrão da saída de JUL" . Hmm. Isso é apenas configuração. Você nem precisa fazer nada em termos de código. (verdade, nos velhos tempos, você pode ter que criar sua própria classe Formatter para fazer a coisa certa).
"Eu uso outras bibliotecas que também usam o logging-framework-X, então achei mais fácil usar essa" . Este é um argumento circular, não é? Por que 'todo mundo' usa o logging-framework-X e não o JUL?
"Todo mundo está usando o logging-framework-X" . Isso para mim é apenas um caso especial do exposto acima. A maioria nem sempre está certa.
Então a grande questão é: por que não JUL? . Do que sinto falta? A razão de ser das fachadas de log (SLF4J, JCL) é que várias implementações de log existem historicamente e a razão disso realmente remonta à era anterior a JUL, como eu a vejo. Se JUL fosse perfeito, as fachadas de madeira não existiriam, ou o quê? Para tornar as coisas mais confusas, o JUL é, de certa forma, uma fachada, permitindo que os manipuladores, formatadores e até o LogManager sejam trocados.
Em vez de adotar várias maneiras de fazer a mesma coisa (registro), não deveríamos questionar por que elas eram necessárias em primeiro lugar? (e veja se esses motivos ainda existem)
Ok, minha pesquisa até agora levou a algumas coisas que eu posso ver que podem ser problemas reais com JUL:
Desempenho . Alguns dizem que o desempenho no SLF4J é superior ao resto. Parece-me um caso de otimização prematura. Se você precisar registrar centenas de megabytes por segundo, não tenho certeza se você está no caminho certo. O JUL também evoluiu e os testes que você fez no Java 1.4 podem não ser mais verdadeiros. Você pode ler sobre isso aqui e esta correção chegou ao Java 7. Muitos também falam sobre a sobrecarga da concatenação de strings nos métodos de log. No entanto, o log baseado em modelo evita esse custo e ele também existe em JUL. Pessoalmente, eu nunca escrevo logs baseados em modelos. Com preguiça de fazer isso. Por exemplo, se eu fizer isso com JUL:
log.finest("Lookup request from username=" + username + ", valueX=" + valueX + ", valueY=" + valueY));
meu IDE me avisará e pedirá permissão para alterá-lo para:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", new Object[]{username, valueX, valueY});
.. o que eu obviamente aceitarei. Permissão garantida ! Obrigado pela ajuda.
Então, eu realmente não escrevo essas declarações pessoalmente, isso é feito pelo IDE.
Concluindo sobre a questão do desempenho, não encontrei nada que sugerisse que o desempenho do JUL não seja bom em comparação com a concorrência.
Configuração do caminho de classe . O JUL pronto para o uso não pode carregar um arquivo de configuração do caminho de classe. São algumas linhas de código para fazê-lo. Eu posso ver por que isso pode ser irritante, mas a solução é curta e simples.
Disponibilidade de manipuladores de saída . O JUL vem com 5 manipuladores de saída prontos para uso: console, fluxo de arquivos, soquete e memória. Estes podem ser estendidos ou novos podem ser escritos. Por exemplo, isso pode estar gravando no Syslog do UNIX / Linux e no Log de Eventos do Windows. Pessoalmente, nunca tive esse requisito nem o vi usado, mas certamente posso me relacionar com o porquê de ser um recurso útil. O Logback vem com um aplicativo para o Syslog, por exemplo. Ainda assim, eu argumentaria que
- 99,5% das necessidades de destinos de saída são cobertas pelo que está em JUL pronto para uso.
- Necessidades especiais podem ser atendidas por manipuladores personalizados em cima de JUL em vez de em cima de outra coisa. Não há nada que sugira que demore mais tempo para escrever um manipulador de saída Syslog para JUL do que para outra estrutura de log.
Estou realmente preocupado que haja algo que eu tenha esquecido. O uso de fachadas de log e implementações de log diferentes de JUL é tão difundido que tenho que chegar à conclusão de que sou eu quem simplesmente não entende. Receio que não seja a primeira vez. :-)
Então, o que devo fazer com a minha API? Eu quero que seja bem sucedido. É claro que posso simplesmente "seguir o fluxo" e implementar o SLF4J (que parece o mais popular hoje em dia), mas, por mim mesmo, ainda preciso entender exatamente o que há de errado com o JUL de hoje que justifica toda essa confusão. Vou me sabotar escolhendo JUL para minha biblioteca?
Teste de desempenho
(seção adicionada por nolan600 em 07-JUL-2012)
Há uma referência abaixo de Ceki sobre a parametrização do SLF4J ser 10 vezes ou mais rápida que a JUL. Então eu comecei a fazer alguns testes simples. À primeira vista, a afirmação está certamente correta. Aqui estão os resultados preliminares (mas continue a ler!):
- Tempo de execução SLF4J, back-end Logback: 1515
- Tempo de execução SLF4J, back-end JUL: 12938
- Tempo de execução JUL: 16911
Os números acima são ms, portanto menos é melhor. Então, 10 vezes a diferença de desempenho é, na verdade, bem próxima. Minha reação inicial: Isso é muito!
Aqui está o núcleo do teste. Como pode ser visto, um número inteiro e uma string são construídos em um loop, que é usado na instrução de log:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(Eu queria que a instrução de log tivesse um tipo de dados primitivo (neste caso, um int) e um tipo de dados mais complexo (nesse caso, uma String). Não tenho certeza se isso importa, mas você o possui.)
A instrução de log para SLF4J:
logger.info("Logging {} and {} ", i, someString);
A instrução de log para JUL:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
A JVM foi 'aquecida' com o mesmo teste executado uma vez antes da medição real ter sido feita. O Java 1.7.03 foi usado no Windows 7. As versões mais recentes do SLF4J (v1.6.6) e Logback (v1.0.6) foram usadas. Stdout e stderr foram redirecionados para o dispositivo nulo.
No entanto, com cuidado agora, verifica-se que JUL está gastando a maior parte do tempo getSourceClassName()
porque JUL, por padrão, imprime o nome da classe de origem na saída, enquanto o Logback não. Então, estamos comparando maçãs e laranjas. Eu tenho que fazer o teste novamente e configurar as implementações de log de maneira semelhante para que elas realmente produzam as mesmas coisas. No entanto, desconfio que o SLF4J + Logback ainda esteja no topo, mas longe dos números iniciais, como indicado acima. Fique ligado.
Btw: O teste foi a primeira vez que trabalhei com SLF4J ou Logback. Uma experiência agradável. JUL é certamente muito menos acolhedor quando você está começando.
Testando o desempenho (parte 2)
(seção adicionada por nolan600 em 08-JUL-2012)
Como se vê, não importa muito para o desempenho como você configura seu padrão em JUL, ou seja, se inclui ou não o nome da fonte. Eu tentei com um padrão muito simples:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
e isso não mudou os horários acima. Meu criador de perfil revelou que o criador de logs ainda passava muito tempo em chamadas, getSourceClassName()
mesmo que isso não fizesse parte do meu padrão. O padrão não importa.
Portanto, estou concluindo sobre a questão do desempenho que, pelo menos para a declaração de log baseada em modelo testada, parece haver um fator de aproximadamente 10 na diferença real de desempenho entre JUL (lento) e SLF4J + Logback (rápido). Assim como Ceki disse.
Também posso ver outra coisa, a saber, que a getLogger()
ligação do SLF4J é muito mais cara que a da JUL. (95 ms vs 0,3 ms se meu criador de perfil for preciso). Isso faz sentido. O SLF4J precisa dedicar algum tempo à ligação da implementação de log subjacente. Isso não me assusta. Essas chamadas devem ser um pouco raras durante a vida útil de um aplicativo. A rapidez deve estar nas chamadas de log reais.
Conclusão final
(seção adicionada por nolan600 em 08-JUL-2012)
Obrigado por todas as suas respostas. Ao contrário do que pensei inicialmente, decidi usar o SLF4J para minha API. Isso é baseado em várias coisas e na sua entrada:
Ele oferece flexibilidade para escolher a implementação do log no momento da implantação.
Problemas com falta de flexibilidade da configuração do JUL quando executados dentro de um servidor de aplicativos.
O SLF4J é certamente muito mais rápido, conforme detalhado acima, principalmente se você o associar ao Logback. Mesmo que este fosse apenas um teste aproximado, tenho motivos para acreditar que muito mais esforço foi feito na otimização no SLF4J + Logback do que no JUL.
Documentação. A documentação do SLF4J é simplesmente muito mais abrangente e precisa.
Flexibilidade de padrões. Ao fazer os testes, decidi imitar JUL o padrão padrão do Logback. Esse padrão inclui o nome do encadeamento. Acontece que o JUL não pode fazer isso imediatamente. Ok, não perdi até agora, mas acho que não deve faltar em uma estrutura de log. Período!
Muitos (ou muitos) projetos Java hoje usam o Maven, portanto, adicionar uma dependência não é algo tão importante, especialmente se essa dependência é bastante estável, ou seja, não muda constantemente sua API. Isso parece ser verdade para o SLF4J. Além disso, o frasco e os amigos do SLF4J são pequenos.
Então o estranho que aconteceu foi que eu fiquei bastante chateado com o JUL depois de trabalhar um pouco com o SLF4J. Ainda me arrependo de que tenha sido assim com JUL. JUL está longe de ser perfeito, mas meio que faz o trabalho. Apenas não muito bem o suficiente. O mesmo pode ser dito Properties
como exemplo, mas não pensamos em abstrair que as pessoas possam conectar sua própria biblioteca de configuração e o que você possui. Eu acho que a razão é que Properties
aparece logo acima da barra, enquanto o oposto é verdadeiro para JUL de hoje ... e no passado ele chegou a zero porque não existia.
InternalLoggerFactory.java
.java.lang.System.Logger
, que é uma interface que pode ser redirecionada para qualquer estrutura de log real que você desejar, desde que essa estrutura seja atualizada e forneça uma implementação dessa interface. Combinado com a modularização, você pode até implementar um aplicativo com um JRE em pacote que não contenhajava.util.logging
, se preferir uma estrutura diferente.Respostas:
Isenção de responsabilidade : Eu sou o fundador dos projetos log4j, SLF4J e logback.
Existem razões objetivas para preferir o SLF4J. Por um lado, o SLF4J permite ao usuário final a liberdade de escolher a estrutura de log subjacente . Além disso, os usuários mais experientes tendem a preferir o logback, que oferece recursos além do log4j , com julho ficando para trás. O jul em termos de recursos pode ser suficiente para alguns usuários, mas para muitos outros simplesmente não é. Em poucas palavras, se o registro for importante para você, você poderá usar o SLF4J com o logback como a implementação subjacente. Se o registro não for importante, jul está bem.
No entanto, como desenvolvedor de sistemas operacionais, você precisa levar em consideração as preferências de seus usuários e não apenas as suas. Segue-se que você deve adotar SLF4J não porque você está convencido de que SLF4J é melhor do que julho, mas porque a maioria dos desenvolvedores Java atualmente (Julho de 2012) preferem SLF4J como sua API de registro. Se, em última análise, você decidir não se importar com a opinião popular, considere os seguintes fatos:
Assim, manter "fatos concretos" acima da opinião pública, embora aparentemente corajoso, é uma falácia lógica nesse caso.
Se ainda não estiver convencido, JB Nizet apresenta um argumento adicional e potente:
Se, por qualquer motivo, você detestar a API do SLF4J e utilizá-la, acabará com a diversão de seu trabalho, e, por todos os meios, julgue . Afinal, existem maneiras de redirecionar jul para o SLF4J .
A propósito, a parametrização de julho é pelo menos 10 vezes mais lenta que o SLF4J, o que acaba fazendo uma diferença notável.
fonte
java.util.logging
foi introduzido no Java 1.4. Antes, havia usos para o log, é por isso que existem muitas outras APIs de log. Essas APIs eram usadas muito antes do Java 1.4 e, portanto, tinham uma grande participação no mercado que não caiu para 0 quando o 1.4 foi lançado.O JUL não começou muito bem, muitas das coisas que você mencionou foram muito piores no 1.4 e só melhoraram no 1.5 (e acho que no 6 também, mas não tenho muita certeza).
O JUL não é adequado para vários aplicativos com configurações diferentes na mesma JVM (pense em vários aplicativos da web que não devem interagir). O Tomcat precisa passar por alguns obstáculos para fazê-lo funcionar (efetivamente reimplementando o JUL, se eu entendi direito).
Você nem sempre pode influenciar qual estrutura de log suas bibliotecas usam. Portanto, o uso do SLF4J (que na verdade é apenas uma camada de API muito fina acima de outras bibliotecas) ajuda a manter uma imagem um pouco consistente de todo o mundo do log (para que você possa decidir a estrutura de log subjacente enquanto ainda mantém o log da biblioteca no mesmo sistema).
Bibliotecas não podem mudar facilmente. Se uma versão anterior de uma biblioteca costumava usar a biblioteca de log-X, ela não pode facilmente mudar para a biblioteca de log-Y (por exemplo, JUL), mesmo que a última seja claramente superiosa: qualquer usuário dessa biblioteca precisaria aprender a nova estrutura de log e (pelo menos) reconfigure seu log. É um grande não-não, especialmente quando não traz ganho aparente para a maioria das pessoas.
Dito tudo isso, acho que o JUL é pelo menos uma alternativa válida para outras estruturas de registro atualmente.
fonte
IMHO, a principal vantagem do uso de uma fachada de registro como o slf4j é que você permite que o usuário final da biblioteca escolha qual implementação de registro concreto ele deseja, em vez de impor sua escolha ao usuário final.
Talvez ele tenha investido tempo e dinheiro no Log4j ou LogBack (formatadores especiais, anexadores, etc.) e prefere continuar usando o Log4j ou o LogBack, em vez de configurar jul. Não tem problema: o slf4j permite isso. É uma escolha sábia usar o Log4j em julho? Talvez talvez não. Mas você não se importa. Deixe o usuário final escolher o que ele prefere.
fonte
Comecei, como você suspeito, a usar o JUL porque era o mais fácil de começar imediatamente. Ao longo dos anos, porém, passei a desejar ter passado um pouco mais de tempo escolhendo.
Meu principal problema agora é que temos uma quantidade substancial de código de 'biblioteca' que é usado em muitos aplicativos e todos eles usam JUL. Sempre que eu uso essas ferramentas em um aplicativo do tipo serviço da Web, o registro desaparece ou vai para algum lugar imprevisível ou estranho.
Nossa solução foi adicionar uma fachada ao código da biblioteca, o que significava que as chamadas de log da biblioteca não foram alteradas, mas foram redirecionadas dinamicamente para qualquer mecanismo de log disponível. Quando incluídos em uma ferramenta POJO, eles são direcionados para JUL, mas quando implantados como um aplicativo Web, são redirecionados para o LogBack.
Lamentamos, é claro, que o código da biblioteca não use o log parametrizado, mas agora ele pode ser adaptado conforme e quando necessário.
Usamos o slf4j para construir a fachada.
fonte
Eu executei jul contra slf4j-1.7.21 sobre logback-1.1.7, saída para um SSD, Java 1.8, Win64
jul correu 48449 ms, logback 27185 ms para um loop de 1M.
Ainda assim, um pouco mais de velocidade e uma API um pouco melhor não valem 3 bibliotecas e 800K para mim.
e
fonte
logger.info()
. Então você está deliberadamente prejudicando o desempenho do jul para compensar uma falha na interface do slf4j. Em vez disso, você deve codificar os dois métodos da maneira que eles são codificados em idioma.