Wires capture -> apply -> cleanup into cycle(cfg): per-file capture and cleanup each wrapped in try/except + log.exception so one file's failure does not abort the cycle; apply failure does not block cleanup; writer is always closed in finally. run(cfg) loops cycle with sleep; main() loads config from argv. logging_setup uses RotatingFileHandler 10MBx5 + console. Unit tests cover all three error-isolation branches via mocks (no real end-to-end smoke; integration deferred to Task 9 pilot). Co-Authored-By: Claude <noreply@anthropic.com>
31 lines
1.2 KiB
Python
31 lines
1.2 KiB
Python
"""Logging configuration for the sync service.
|
|
|
|
Configures the root logger with a RotatingFileHandler (10 MB x 5, UTF-8) plus
|
|
a console StreamHandler. The level and log path come from ``cfg.logging``.
|
|
"""
|
|
import logging
|
|
import logging.handlers
|
|
import os
|
|
|
|
|
|
def setup_logging(cfg_dict: dict | None):
|
|
"""Configure root logging from the ``cfg.logging`` dict.
|
|
|
|
``cfg_dict`` is ``SyncConfig.logging`` (a dict or None). ``level`` is a
|
|
logging-level name string (default ``"INFO"``); ``path`` is the log file
|
|
path (default ``"sync.log"``). The parent directory is created if missing.
|
|
"""
|
|
level = getattr(logging, (cfg_dict or {}).get("level", "INFO")) if cfg_dict else logging.INFO
|
|
path = (cfg_dict or {}).get("path", "sync.log")
|
|
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
|
h = logging.handlers.RotatingFileHandler(
|
|
path, maxBytes=10 * 1024 * 1024, backupCount=5, encoding="utf-8"
|
|
)
|
|
h.setFormatter(logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s"))
|
|
root = logging.getLogger()
|
|
root.setLevel(level)
|
|
root.addHandler(h)
|
|
sh = logging.StreamHandler()
|
|
sh.setFormatter(logging.Formatter("%(levelname)s %(message)s"))
|
|
root.addHandler(sh)
|