18 pontos por hongminhee 2025-06-23 | 7 comentários | Compartilhar no WhatsApp

Biblioteca vs aplicação: requisitos de logging fundamentalmente diferentes

  • Logging de aplicação: configuração e gerenciamento explícitos em um ambiente controlado diretamente pelo desenvolvedor
  • Logging de biblioteca: incluído em projetos de terceiros, exigindo respeito ao ambiente e ao poder de escolha do usuário
  • Limites das abordagens existentes: ao aplicar loggers centrados em aplicações (winston, Pino) a bibliotecas, surge o problema da imposição
  • O dilema de quem cria bibliotecas: fornecer informações de depuração vs não impor ônus ao usuário

Problemas atuais do logging em bibliotecas

  • Ecossistema de logging fragmentado: Express usa DEBUG=express:*, Mongoose usa mongoose.set('debug', true) etc., cada um com uma abordagem diferente
  • Dilema das dependências: ao usar bibliotecas centradas em aplicações como winston e Pino, impõem-se ao usuário dependências e configurações indesejadas
  • Silêncio vs imposição: a escolha fica entre abrir mão do logging por completo ou forçar um método de logging ao usuário
  • Complexidade da injeção de dependência: é uma abordagem mais sofisticada, mas aumenta a complexidade da API e o peso para o usuário

A filosofia "biblioteca em primeiro lugar" do LogTape

  • Ativação condicional: se o logging não estiver configurado, não faz absolutamente nada; quando configurado, é gerenciado de forma integrada
  • Garantia de escolha para o usuário: a biblioteca não impõe um método de logging e só é ativado quando o usuário quiser
  • Zero dependências: tamanho de 5.3KB, eliminando riscos de segurança na cadeia de suprimentos e evitando conflitos de versão
  • Suporte completo a ESM/CJS: resolve problemas na cadeia de compatibilidade e otimiza bundles com tree shaking

Vantagens práticas

  • Otimização de desempenho: overhead quase zero quando desativado, e excelente desempenho de saída no console quando ativado
  • Separação por namespace: categorias hierárquicas no formato ["my-lib", "feature"] evitam conflitos
  • Projeto com foco em TypeScript: oferece segurança total de tipos sem pacotes de tipos adicionais
  • Integração com sistemas existentes: suporte à adoção gradual por meio de adaptadores para winston e Pino

Considerações realistas

  • O significado dos adaptadores: reconhece que ainda não existe um padrão de ecossistema e propõe um compromisso prático
  • Inspiração no ecossistema Python: referência ao caso de sucesso do Python, unificado pela biblioteca padrão logging
  • Abordagem orientada ao futuro: apresentada como uma opção para a melhoria gradual do ecossistema de bibliotecas

7 comentários

 
sunrabbit 2025-06-25

Não entendo muito bem como isso seria “inoperante” quando não há configuração.
Ao chamar getLogger, o logger já é criado e, se você emitir um debug, ele funciona.

Pelo que vi no código, isso só faz parecer que não está funcionando,
e também não adia as operações com string,
então, sinceramente, não entendo em que esse “inoperante” é diferente de outras bibliotecas que simplesmente não exibem nada quando você define o nível de log.

 
hongminhee 2025-06-25

Ué, os logs estão sendo emitidos mesmo sem chamar configure()/configureSync()? Onde eles estão sendo emitidos? Estão sendo exibidos no console?

 
sunrabbit 2025-06-26

Ah, o que eu quis dizer aqui com funcionar não é que o log seja salvo no console ou em um arquivo, mas sim se a função é executada e se isso de fato gera overhead.

Acho que isso pode ter causado um mal-entendido

 
sunrabbit 2025-06-26

Claro, considerando que o principal overhead do logger é a system call, dá para dizer que não é sem overhead,
mas dá para dizer que isso é um diferencial em relação a outros loggers? Nesse ponto, não, porque os outros loggers também funcionam da mesma forma.

 
hongminhee 2025-06-26

Ah, entendi o que você quis dizer. Primeiro, quando rodei um benchmark usando null output como referência, pareceu que praticamente não há overhead. Mas, mais do que o overhead de desempenho, achei que o mais importante era se o comportamento padrão é no-op ou não. Do ponto de vista de quem desenvolve a biblioteca, mesmo que ela gere logs internamente, seria problemático se, ao executar a aplicação que usa essa biblioteca, os logs fossem parar arbitrariamente no console ou em um arquivo.

 
sunrabbit 2025-06-26

Ah, era SHOW GN.
Acho que parte do motivo de eu não ter me identificado é que, hoje em dia, o ecossistema costuma escolher muito uma forma em que o logger é injetado de fora.
Se não estiver configurado, naturalmente não funciona.
Mesmo assim, como também é uma interface de logger que não existia nesse ecossistema até agora, e tem bastante liberdade, acho que acaba sendo melhor.

No caso do benchmark que você passou, como ele produz null output excluindo o system call,
acho que essa parte realmente pode variar bastante dependendo do formato do logger interno.

Nesse ponto, ele chega a ter uma diferença de até 3x em relação ao Pino. Caramba.


FYI: além disso, a forma de logger injetado de fora que eu mencionei pode ser vista facilmente só olhando o Openai Node sdk, já que ele recebe o logger externamente e faz a saída dessa forma.