Spaces:
Running
Running
| """Structlog configuration for the pipeline. | |
| Call configure_structlog() once at application startup to set up | |
| structured logging with human-readable console output in dev | |
| and JSON output when use_json=True is passed. | |
| Logs always go to stderr (console/PyCharm) AND to ``logs/pipeline.log`` | |
| (rotating, 10 MB x 3 backups) so the assistant can read them without | |
| needing the IDE console. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import sys | |
| from logging.handlers import RotatingFileHandler | |
| import structlog | |
| from common.paths import REPO_ROOT | |
| LOG_DIR = REPO_ROOT / "logs" | |
| LOG_FILE = LOG_DIR / "pipeline.log" | |
| LOG_MAX_BYTES = 10 * 1024 * 1024 | |
| LOG_BACKUP_COUNT = 3 | |
| def configure_structlog(use_json: bool) -> None: | |
| """Configure structlog processors and stdlib integration. | |
| Uses ConsoleRenderer for human-readable dev output. | |
| Integrates with stdlib logging so third-party libraries | |
| (litellm, httpx, etc.) also emit structured logs. | |
| Also attaches a RotatingFileHandler so output lands in | |
| ``logs/pipeline.log`` regardless of how the process is launched. | |
| """ | |
| shared_processors: list[structlog.types.Processor] = [ | |
| structlog.contextvars.merge_contextvars, | |
| structlog.stdlib.add_log_level, | |
| structlog.stdlib.add_logger_name, | |
| structlog.processors.TimeStamper(fmt="iso"), | |
| structlog.processors.StackInfoRenderer(), | |
| structlog.processors.UnicodeDecoder(), | |
| ] | |
| if use_json: | |
| renderer: structlog.types.Processor = structlog.processors.JSONRenderer() | |
| else: | |
| renderer = structlog.dev.ConsoleRenderer() | |
| structlog.configure( | |
| processors=[ | |
| *shared_processors, | |
| structlog.stdlib.ProcessorFormatter.wrap_for_formatter, | |
| ], | |
| logger_factory=structlog.stdlib.LoggerFactory(), | |
| wrapper_class=structlog.stdlib.BoundLogger, | |
| cache_logger_on_first_use=True, | |
| ) | |
| formatter = structlog.stdlib.ProcessorFormatter( | |
| processors=[ | |
| structlog.stdlib.ProcessorFormatter.remove_processors_meta, | |
| renderer, | |
| ], | |
| ) | |
| console_handler = logging.StreamHandler(sys.stderr) | |
| console_handler.setFormatter(formatter) | |
| LOG_DIR.mkdir(parents=True, exist_ok=True) | |
| file_handler = RotatingFileHandler( | |
| LOG_FILE, | |
| maxBytes=LOG_MAX_BYTES, | |
| backupCount=LOG_BACKUP_COUNT, | |
| encoding="utf-8", | |
| ) | |
| plain_renderer = structlog.dev.ConsoleRenderer(colors=False) | |
| file_formatter = structlog.stdlib.ProcessorFormatter( | |
| processors=[ | |
| structlog.stdlib.ProcessorFormatter.remove_processors_meta, | |
| plain_renderer, | |
| ], | |
| ) | |
| file_handler.setFormatter(file_formatter) | |
| root_logger = logging.getLogger() | |
| root_logger.handlers.clear() | |
| root_logger.addHandler(console_handler) | |
| root_logger.addHandler(file_handler) | |
| root_logger.setLevel(logging.INFO) | |