Registro do NodeJS facilitado


Quantas vezes você logger.info('ServiceName.methodName.') E logger.info('ServiceName.methodName -> done.') Para cada método do seu serviço que você deseja registrar? Deseja que ele seja automatizado e tenha a mesma assinatura constante em todo o aplicativo? Se é assim, somos muito parecidos, sofremos a mesma dor muitas vezes e agora podemos finalmente tentar resolvê-la. Juntos. Senhoras e senhores, deixe-me apresentar ... registrador de classe !


"O porquê" do class-logger


Engenheiros geralmente são perfeccionistas. Perfeccionistas ao extremo. Nós gostamos de abstrações legais. Nós gostamos de código limpo. Vemos beleza em linguagens artificiais que outras pessoas nem conseguem ler. Gostamos de fabricar pequenos universos digitais, vivendo de acordo com as regras que estabelecemos. Provavelmente gostamos de tudo isso, porque somos muito preguiçosos. Não, não temos medo do trabalho, mas odiamos fazer qualquer trabalho que possa ser automatizado.


Depois de escrever algumas milhares de linhas de código de registro, geralmente criamos certos padrões, padronizando o que queremos registrar. Ainda temos que aplicar esses padrões manualmente. Portanto, a idéia central do criador de classes é fornecer uma maneira padronizada declarativa e altamente configurável de registrar mensagens antes e depois da execução de um método de classe.


Início rápido


Vamos começar a correr e ver como é o código real.


 import { LogClass, Log } from 'class-logger' @LogClass() class ServiceCats { @Log() eat(food: string) { return 'purr' } } 

Este serviço registrará três vezes:


  • Na sua criação, com uma lista de argumentos transmitidos ao construtor.
  • Antes de eat é executado com uma lista de seus argumentos.
  • Depois de eat é executado com uma lista de seus argumentos e seu resultado.

Em palavras de código:


 // Logs before the actual call to the constructor // `ServiceCats.construct. Args: [].` const serviceCats = new ServiceCats() // Logs before the actual call to `eat` // `ServiceCats.eat. Args: [milk].` serviceCats.eat('milk') // Logs after the actual call to `eat` // `ServiceCats.eat -> done. Args: [milk]. Res: purr.` 

Demonstração ao vivo .


O que mais poderíamos registrar? Aqui está a lista completa de eventos:


  • Antes da construção da aula.
  • Antes de métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.
  • Após métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.
  • Erros de métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.

Propriedade funcional é uma função de seta atribuída a uma propriedade ( class ServiceCats { private meow = () => null } ).

Ajustando-o às nossas necessidades


Até aí tudo bem, mas nos foi prometido "personalizável", certo? Então, como podemos ajustá-lo?


O class-logger fornece três camadas de configuração hierárquica:


  • Global
  • Class
  • Método

A cada chamada de método, todos os três são avaliados e mesclados de cima para baixo. Há alguma configuração global padrão sã, para que você possa usar a biblioteca sem nenhuma configuração.


Configuração global


É a configuração em todo o aplicativo. Pode ser definido com a chamada setConfig .


 import { setConfig } from 'class-logger' setConfig({ log: console.info, }) 

Configuração da classe


Isso afeta todos os métodos da sua classe. Pode substituir a configuração global.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // It overrides global config for this service log: console.debug, }) class ServiceCats {} 

Configuração do método


Afeta apenas o próprio método. Substitui a configuração da classe e, portanto, a configuração global.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // It overrides global config for this service log: console.debug, }) class ServiceCats { private energy = 100 @Log({ // It overrides class config for this method only log: console.warn, }) eat(food: string) { return 'purr' } // This method stil uses `console.debug` provided by class config sleep() { this.energy += 100 } } 

Demonstração ao vivo


Opções de configuração


Bem, aprendemos como alterar os padrões, mas não faria mal cobrir o que há para configurar, não é?


Aqui você pode encontrar muitas substituições úteis de configuração .

Aqui está o link para a interface do objeto de configuração, caso você fale TypeScript melhor que o inglês :)

O objeto de configuração possui estas propriedades:


registro


É uma função que efetua o log real da mensagem final formatada. É usado para registrar estes eventos:


  • Antes da construção da aula.
  • Antes de métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.
  • Após métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.

Padrão: console.log


logError


É uma função que faz o log real da mensagem de erro final formatada. É usado para registrar este e único evento:


  • Erros de métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.

Padrão: console.error


formatador


É um objeto com dois métodos: start e end . Ele formata os dados de log na sequência final.


start formata mensagens para esses eventos:


  • Antes da construção da aula.
  • Antes de métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.

end formata mensagens para esses eventos:


  • Após métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.
  • Erros de métodos estáticos e não estáticos síncronos e assíncronos e propriedades funcionais.

Padrão: new ClassLoggerFormatterService()


incluir


A configuração do que deve ser incluído na mensagem.


args

Pode ser um objeto booleano ou.


Se for booleano, ele define se deve incluir a lista de argumentos (lembre-se de que Args: [milk] ?) Em ambos, mensagens de início (antes da construção e antes da chamada do método) e final (após a chamada do método, chamada do método de erro).


Se for um objeto, ele deve ter duas propriedades booleanas: start e end . start inclui / exclui a lista de argumentos para mensagens de início; end faz o mesmo para mensagens de fim.


Padrão: true


construir

Um sinalizador booleano que define se a construção de classe deve ser registrada ou não.


Padrão: true


resultado

Outra configuração de sinalizador booleano deve incluir um valor de retorno de uma chamada de método ou um erro gerado por ela. Lembre-se de Res: purr ? Se você definir esse sinalizador como false não haverá Res: purr .


Padrão: true


classInstance

Mais uma vez, um booleano ou um objeto.
Se você habilitá-lo, uma representação estrita da sua instância de classe será adicionada aos logs. Em outras palavras, se sua instância de classe tiver algumas propriedades, elas serão convertidas em uma sequência JSON e adicionadas à mensagem de log.


Nem todas as propriedades serão adicionadas. O registrador de classes segue esta lógica:


  • Pegue propriedades próprias (sem protótipo) de uma instância.
    • Porque É um caso raro quando o seu protótipo é alterado dinamicamente, portanto, dificilmente faz sentido registrá-lo.
  • Solte qualquer um deles que tenha o tipo de function .
    • Porque Na maioria das vezes function propriedades das function são apenas funções de seta imutáveis ​​usadas em vez de métodos de classe regulares para preservar this contexto. Não faz muito sentido inchar seus logs com corpos estritos dessas funções.
  • Solte qualquer um deles que não seja um objeto simples.
    • Quais objetos são simples? ClassLoggerFormatterService considera um objeto um objeto simples se seu protótipo for estritamente igual a Object.prototype .
    • Porque Frequentemente, incluímos instâncias de outras classes como propriedades (as injetamos como dependências). Nossos logs se tornariam extremamente gordos se incluíssemos versões estritas dessas dependências.
  • Stringify o que resta.

 class ServiceA {} @LogClass({ include: { classInstance: true, }, }) class Test { private serviceA = new ServiceA() private prop1 = 42 private prop2 = { test: 42 } private method1 = () => null @Log() public method2() { return 42 } } // Logs to the console before the class' construction: // 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' const test = new Test() // Logs to the console before the method call: // 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' test.method2() // Logs to the console after the method call: // 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.' 

Padrão: false


Assumindo o controle sobre a formatação


E se você gostar da ideia geral, mas quiser que suas mensagens tenham uma aparência diferente? Você pode assumir o controle completo da formatação passando seu próprio formatador personalizado.


Você pode escrever seu próprio formatador a partir do zero. Totalmente. No entanto, não abordaremos esta opção aqui (se você estiver realmente interessado nisso, dirija-se à seção "Formatação" do README).


A coisa mais rápida e, provavelmente, mais fácil de fazer é subclassificar um formatador padrão ClassLoggerFormatterService - ClassLoggerFormatterService .


ClassLoggerFormatterService possui estes métodos protegidos, servindo como blocos de construção da mensagem final:


  • base
    • Retorna o nome da classe com o nome do método. Exemplo: ServiceCats.eat .
  • operation
    • Retorna -> done ou -> error base em se foi uma execução bem-sucedida de um método ou um erro.
  • args
    • Retorna uma lista estrita de argumentos. Exemplo :. . Args: [milk] . Ele usa o fast-safe-stringify para objetos sob o capô.
  • classInstance
    • Retorna uma instância de classe com string. Exemplo :. . Class instance: {"prop1":42,"prop2":{"test":42}} . Se você optar por incluir a instância da classe, mas ela não estiver disponível (é assim para métodos estáticos e construção de classes), ela retornará N/A
  • result
    • Retorna um resultado estrito da execução (mesmo que tenha sido um erro). Usa fast-safe-stringify para serializar objetos. Um erro estrito será composto das seguintes propriedades:
    • Nome da classe (função) com a qual o erro foi criado ( error.constructor.name ).
    • Código do erro ( error.code ).
    • Mensagem de erro (mensagem de erro).
    • Nome do erro ( error.name ).
    • Rastreio de pilha ( error.stack ).
  • final
    • Devoluções . Apenas . .

A mensagem start consiste em:


  • base
  • args
  • classInstance
  • final

A mensagem end consiste em:


  • base
  • operation
  • args
  • classInstance
  • result
  • final

Você pode substituir qualquer um desses métodos de blocos de construção. Vamos dar uma olhada em como podemos adicionar um carimbo de data / hora. Não estou dizendo que deveríamos. pino , winston e muitos outros registradores são capazes de adicionar registros de data e hora por conta própria. Portanto, o exemplo é puramente educativo.


 import { ClassLoggerFormatterService, IClassLoggerFormatterStartData, setConfig, } from 'class-logger' class ClassLoggerTimestampFormatterService extends ClassLoggerFormatterService { protected base(data: IClassLoggerFormatterStartData) { const baseSuper = super.base(data) const timestamp = Date.now() const baseWithTimestamp = `${timestamp}:${baseSuper}` return baseWithTimestamp } } setConfig({ formatter: new ClassLoggerTimestampFormatterService(), }) 

Demonstração ao vivo


Conclusão


Por favor, não esqueça de seguir as etapas de instalação e se familiarizar com os requisitos antes de decidir usar esta biblioteca.


Felizmente, você encontrou algo útil para o seu projeto. Sinta-se livre para me comunicar seus comentários! Certamente aprecio qualquer crítica e pergunta.

Source: https://habr.com/ru/post/pt447358/


All Articles