Este artigo cobre o básico da utilização do módulo de exploração madeireira padrão que é fornecido com todas as distribuições Python. Depois de ler isto, deverá ser capaz de integrar facilmente o registo na sua aplicação Python.

Módulo de registo padrão da biblioteca

Python vem com um módulo de registo na biblioteca padrão que fornece uma estrutura flexível para a emissão de mensagens de registo a partir de programas Python. Este módulo é amplamente utilizado por bibliotecas e é o primeiro ponto de chegada para a maioria dos programadores quando se trata de registo.

O módulo fornece uma forma de aplicações configurar diferentes manipuladores de registo e uma forma de encaminhar mensagens de registo para estes manipuladores. Isto permite uma configuração altamente flexível que pode lidar com muitos casos de utilização diferentes.

Para emitir uma mensagem de registo, um chamador solicita primeiro um registador nomeado. O nome pode ser utilizado pela aplicação para configurar diferentes regras para diferentes registadores. Este registador pode então ser utilizado para emitir mensagens simplesmente formatadas a diferentes níveis de registo (DEBUG, INFO, ERROR, etc.), que mais uma vez podem ser utilizadas pela aplicação para tratar mensagens de maior prioridade diferente das de menor prioridade. Embora possa parecer complicado, pode ser tão simples como isto:

import logginglog = logging.getLogger("my-logger")log.info("Hello, world")

Internalmente, a mensagem é transformada num objecto LogRecord e encaminhada para um objecto Handler registado para este logger. O manipulador irá então utilizar um Formatter para transformar o LogRecord numa string e emitir essa string.

Felizmente, na maioria das vezes os programadores não têm de estar cientes dos detalhes. A documentação Python contém um excelente artigo sobre o módulo de registo e como tudo funciona em conjunto. O resto deste artigo centrar-se-á nas melhores práticas em vez de todas as formas possíveis de utilização deste módulo.

Níveis de registo

Nem todas as mensagens de registo são criadas iguais. Os níveis de registo estão listados aqui na documentação Python; incluimo-los aqui para referência. Quando se define um nível de registo em Python usando o módulo padrão, está-se a dizer à biblioteca que se pretende tratar de todos os eventos a partir desse nível. Se definir o nível de registo para INFO, este incluirá mensagens INFO, AVISO, ERRO, e CRÍTICO. As mensagens NOTSET e DEBUG não serão incluídas aqui.

Level Valor numérico
CRÍTICO 50
ERROR 40
AVISO 30
INFO 20
DEBUG 10
NOTSET 0

Logging from Modules

Um poçoA aplicação Python organizada é provavelmente composta por muitos módulos. Por vezes estes módulos destinam-se a ser utilizados por outros programas, mas a menos que esteja a desenvolver intencionalmente módulos reutilizáveis dentro da sua aplicação, é provável que esteja a utilizar módulos disponíveis a partir de pypi e módulos que você mesmo escreve especificamente para a sua aplicação.

Em geral, um módulo deve emitir mensagens de registo como uma melhor prática e não deve configurar a forma como essas mensagens são tratadas. Essa é a responsabilidade da aplicação.

A única responsabilidade dos módulos é facilitar à aplicação o encaminhamento das suas mensagens de registo. Por este motivo, é uma convenção para cada módulo utilizar simplesmente um logger nomeado como o próprio módulo. Isto torna mais fácil para a aplicação encaminhar diferentes módulos de forma diferente, mantendo ao mesmo tempo o código de registo no módulo simples. O módulo só precisa de duas linhas para configurar o registo, e depois usar o logger nomeado:

import logginglog = logging.getLogger(__name__)def do_something(): log.debug("Doing something!")

É tudo o que há para ele. Em Python, __name__ contém o nome completo do módulo actual, pelo que este irá simplesmente funcionar em qualquer módulo.

Configurar Registo

A sua aplicação principal deve configurar o subsistema de registo de modo a que as mensagens de registo vão para onde devem. O módulo de registo Python fornece um grande número de formas de afinar isto, mas para quase todas as aplicações, a configuração pode ser muito simples.

Em geral, uma configuração consiste em adicionar um Formatter e um Handler ao registador raiz. Como isto é tão comum, o módulo de registo fornece uma função utilitária chamada basicConfig que trata da maioria dos casos de utilização.

Aplicações devem configurar o registo o mais cedo possível, de preferência como a primeira coisa na aplicação, para que as mensagens de registo não se percam durante o arranque.

Finalmente, as aplicações devem envolver um bloco de tentativa/excepção em torno do código principal da aplicação para enviar quaisquer excepções através da interface de registo em vez de apenas para stderr. Isto é conhecido como um manipulador de tentativa global. Não deve ser onde se lida com todo o seu registo de excepções, deve continuar a planear excepções na tentativa de registo de blocos nos pontos necessários do seu código como regra geral.

Exemplo 1 – Registo na Saída Padrão para Systemd

Esta é a opção mais simples e provavelmente a melhor para configurar o registo nos dias de hoje. Ao utilizar o systemd para executar um daemon, as aplicações podem apenas enviar mensagens de registo para stdout ou stderr e mandar o systemd reencaminhar as mensagens para journald e syslog. Como uma vantagem adicional, isto não requer sequer excepções de captura, uma vez que Python já as escreve a erro padrão. Dito isto, siga a convenção apropriada e trate as suas excepções.

No caso de executar Python em contentores como Docker, o registo na saída padrão é também muitas vezes o movimento mais fácil, uma vez que esta saída pode ser directa e facilmente gerida pelo próprio contentor.

import loggingimport oslogging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))exit(main())

é isso. A aplicação irá agora registar todas as mensagens com nível INFO ou superior para stderr, usando um formato simples:

ERROR:the.module.name:The log message

A aplicação pode até ser configurada para incluir mensagens DEBUG, ou talvez apenas ERROR, definindo a variável de ambiente LOGLEVEL.

O único problema com esta solução é que as excepções são registadas como múltiplas linhas, o que pode causar problemas para análise posterior. Infelizmente, configurar Python para enviar excepções de múltiplas linhas como uma única linha não é tão simples, mas certamente possível. Note a implementação aqui abaixo, chamando logging.exception é a abreviatura equivalente a logging.error(..., exc_info=True).

import loggingimport os class OneLineExceptionFormatter(logging.Formatter): def formatException(self, exc_info): result = super().formatException(exc_info) return repr(result) def format(self, record): result = super().format(record) if record.exc_text: result = result.replace("\n", "") return result handler = logging.StreamHandler()formatter = OneLineExceptionFormatter(logging.BASIC_FORMAT)handler.setFormatter(formatter)root = logging.getLogger()root.setLevel(os.environ.get("LOGLEVEL", "INFO"))root.addHandler(handler) try: exit(main())except Exception: logging.exception("Exception in main(): ") exit(1)

Exemplo 2 – Syslog

A alternativa é enviá-lo directamente para o syslog. Isto é óptimo para sistemas operativos mais antigos que não têm sistemad. Num mundo ideal, isto deveria ser simples, mas infelizmente, Python requer uma configuração um pouco mais elaborada para poder enviar mensagens unicode de registo. Aqui está um exemplo de implementação.

import loggingimport logging.handlersimport os class SyslogBOMFormatter(logging.Formatter): def format(self, record): result = super().format(record) return "ufeff" + result handler = logging.handlers.SysLogHandler('/dev/log')formatter = SyslogBOMFormatter(logging.BASIC_FORMAT)handler.setFormatter(formatter)root = logging.getLogger()root.setLevel(os.environ.get("LOGLEVEL", "INFO"))root.addHandler(handler) try: exit(main())except Exception: logging.exception("Exception in main()") exit(1)

Exemplo 3 – Ficheiro de registo

A opção final é registar mensagens directamente num ficheiro. Isto é raramente útil hoje em dia, uma vez que os administradores podem configurar o syslog para escrever certas mensagens em ficheiros específicos, ou, se for implantado dentro de contentores, este é um anti-padrão. Também se utilizar o registo centralizado, ter de lidar com ficheiros de registo adicionais é uma preocupação adicional. Mas é uma opção que ainda está disponível.

Ao iniciar sessão em ficheiros, o principal a ter cuidado é que os ficheiros de registo precisam de ser rodados regularmente. A aplicação precisa de detectar o ficheiro de registo a ser renomeado e lidar com essa situação. Enquanto Python fornece o seu próprio manipulador de rotação de ficheiros, é melhor deixar a rotação dos ficheiros para ferramentas dedicadas, tais como o logrotate. O WatchedFileHandler irá acompanhar o ficheiro de registo e reabri-lo se for rodado, fazendo-o funcionar bem com o logrotate sem requerer quaisquer sinais específicos.

Existe uma implementação de amostra.

import loggingimport logging.handlersimport os handler = logging.handlers.WatchedFileHandler( os.environ.get("LOGFILE", "/var/log/yourapp.log"))formatter = logging.Formatter(logging.BASIC_FORMAT)handler.setFormatter(formatter)root = logging.getLogger()root.setLevel(os.environ.get("LOGLEVEL", "INFO"))root.addHandler(handler) try: exit(main())except Exception: logging.exception("Exception in main()") exit(1)

Outros Destinos

É possível utilizar outros destinos de log, e certas estruturas fazem uso disto (por exemplo Django pode enviar certas mensagens de registo como e-mail). O HTTPHandler pode ser útil quando está a correr num PaaS e não tem acesso directo ao anfitrião para configurar o syslog ou está atrás de uma firewall que bloqueia o syslog de saída, e pode ser utilizado para registar directamente em sistemas de registo centralizados como SolarWinds® Loggly®.

Muitos dos manipuladores de registo mais elaborados na biblioteca de registo podem facilmente bloquear a aplicação, causando interrupções simplesmente porque a infra-estrutura de registo não respondia. Por estas razões, é melhor manter a configuração de registo de uma aplicação tão simples quanto possível.

Uma tendência crescente no registo em geral é de separá-la o mais possível da funcionalidade principal da sua aplicação. Dessa forma, pode ter comportamentos diferentes em ambientes ou contextos de implantação diferentes. Utilizar o HTTPHandler com um sistema como Loggly é uma forma simples de o conseguir directamente na sua aplicação.

Ao implementar em contentores, tente manter as coisas tão simples quanto possível. Efectue o log out/err e confie no seu anfitrião de contentores ou plataforma de orquestração para tratar de descobrir o que fazer com os logs. Ainda pode utilizar os serviços de centralização de logs, mas com uma abordagem de sidecar ou expedidor de logs.

Se for desejável uma configuração de granulometria fina, o módulo de registo também fornece a capacidade de carregar a configuração de registo a partir de um ficheiro de configuração. Isto é bastante poderoso, mas raramente necessário. Ao carregar a configuração de registo a partir de um ficheiro, especificar disable_existing_loggers=False. O padrão, que existe apenas para compatibilidade retroactiva, desactivará quaisquer registadores criados por módulos. Isto quebra muitos módulos, por isso use isto com precaução.

Sumário

O registo em Python é simples e bem padronizado, graças a uma poderosa estrutura de registo directamente na biblioteca padrão.

Módulos devem simplesmente registar tudo numa instância de registo para o nome do seu módulo. Isto facilita a aplicação de encaminhar mensagens de registo de diferentes módulos para locais diferentes, se necessário.

Aplicações têm então várias opções para configurar o registo. Numa infra-estrutura moderna, no entanto, seguir as melhores práticas simplifica muito isto. A não ser que seja especificamente necessário, basta fazer o registo para stdoutstderr e deixar system ou as suas mensagens de registo de manuseamento de contentores são suficientes e a melhor abordagem.

Ver. Analise-a. Inspeccione-a. Resolva-o

Ver o que interessa.

INICIAR TESTE GRÁTIS

Categorias: Articles

0 comentários

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *