Introducción
Esta sección presenta brevemente el módulo de registro.
This tutorial is from open-source community. Access the source code
💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí
Esta sección presenta brevemente el módulo de registro.
El módulo logging
es un módulo de la biblioteca estándar para registrar información de diagnóstico. También es un módulo muy grande con mucha funcionalidad sofisticada. Mostraremos un ejemplo simple para ilustrar su utilidad.
En los ejercicios, escribimos una función parse()
que se parecía a esto:
## 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("No se pudo analizar :", line)
print("Razón :", e)
return records
Presta atención a la instrucción try-except
. ¿Qué debes hacer en el bloque except
?
¿Debes imprimir un mensaje de advertencia?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
print("No se pudo analizar :", line)
print("Razón :", e)
¿O la ignoras en silencio?
try:
records.append(split(line,types,names,delimiter))
except ValueError as e:
pass
Ninguna de las soluciones es satisfactoria porque a menudo se desea ambas conductas (seleccionables por el usuario).
El módulo logging
puede resolver esto.
## 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("No se pudo analizar : %s", line)
log.debug("Razón : %s", e)
El código se modifica para emitir mensajes de advertencia o un objeto Logger
especial. El creado con logging.getLogger(__name__)
.
Crea un objeto de registro.
log = logging.getLogger(name) ## name es una cadena
Emisión de mensajes de registro.
log.critical(message [, args])
log.error(message [, args])
log.warning(message [, args])
log.info(message [, args])
log.debug(message [, args])
Cada método representa un nivel diferente de gravedad.
Todos ellos crean un mensaje de registro formateado. args
se utiliza con el operador %
para crear el mensaje.
logmsg = message % args ## Escrito en el registro
El comportamiento del registro se configura por separado.
## main.py
...
if __name__ == '__main__':
import logging
logging.basicConfig(
filename = 'app.log', ## Archivo de salida del registro
level = logging.INFO, ## Nivel de salida
)
Por lo general, esta es una configuración única al inicio del programa. La configuración es independiente del código que hace las llamadas de registro.
El registro es altamente configurable. Puedes ajustar cada aspecto de él: archivos de salida, niveles, formatos de mensaje, etc. Sin embargo, el código que utiliza el registro no tiene que preocuparse por eso.
En fileparse.py
, hay un manejo de errores relacionado con las excepciones causadas por entradas incorrectas. Se ve así:
## fileparse.py
import csv
def parse_csv(lines, select=None, types=None, has_headers=True, delimiter=',', silence_errors=False):
'''
Analiza un archivo CSV en una lista de registros con conversión de tipo.
'''
if select and not has_headers:
raise RuntimeError('select requiere encabezados de columna')
rows = csv.reader(lines, delimiter=delimiter)
## Lee los encabezados del archivo (si los hay)
headers = next(rows) if has_headers else []
## Si se han seleccionado columnas específicas, crea índices para filtrado y establece columnas de salida
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Omite filas sin datos
continue
## Si se seleccionan índices de columna específicos, escoge ellos
if select:
row = [ row[index] for index in indices]
## Aplica la conversión de tipo a la fila
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
print(f"Fila {rowno}: No se pudo convertir {row}")
print(f"Fila {rowno}: Razón {e}")
continue
## Crea un diccionario o una tupla
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Observa las declaraciones print
que emiten mensajes de diagnóstico. Reemplazar esas impresiones con operaciones de registro es relativamente sencillo. Cambia el código de la siguiente manera:
## 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):
'''
Analiza un archivo CSV en una lista de registros con conversión de tipo.
'''
if select and not has_headers:
raise RuntimeError('select requiere encabezados de columna')
rows = csv.reader(lines, delimiter=delimiter)
## Lee los encabezados del archivo (si los hay)
headers = next(rows) if has_headers else []
## Si se han seleccionado columnas específicas, crea índices para filtrado y establece columnas de salida
if select:
indices = [ headers.index(colname) for colname in select ]
headers = select
records = []
for rowno, row in enumerate(rows, 1):
if not row: ## Omite filas sin datos
continue
## Si se seleccionan índices de columna específicos, escoge ellos
if select:
row = [ row[index] for index in indices]
## Aplica la conversión de tipo a la fila
if types:
try:
row = [func(val) for func, val in zip(types, row)]
except ValueError as e:
if not silence_errors:
log.warning("Fila %d: No se pudo convertir %s", rowno, row)
log.debug("Fila %d: Razón %s", rowno, e)
continue
## Crea un diccionario o una tupla
if headers:
record = dict(zip(headers, row))
else:
record = tuple(row)
records.append(record)
return records
Ahora que has hecho estos cambios, intenta usar un poco de tu código con datos incorrectos.
>>> import report
>>> a = report.read_portfolio('missing.csv')
Fila 4: Fila incorrecta: ['MSFT', '', '51.23']
Fila 7: Fila incorrecta: ['IBM', '', '70.44']
>>>
Si no haces nada, solo obtendrás mensajes de registro para el nivel WARNING
y superior. La salida se verá como declaraciones print
simples. Sin embargo, si configuras el módulo de registro, obtendrás información adicional sobre los niveles de registro, el módulo y más. Escribe estos pasos para ver eso:
>>> import logging
>>> logging.basicConfig()
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Fila 4: Fila incorrecta: ['MSFT', '', '51.23']
WARNING:fileparse:Fila 7: Fila incorrecta: ['IBM', '', '70.44']
>>>
Notarás que no ves la salida de la operación log.debug()
. Escribe esto para cambiar el nivel.
>>> logging.getLogger('fileparse').setLevel(logging.DEBUG)
>>> a = report.read_portfolio('missing.csv')
WARNING:fileparse:Fila 4: Fila incorrecta: ['MSFT', '', '51.23']
DEBUG:fileparse:Fila 4: Razón: literal no válido para int() con base 10: ''
WARNING:fileparse:Fila 7: Fila incorrecta: ['IBM', '', '70.44']
DEBUG:fileparse:Fila 7: Razón: literal no válido para int() con base 10: ''
>>>
Desactiva todos, excepto los mensajes de registro más críticos:
>>> logging.getLogger('fileparse').setLevel(logging.CRITICAL)
>>> a = report.read_portfolio('missing.csv')
>>>
Para agregar registro a una aplicación, necesitas tener algún mecanismo para inicializar el módulo de registro en el módulo principal. Una forma de hacer esto es incluir un código de configuración que se ve como esto:
## Este archivo configura la configuración básica del módulo de registro.
## Cambia las configuraciones aquí para ajustar la salida del registro según sea necesario.
import logging
logging.basicConfig(
filename = 'app.log', ## Nombre del archivo de registro (omite para usar stderr)
filemode = 'w', ## Modo de archivo (usa 'a' para adjuntar)
level = logging.WARNING, ## Nivel de registro (DEBUG, INFO, WARNING, ERROR o CRITICAL)
)
Nuevamente, necesitarías poner esto en algún lugar de los pasos de inicio de tu programa. Por ejemplo, ¿dónde pondrías esto en tu programa report.py
?
¡Felicitaciones! Has completado el laboratorio de Registro. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.