diff --git a/src/sync/logging_setup.py b/src/sync/logging_setup.py index 7ca32f9..3186857 100644 --- a/src/sync/logging_setup.py +++ b/src/sync/logging_setup.py @@ -18,12 +18,26 @@ def setup_logging(cfg_dict: dict | None): 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) + + # Idempotent: drop any handlers already attached to the root logger before + # re-adding. setup_logging can be called from more than one entry point + # (e.g. main.py and service.run), and the old code unconditionally + # addHandler'd each time, stacking duplicate handlers so every log line was + # written twice. Clearing first means repeated calls always yield exactly + # one file handler + one console handler, regardless of caller. + root = logging.getLogger() + for old in list(root.handlers): + root.removeHandler(old) + try: + old.close() + except Exception: + pass + root.setLevel(level) + 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"))