Logging Guide
deepretro uses structlog for structured,
leveled logging. Every module creates a logger at the top of the file and emits
log events with key-value context that is machine-parseable and
human-readable.
Quick start
No configuration is required. By default, structlog prints colored log lines to stdout at all levels.
To control the log level or output format, call
deepretro.logging.configure_logging() once at your application entry
point:
from deepretro.logging import configure_logging
# Show only warnings and above
configure_logging(level="WARNING")
# Silence all deepretro logging
configure_logging(level="CRITICAL")
# Verbose JSON output for production / log aggregation
configure_logging(level="DEBUG", json_output=True)
If you already call structlog.configure() in your application (e.g. in
src/main.py), that configuration applies automatically — you do not need
configure_logging.
Minimal application example
Use configure_logging() once near your entry point, then create module
loggers with structlog.get_logger():
import structlog
from deepretro.logging import configure_logging
configure_logging(level="INFO")
logger = structlog.get_logger(__name__)
logger.info("Starting retrosynthesis", molecule="CCO")
For production log pipelines, switch the same helper to JSON output:
configure_logging(level="DEBUG", json_output=True)
How to log in a new module
Add two lines at the top of your module:
import structlog
logger = structlog.get_logger()
Then use the standard log levels:
logger.debug("Low-level detail", key="value")
logger.info("Normal operation", molecule=smiles)
logger.warning("Recoverable issue", attempt=attempt)
logger.error("Failure", smiles=smiles, error=str(exc))
Prefer structured key-value pairs over f-string interpolation so that downstream consumers (ELK, Datadog, etc.) can parse fields automatically.
Log level conventions
Level |
When to use |
|---|---|
|
Detailed diagnostics: prompt text, raw response bodies, intermediate computation results. Hidden by default in production. |
|
Normal milestones: “Starting retrosynthesis”, “Pipeline succeeded”, “Validity check complete”. |
|
Recoverable problems: retries, fallbacks, rejected pathways, missing optional config. |
|
Failures that prevent the current operation from completing: invalid SMILES, JSON parse errors, all API attempts exhausted. |
Context propagation with bound_contextvars
Use structlog.contextvars.bound_contextvars to attach context (e.g. a
job ID) that will appear in every log line emitted within the block — even
from deeply nested functions:
from structlog.contextvars import bound_contextvars
def run_retrosynthesis(molecule: str, job_id: str):
with bound_contextvars(job_id=job_id, molecule=molecule):
logger.info("Starting retrosynthesis")
# All downstream log calls automatically include job_id + molecule
result = call_llm(molecule)
return result
This replaces the previous contextvars.ContextVar / job_context pattern.
There is no need to pass loggers through function arguments.