Install (or reset) a JSON-lines file handler for the plugin logger. Idempotent: subsequent calls close any existing handlers first so test harnesses and repeated CLI invocations don't accumulate handlers. The ``home`` arg exists for tests to point logs at ``tmp_path`` instead of the
(home: str | Path | None = None)
| 77 | |
| 78 | |
| 79 | def configure(home: str | Path | None = None) -> logging.Logger: |
| 80 | """Install (or reset) a JSON-lines file handler for the plugin logger. |
| 81 | |
| 82 | Idempotent: subsequent calls close any existing handlers first so test |
| 83 | harnesses and repeated CLI invocations don't accumulate handlers. The |
| 84 | ``home`` arg exists for tests to point logs at ``tmp_path`` instead |
| 85 | of the user's real home dir. |
| 86 | """ |
| 87 | logger = logging.getLogger(LOGGER_NAME) |
| 88 | # Swap handlers atomically; don't leak FDs from previous invocations. |
| 89 | for handler in list(logger.handlers): |
| 90 | logger.removeHandler(handler) |
| 91 | try: |
| 92 | handler.close() |
| 93 | except Exception: # noqa: BLE001 — cleanup must never raise |
| 94 | pass |
| 95 | |
| 96 | home_dir = Path(home).expanduser().resolve() if home else get_home_dir() |
| 97 | log_dir = home_dir / "logs" |
| 98 | try: |
| 99 | log_dir.mkdir(parents=True, exist_ok=True) |
| 100 | except OSError: |
| 101 | # Disk full / permission denied: log to stderr rather than blowing |
| 102 | # up the whole CLI. The plugin itself still works. |
| 103 | stderr = logging.StreamHandler() |
| 104 | stderr.setFormatter(JsonFormatter()) |
| 105 | logger.addHandler(stderr) |
| 106 | logger.setLevel(logging.INFO) |
| 107 | return logger |
| 108 | |
| 109 | file_handler = logging.handlers.TimedRotatingFileHandler( |
| 110 | log_dir / LOG_FILENAME, |
| 111 | when="midnight", |
| 112 | backupCount=DEFAULT_RETENTION_DAYS, |
| 113 | encoding="utf-8", |
| 114 | ) |
| 115 | file_handler.setFormatter(JsonFormatter()) |
| 116 | logger.addHandler(file_handler) |
| 117 | logger.setLevel(logging.INFO) |
| 118 | # Don't bubble up to the root logger — avoids double-printing if the |
| 119 | # host process (tests, Codex hook runner) attaches its own stderr handler. |
| 120 | logger.propagate = False |
| 121 | return logger |
| 122 | |
| 123 | |
| 124 | def get_logger() -> logging.Logger: |
nothing calls this directly
no test coverage detected