From 0c3b0891c597d113a0ae4c58e034a806a4081c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20P=C3=A9rez?= Date: Sun, 24 May 2026 15:38:47 -0400 Subject: [PATCH] feat: add LOGGER.ENABLED toggle to disable application logging When LOGGER.ENABLED is false, setup_logging attaches only a NullHandler so legacy configs without the key keep current behavior. Document the option in EN/ES configuration guides and example YAML templates. --- adn-parrot.example.yaml | 1 + adn-server.example.yaml | 1 + docs/en/server/user-guide/configuration.md | 1 + docs/es/server/user-guide/configuration.md | 1 + .../infrastructure/logging_config.py | 27 +++++++++++++++++-- 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/adn-parrot.example.yaml b/adn-parrot.example.yaml index 03c9ff2..d34f886 100644 --- a/adn-parrot.example.yaml +++ b/adn-parrot.example.yaml @@ -27,6 +27,7 @@ REPORTS: REPORT_CLIENTS: "127.0.0.1" LOGGER: + # ENABLED: false # omit or true = normal; false = no console/file logs (NullHandler) LOG_FILE: /var/log/adn-server/parrot.log LOG_HANDLERS: file-timed LOG_LEVEL: DEBUG diff --git a/adn-server.example.yaml b/adn-server.example.yaml index 6cdc0fb..dcfb1a4 100644 --- a/adn-server.example.yaml +++ b/adn-server.example.yaml @@ -25,6 +25,7 @@ REPORTS: REPORT_CLIENTS: "127.0.0.1" LOGGER: + # ENABLED: false # omit or true = normal; false = no console/file logs (NullHandler) LOG_FILE: /var/log/adn-server/adn-server.log LOG_HANDLERS: file-timed LOG_LEVEL: INFO diff --git a/docs/en/server/user-guide/configuration.md b/docs/en/server/user-guide/configuration.md index f45835b..ee3bcd1 100644 --- a/docs/en/server/user-guide/configuration.md +++ b/docs/en/server/user-guide/configuration.md @@ -163,6 +163,7 @@ Implemented in `infrastructure/logging_config.py` (`setup_logging`). Values are | Key | Meaning | |-----|---------| +| **ENABLED** | **`true`** (default when omitted) — normal logging. **`false`** — disable application log output (no console or file handlers; `NullHandler` only). Legacy configs without this key behave as today. | | **LOG_HANDLERS** | Comma-separated list of handler **tokens** (whitespace around commas is fine). Each token selects outputs; you can combine several. Recognised values: **`console-timed`** or **`console`** — log to **stderr** with format `LEVEL asctime message`; **`file-timed`** or **`file`** — log to **LOG_FILE** with the same format (UTF-8). **Default** if omitted: `console-timed`. Examples: `console-timed` only; `file-timed` only; `console-timed,file-timed` for both console and file. | | **LOG_FILE** | Path used when **`file-timed`** or **`file`** is in **LOG_HANDLERS**. If missing, the code defaults to `/dev/null`. If the path is **`/dev/null`**, file handlers are **not** attached even if listed. If the file cannot be opened (permissions, missing directory), a warning is written to stderr and logging continues without that file handler. | | **LOG_LEVEL** | Root logger level: **`DEBUG`**, **`INFO`**, **`WARNING`**, **`ERROR`**, **`CRITICAL`** (case-insensitive; default **INFO**). Unknown names fall back to **INFO**. You can override at startup with **`python adn-server.py --logging LEVEL`** (same names). A custom **`TRACE`** level is registered for occasional `logger.trace(...)` calls; use **`DEBUG`** for verbose diagnostics in normal operation. | diff --git a/docs/es/server/user-guide/configuration.md b/docs/es/server/user-guide/configuration.md index 7849b0c..c17b643 100644 --- a/docs/es/server/user-guide/configuration.md +++ b/docs/es/server/user-guide/configuration.md @@ -163,6 +163,7 @@ Implementado en `infrastructure/logging_config.py` (`setup_logging`). Los valore | Clave | Significado | |-------|-------------| +| **ENABLED** | **`true`** (por defecto si se omite) — logging normal. **`false`** — desactiva la salida de logs de la aplicación (sin consola ni fichero; solo `NullHandler`). Las configs antiguas sin esta clave no cambian. | | **LOG_HANDLERS** | Lista separada por comas de **tokens** de manejador (espacios alrededor de las comas están bien). Cada token elige salidas; puedes combinar varios. Valores reconocidos: **`console-timed`** o **`console`** — log a **stderr** con formato `LEVEL asctime message`; **`file-timed`** o **`file`** — log a **LOG_FILE** con el mismo formato (UTF-8). **Por defecto** si falta: `console-timed`. Ejemplos: solo `console-timed`; solo `file-timed`; `console-timed,file-timed` para consola y fichero. | | **LOG_FILE** | Ruta usada cuando **`file-timed`** o **`file`** está en **LOG_HANDLERS**. Si falta, el código usa por defecto `/dev/null`. Si la ruta es **`/dev/null`**, los manejadores de fichero **no** se adjuntan aunque estén listados. Si no se puede abrir el fichero (permisos, directorio inexistente), se escribe un aviso a stderr y el logging continúa sin ese manejador de fichero. | | **LOG_LEVEL** | Nivel del logger raíz: **`DEBUG`**, **`INFO`**, **`WARNING`**, **`ERROR`**, **`CRITICAL`** (sin distinguir mayúsculas; por defecto **INFO**). Nombres desconocidos caen en **INFO**. Puedes sobrescribir al arranque con **`python adn-server.py --logging LEVEL`** (mismos nombres). Hay un nivel personalizado **`TRACE`** registrado para llamadas ocasionales `logger.trace(...)`; usa **`DEBUG`** para diagnóstico detallado en operación normal. | diff --git a/src/adn_server/infrastructure/logging_config.py b/src/adn_server/infrastructure/logging_config.py index 3528586..ec79e45 100644 --- a/src/adn_server/infrastructure/logging_config.py +++ b/src/adn_server/infrastructure/logging_config.py @@ -31,6 +31,18 @@ from functools import partial, partialmethod from typing import Any +def logging_enabled(log_config: dict[str, Any]) -> bool: + """LOGGER.ENABLED — default True when omitted (legacy configs unchanged).""" + if "ENABLED" not in log_config: + return True + val = log_config.get("ENABLED") + if isinstance(val, bool): + return val + if isinstance(val, str): + return val.strip().lower() in ("1", "true", "yes", "on") + return bool(val) + + def reopen_file_handlers(logger: logging.Logger | None = None) -> int: """Reopen all :class:`logging.FileHandler` streams on *logger* (default: root). @@ -61,11 +73,22 @@ def reopen_file_handlers(logger: logging.Logger | None = None) -> int: def setup_logging(log_config: dict[str, Any]) -> logging.Logger: - """Configure logging from CONFIG['LOGGER']. Returns root logger.""" + """Configure logging from CONFIG['LOGGER']. Returns application logger.""" + log_name = log_config.get("LOG_NAME", "ADN") + + if not logging_enabled(log_config): + logging.basicConfig( + level=logging.CRITICAL, + handlers=[logging.NullHandler()], + force=True, + ) + logger = logging.getLogger(log_name) + logger.setLevel(logging.CRITICAL) + return logger + level = getattr(logging, (log_config.get("LOG_LEVEL", "INFO")).upper(), logging.INFO) log_file = log_config.get("LOG_FILE", "/dev/null") handlers_cfg = log_config.get("LOG_HANDLERS", "console-timed").strip().split(",") - log_name = log_config.get("LOG_NAME", "ADN") logging.TRACE = 5 logging.addLevelName(logging.TRACE, "TRACE")