`logging` — Structured Logging
Python · Standard Library · Run with python3
Explanation
The `logging` module provides a configurable, hierarchical logging system. Loggers are arranged in a dotted-name hierarchy (`__name__` is idiomatic). Handlers direct log records to outputs (stderr, files, sockets, HTTP). Formatters control the output format. Log levels: DEBUG < INFO < WARNING < ERROR < CRITICAL. Python 3.12 added `logging.getLogger(__name__)` convenience and Python 3.11 added `logging.getLevelNamesMapping()`. Use `structlog` or Python 3.12's `logging.getLogger` for structured JSON logging in production.
Real-World Scenario
A microservice uses one logger per module (`logger = logging.getLogger(__name__)`). A JSON formatter in production emits structured logs that Elasticsearch or Loki can index and search. Log levels control verbosity between development (DEBUG) and production (WARNING). ```python import logging import json import sys class JSONFormatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: log_entry = { "level": record.levelname, "message": record.getMessage(), "module": record.module, "line": record.lineno, } if record.exc_info: log_entry["exception"] = self.formatException(record.exc_info) return json.dumps(log_entry) logger = logging.getLogger("myapp.service") handler = logging.StreamHandler(sys.stdout) handler.setFormatter(JSONFormatter()) logger.addHandler(handler) logger.setLevel(logging.DEBUG) logger.info("Service started") logger.debug("Connecting to %s", "localhost:5432") try: 1 / 0 except ZeroDivisionError: logger.error("Unexpected error", exc_info=True) ```
Quick Snippet
import logging
import json
import sys
class JSONFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
log_entry = {
"level": record.levelname,
"message": record.getMessage(),
"module": record.module,
"line": record.lineno,
}
if record.exc_info:
log_entry["exception"] = self.formatException(record.exc_info)
return json.dumps(log_entry)
logger = logging.getLogger("myapp.service")
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.info("Service started")
logger.debug("Connecting to %s", "localhost:5432")
try:
1 / 0
except ZeroDivisionError:
logger.error("Unexpected error", exc_info=True)Full Example
# Compile: python3 logging_basics.py
# Output:
# JSON log lines printed to stdout
import logging
import json
import sys
class JSONFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
log_entry = {
"level": record.levelname,
"message": record.getMessage(),
"module": record.module,
}
if record.exc_info:
log_entry["exception"] = self.formatException(record.exc_info)
return json.dumps(log_entry)
logger = logging.getLogger("myapp.service")
# Clear any existing handlers to avoid duplicate output in tests
logger.handlers.clear()
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.propagate = False
logger.info("Service started")
logger.debug("Connecting to %s", "localhost:5432")
try:
1 / 0
except ZeroDivisionError:
logger.error("Unexpected division error", exc_info=True)
python3 logging_basics.py