Skip to content

Migration Guide

obslog interoperates with existing logging rather than demanding a rewrite. You can adopt it incrementally.

From the standard library logging

Keep your existing handlers and route obslog through them, or route stdlib logs into obslog. Both directions live in obslog.integrations (no extra required).

import logging
import obslog
from obslog.integrations import LoggingSink, ObslogHandler

# obslog -> logging: obslog records flow through your existing logging config
obslog.configure(sinks=[LoggingSink(logging.getLogger("app"))])

# logging -> obslog: third-party library logs become obslog records
logging.getLogger().addHandler(ObslogHandler())

Translation of concepts:

stdlib logging obslog
logger.info("user %s", uid) log.info("user.action", user_id=uid)
logging.getLogger(__name__) obslog.get_logger(__name__)
extra={...} keyword fields (land under metadata)
levels DEBUG..CRITICAL TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL
dictConfig configure_from_file / configure_from_env

From structlog

# structlog
log = structlog.get_logger().bind(request_id=rid)
log.info("order_completed", order_id=7)

# obslog
log = obslog.get_logger(__name__).bind(request_id=rid)
log.info("order.completed", order_id=7)
  • bind() works the same (immutable) and returns a new logger.
  • structlog's contextvars binding maps to obslog.context(...).
  • The processor pipeline maps to obslog processors inside a Pipeline sink.

From loguru

# loguru
from loguru import logger

logger.bind(order_id=7).info("Order completed")

# obslog
obslog.get_logger(__name__).info("order.completed", order_id=7)
  • loguru's single global logger becomes get_logger(...); obslog additionally supports isolated providers so libraries never fight over global state.
  • logger.contextualize(...) maps to obslog.context(...).
  • Prefer stable event names over human sentences — the human message is optional (message=), the event name is the machine key.

Tips

  • Reach for event names (order.completed) rather than sentences; put the varying data in fields, not string interpolation.
  • Use error_code= for a stable identifier the moment you have a known failure mode.