Introducción al módulo de registro

PythonPythonBeginner
Practicar Ahora

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í

Introducción

Esta sección presenta brevemente el módulo de registro.

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.

Excepciones revisadas

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).

Usando registro

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__).

Bases del registro

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

Configuración del 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.

Comentarios

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.

Ejercicio 8.2: Agregar registro a un módulo

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')
>>>

Ejercicio 8.3: Agregar registro a un programa

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?

✨ Revisar Solución y Practicar

Resumen

¡Felicitaciones! Has completado el laboratorio de Registro. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.