---
title: "Configura el logging de Python con los niveles de log correctos y captura de excepciones"
description: "Python logging solo imprime WARNING y superiores hasta que lo configuras. Aquí van la configuración en 3 líneas, los 5 niveles y cómo registrar una excepción con su traceback, en Python 3.13."
canonical: "https://logbrew.co/es/guides/python-logging"
last_updated: "2026-11-10"
---

# Configura el logging de Python con los niveles de log correctos y captura de excepciones

Python `logging` solo imprime WARNING y superiores hasta que lo configuras. Aquí van la configuración en 3 líneas, los 5 niveles y cómo registrar una excepción con su traceback, en Python 3.13.

Probado el 10 de noviembre de 2026 con Python 3.13.5 y logbrew-sdk 0.1.29

Por [Furkan Erday](https://github.com/furkanerday). Furkan Erday es el fundador de LogBrew. Desarrolla LogBrew con Claude Code y Codex todos los días.

Canonical: https://logbrew.co/es/guides/python-logging

## Qué hace cada nivel de log de Python sin configurar nada, y en LogBrew

| Llamada de logging | Impreso sin configurar | Nivel propio en LogBrew |
| --- | --- | --- |
| `log.debug()` | Descartado | Fusionado en `info` |
| `log.info()` | Descartado | `info` |
| `log.warning()` | `WARNING:root:` | `warning` |
| `log.error()` | `ERROR:root:` | `error` |
| `log.critical()` | `CRITICAL:root:` | `critical` |

5 llamadas al logger raíz sin configuración, y luego las mismas 5 a través del handler de LogBrew. Una taza llena significa que se imprimió, o que se conservó como nivel propio.

## Configura el logging en 3 líneas

### Llama a basicConfig() una vez, antes de la primera llamada de log

`basicConfig()` fija el nivel y el formato de línea del logger raíz. Sin configuración, Python solo imprime WARNING y superiores, como `WARNING:root:keg below 10%`, y descarta DEBUG e INFO.

```bash
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
log = logging.getLogger("taproom")

log.debug("keg pressure read")
log.info("tap opened")
log.warning("keg below 10%")
log.error("keg empty")
log.critical("cooler offline")
```

Salida

```text
2026-11-10 08:34:04,828 INFO taproom tap opened
2026-11-10 08:34:04,828 WARNING taproom keg below 10%
2026-11-10 08:34:04,828 ERROR taproom keg empty
2026-11-10 08:34:04,828 CRITICAL taproom cooler offline
```

### Nombra un logger por cada módulo

`logging.getLogger(__name__)` da a cada módulo su propio logger, así cada línea nombra el módulo del que viene. Reserva el logger raíz solo para `basicConfig()`.

### Lee el nivel del entorno

`basicConfig()` acepta el nombre del nivel como texto. Ejecuta esto con `LOG_LEVEL=DEBUG` y la línea de debug aparece, sin cambiar código.

```bash
import logging
import os

logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))
log = logging.getLogger("taproom")
log.debug("keg pressure read")
log.info("tap opened")
```

Salida

```text
DEBUG:taproom:keg pressure read
INFO:taproom:tap opened
```

### Pasa force=True para configurar de nuevo

Python ignora una segunda llamada a `basicConfig()` cuando el logger raíz ya tiene un handler. Pasa `force=True` para reemplazar los handlers, por ejemplo en las pruebas.

```bash
import logging

logging.basicConfig(level=logging.WARNING)
logging.basicConfig(level=logging.DEBUG)
logging.debug("second basicConfig: not printed")
logging.basicConfig(level=logging.DEBUG, force=True)
logging.debug("basicConfig with force=True: printed")
```

Salida

```text
DEBUG:root:basicConfig with force=True: printed
```

## Registra cada excepción con su traceback

### Llama a log.exception() dentro de except

`log.exception()` registra en ERROR y añade el traceback. Pasa `exc_info=True` a una llamada de cualquier otro nivel para hacer lo mismo ahí.

```bash
import logging

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s %(message)s")
log = logging.getLogger("taproom")
taps = {"ipa": 12}

def pour(beer):
    return taps[beer]

try:
    pour("stout")
except KeyError:
    log.exception("pour failed")
```

Salida

```text
ERROR taproom pour failed
Traceback (most recent call last):
  File "app.py", line 11, in <module>
    pour("stout")
    ~~~~^^^^^^^^^
  File "app.py", line 8, in pour
    return taps[beer]
           ~~~~^^^^^^
KeyError: 'stout'
```

### Envía cada línea a LogBrew con el handler de logging

Añade `LogBrewLoggingHandler` a tu logger. Cada registro se convierte en un log de LogBrew con su nivel, nombre de logger, archivo y línea. El nombre y el mensaje de la excepción van incluidos, igual que los valores de `extra={}`.

```bash
import logging
import os

from logbrew_sdk import HttpTransport, LogBrewClient, LogBrewLoggingHandler

client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"])
transport = HttpTransport()
log = logging.getLogger("taproom")
log.addHandler(LogBrewLoggingHandler(client, transport, metadata={"service": "taproom"}))
log.setLevel(logging.INFO)

taps = {"ipa": 12}

def pour(beer):
    return taps[beer]

try:
    pour("stout")
except KeyError:
    log.exception("pour failed", extra={"order_id": "ord_42"})

response = client.flush(transport)
print("HTTP", response.status_code, "accepted", response.accepted_events, "logs")
```

Salida

```text
HTTP 200 accepted 1 logs
```

### Lee el log de vuelta con sus campos de excepción

`logbrew read logs --severity error --json` devuelve la línea con `exceptionName`, `exceptionMessage`, el archivo, la función, el número de línea y tu `order_id`.

```bash
logbrew read logs --project <project_id> --severity error --since 1h --json | jq '.[0] | {level, message, logger: .attributes.logger, metadata: .attributes.metadata}'
```

Salida

```text
{
  "level": "error",
  "message": "pour failed",
  "logger": "taproom",
  "metadata": {
    "exceptionMessage": "'stout'",
    "exceptionName": "KeyError",
    "fileName": "send.py",
    "functionName": "<module>",
    "levelName": "ERROR",
    "levelNumber": 40,
    "lineNumber": 20,
    "module": "send",
    "order_id": "ord_42",
    "processName": "MainProcess",
    "service": "taproom",
    "threadName": "MainThread"
  }
}
```

### Captura el fallo que termina el proceso

`install_process_error_capture()` se engancha a `sys.excepthook`. Una excepción que nadie captura se convierte en una incidencia de nivel `critical` con `handled: false`, capturada antes de que el proceso salga con código 1.

```bash
import atexit
import json
import os

from logbrew_sdk import LogBrewClient, RecordingTransport, install_process_error_capture

transport = RecordingTransport.always_accept()
client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"], transport=transport)
install_process_error_capture(client, on_capture_error=print)


@atexit.register
def show_captured_issue() -> None:
    issue = json.loads(transport.last_body())["events"][0]["attributes"]
    print(json.dumps({k: issue[k] for k in ("title", "level")}), json.dumps(issue["exception"]))


kegs = {"ipa": 3}
kegs["stout"] -= 1
```

Salida

```text
Traceback (most recent call last):
  File "<stdin>", line 19, in <module>
KeyError: 'stout'
{"title": "KeyError", "level": "critical"} {"type": "KeyError", "mechanism": {"type": "python.excepthook", "handled": false}}
```

## Lo que vimos al probarlo

Sin configuración, 5 llamadas al logger raíz imprimieron 3 líneas en stderr y descartaron las llamadas DEBUG e INFO. La línea por defecto es el nivel, el nombre del logger y el mensaje, como `WARNING:root:keg below 10%`.

Una segunda llamada a `basicConfig()` no cambió nada hasta que pasamos `force=True`. A través del handler de LogBrew, las llamadas DEBUG e INFO llegaron como `info`, WARNING como `warning`, ERROR como `error` y CRITICAL como `critical`.

`log.exception()` añadió `exceptionName` y `exceptionMessage` al log de LogBrew. Con `include_exception_text=True` en el handler, el traceback completo llegó como `exceptionText`.

```bash
import logging

logging.debug("keg pressure read")
logging.info("tap opened")
logging.warning("keg below 10%")
logging.error("keg empty")
logging.critical("cooler offline")
```

Salida

```text
WARNING:root:keg below 10%
ERROR:root:keg empty
CRITICAL:root:cooler offline
```

## Los 5 niveles de log y cuándo usar cada uno

DEBUG es para los valores que necesitas mientras arreglas un bug, como la presión del barril, el texto SQL o el número de reintentos. Déjalo apagado en producción.

INFO registra lo que hizo el programa, como un grifo abierto o un pedido pagado. WARNING registra lo que salió mal pero se gestionó, como un barril por debajo del 10 %. ERROR registra una solicitud o un trabajo que falló, como un barril vacío. CRITICAL registra lo que detiene el programa, como un refrigerador apagado.

Registra cada excepción una vez, donde la gestionas. Una línea en cada nivel de la pila de llamadas envía 3 alertas por 1 fallo.

```bash
import logging

for name in ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"):
    print(name, logging.getLevelName(name))
print("root logger level:", logging.getLevelName(logging.getLogger().level))
```

Salida

```text
DEBUG 10
INFO 20
WARNING 30
ERROR 40
CRITICAL 50
root logger level: WARNING
```

## Lo que LogBrew registra

Cada registro se convierte en 1 log con su nivel, nombre de logger, nombre de archivo, función, número de línea y módulo. Los valores primitivos de `extra={}` se convierten en campos por los que puedes filtrar, y `metadata={}` en el handler añade un campo a cada línea, como `service` arriba.

En una excepción, el log recibe `exceptionName` y `exceptionMessage`. Pasa `include_exception_text=True` al handler para guardar también el texto del traceback.

`install_process_error_capture()` convierte una excepción no capturada en una incidencia de nivel `critical`, con el tipo de excepción, los frames de la pila y el mecanismo `python.excepthook`.

## Preguntas frecuentes

### ¿Cuál es el nivel de logging por defecto en Python?

WARNING. Sin una llamada a `basicConfig()`, el logger raíz imprime WARNING, ERROR y CRITICAL en stderr como `WARNING:root:message`, y descarta DEBUG e INFO.

### ¿Por qué logging.basicConfig no hace nada?

El logger raíz ya tiene un handler, de una llamada anterior a `basicConfig()` o de una librería, así que Python ignora la llamada. Pasa `force=True` para reemplazar los handlers.

### ¿Qué diferencia hay entre logging.error y logging.exception?

`log.exception()` es `log.error()` con `exc_info=True`. Registra en ERROR y añade el traceback. Llámalo solo dentro de un bloque `except`.

### ¿Cuáles son los niveles de logging de Python en orden?

DEBUG 10, INFO 20, WARNING 30, ERROR 40, CRITICAL 50. Un logger deja pasar un registro cuando su número es igual o superior al nivel que fijaste.

### ¿Uso print() o logging en Python?

Usa `logging`. `print()` no tiene nivel, ni marca de tiempo, ni nombre de logger, ni handler. Con `logging` activas líneas por módulo y las envías a un archivo o a LogBrew.

## Ve cada excepción de Python con su traceback

El SDK de Python de LogBrew envía cada línea de log con su nivel, logger, archivo y línea, y convierte una excepción no capturada en una incidencia con su traza de pila. Empieza con el generoso plan gratuito, sin tarjeta.

[Empezar gratis](https://logbrew.co/es)

## Fuentes

- [Python docs, Logging HOWTO](https://docs.python.org/3/howto/logging.html)
- [Python docs, logging module](https://docs.python.org/3/library/logging.html)
- [python/cpython#78078](https://github.com/python/cpython/issues/78078)

## Más guías para agentes y Python

- [Añade un servidor MCP a Claude Code y Codex](https://logbrew.co/es/guides/mcp-server-claude-code-codex)
- [Usa AGENTS.md en Claude Code y Codex](https://logbrew.co/es/guides/agents-md)
- [Soluciona el error 529 overloaded y el error 429 de rate limit en Claude](https://logbrew.co/es/guides/claude-overloaded-rate-limit-error)
- [Logging estructurado con un formato de log y niveles que puedes buscar por campo](https://logbrew.co/es/guides/structured-logging)
- [Cómo solucionar errores en Claude Code, Codex, JavaScript y Python](https://logbrew.co/es/guides)
