Introdução
Esta seção apresenta brevemente o módulo de logging (registro).
This tutorial is from open-source community. Access the source code
Esta seção apresenta brevemente o módulo de logging (registro).
loggingO módulo logging é um módulo da biblioteca padrão para registrar informações de diagnóstico. É também um módulo muito grande com muita funcionalidade sofisticada. Mostraremos um exemplo simples para ilustrar sua utilidade.
Nos exercícios, escrevemos uma função parse() que se parecia com isto:
## fileparse.py
def parse(f, types=None, names=None, delimiter=None):
records = []
for line in f:
line = line.strip()
if not line: continue
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
return records
Concentre-se na instrução try-except. O que você deve fazer no bloco except?
Você deve imprimir uma mensagem de aviso?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("Couldn't parse :", line)
print("Reason :", e)
Ou você ignora silenciosamente?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
pass
Nenhuma das soluções é satisfatória porque você frequentemente quer ambos os comportamentos (selecionáveis pelo usuário).
loggingO módulo logging pode resolver isso.
## fileparse.py
import logging
log = logging.getLogger(__name__)
def parse(f,types=None,names=None,delimiter=None):
...
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
log.warning("Couldn't parse : %s", line)
log.debug("Reason : %s", e)
O código é modificado para emitir mensagens de aviso ou um objeto Logger especial. Aquele criado com logging.getLogger(__name__).
LoggingCrie um objeto logger.
log = logging.getLogger(name) ## name is a string
Emitindo mensagens de log.
log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])
Cada método representa um nível diferente de severidade.
Todos eles criam uma mensagem de log formatada. args é usado com o operador % para criar a mensagem.
logmsg = message % args ## Written to the log
LoggingO comportamento de logging é configurado separadamente.
## main.py
...
if __name__ == '__main__':
import logging
logging.basicConfig(
filename = 'app.log', ## Log output file
level = logging.INFO, ## Output level
)
Tipicamente, esta é uma configuração única na inicialização do programa. A configuração é separada do código que faz as chamadas de logging.
O logging é altamente configurável. Você pode ajustar todos os seus aspectos: arquivos de saída, níveis, formatos de mensagem, etc. No entanto, o código que usa o logging não precisa se preocupar com isso.
logging a um móduloEm fileparse.py, há algum tratamento de erros relacionado a exceções causadas por entrada incorreta. Ele se parece com isto:
## fileparse.py
import csv
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Parse a CSV file into a list of records with type conversion.
'''
if select and not has_headers:
raise RuntimeError('select requires column headers')
rows = csv.reader(lines, delimiter=delimiter)
## Read the file headers (if any)
headers = next(rows) if has_headers else []
## If specific columns have been selected, make indices for filtering and set output columns
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Skip rows with no data
continue
## If specific column indices are selected, pick them out
if select:
row = [ row[index] for index in indices]
## Apply type conversion to the row
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
print(f"Row {rowno}: Couldn't convert {row}")
print(f"Row {rowno}: Reason {e}")
continue
## Make a dictionary or a tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Observe as instruções print que emitem mensagens de diagnóstico. Substituir esses prints por operações de logging é relativamente simples. Altere o código assim:
## fileparse.py
import csv
import logging
log = logging.getLogger(__name__)
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Parse a CSV file into a list of records with type conversion.
'''
if select and not has_headers:
raise RuntimeError('select requires column headers')
rows = csv.reader(lines, delimiter=delimiter)
## Read the file headers (if any)
headers = next(rows) if has_headers else []
## If specific columns have been selected, make indices for filtering and set output columns
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Skip rows with no data
continue
## If specific column indices are selected, pick them out
if select:
row = [ row[index] for index in indices]
## Apply type conversion to the row
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
log.warning("Row %d: Couldn't convert %s", rowno, row)
log.debug("Row %d: Reason %s", rowno, e)
continue
## Make a dictionary or a tuple
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Agora que você fez essas alterações, tente usar um pouco do seu código em dados ruins.
>>> import report
>>> a = report.read_portfolio('missing.csv')
Row 4: Bad row: ['MSFT', '', '51.23']
Row 7: Bad row: ['IBM', '', '70.44']
>>>
Se você não fizer nada, você só receberá mensagens de logging para o nível WARNING e acima. A saída se parecerá com instruções print simples. No entanto, se você configurar o módulo logging, você obterá informações adicionais sobre os níveis de logging, módulo e muito mais. Digite estas etapas para ver isso:
>>> import logging
>>> logging.basicConfig()
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
>>>
Você notará que não vê a saída da operação log.debug(). Digite isto para alterar o nível.
>>> logging.getLogger('fileparse').setLevel(logging.DEBUG)
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Row 4: Bad row: ['MSFT', '', '51.23']
DEBUG:fileparse:Row 4: Reason: invalid literal for int() with base 10: ''
WARNING:fileparse:Row 7: Bad row: ['IBM', '', '70.44']
DEBUG:fileparse:Row 7: Reason: invalid literal for int() with base 10: ''
>>>
Desligue todas, exceto as mensagens de logging mais críticas:
>>> logging.getLogger('fileparse').setLevel(logging.CRITICAL)
>>> a = report.read_portfolio('missing.csv')
>>>
Logging a um ProgramaPara adicionar logging a uma aplicação, você precisa ter algum mecanismo para inicializar o módulo logging no módulo principal. Uma maneira de fazer isso é incluir algum código de configuração que se parece com isto:
## Este arquivo configura a configuração básica do módulo logging.
## Altere as configurações aqui para ajustar a saída de logging conforme necessário.
import logging
logging.basicConfig(
filename = 'app.log', ## Nome do arquivo de log (omita para usar stderr)
filemode = 'w', ## Modo do arquivo (use 'a' para anexar)
level = logging.WARNING, ## Nível de logging (DEBUG, INFO, WARNING, ERROR, ou CRITICAL)
)
Novamente, você precisaria colocar isso em algum lugar nos passos de inicialização do seu programa. Por exemplo, onde você colocaria isso no seu programa report.py?
Parabéns! Você concluiu o laboratório de Logging. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.