Coverage for src / molecular_simulations / logging_config.py: 96%
23 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-13 01:26 -0600
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-13 01:26 -0600
1from __future__ import annotations
2import logging
3import logging.config
4import os
5import socket
6from datetime import datetime
8def configure_logging(
9 level: str | int | None = None,
10 to_file: str | None = None,
11 fmt: str | None = None,
12) -> None:
13 """
14 Opt-in configuration for apps/CLIs/examples.
15 Library code should *not* call this implicitly.
16 """
17 level = (level or os.getenv("MS_LOG_LEVEL") or "INFO")
18 fmt = fmt or os.getenv("MS_LOG_FMT") or (
19 "%(asctime)s | %(levelname)s | %(name)s | %(message)s "
20 "[host=%(hostname)s pid=%(process)d rank=%(mpirank)s]"
21 )
23 class _ContextFilter(logging.Filter):
24 def filter(self, record: logging.LogRecord) -> bool:
25 record.hostname = socket.gethostname()
26 # Fill MPI rank if available; else 0
27 try:
28 from mpi4py import MPI # noqa: WPS433
29 record.mpirank = MPI.COMM_WORLD.Get_rank()
30 except Exception:
31 record.mpirank = 0
32 return True
34 handlers = {
35 "console": {
36 "class": "logging.StreamHandler",
37 "level": level,
38 "filters": ["ctx"],
39 "formatter": "standard",
40 }
41 }
43 if to_file or os.getenv("MS_LOG_FILE"):
44 handlers["file"] = {
45 "class": "logging.handlers.RotatingFileHandler",
46 "level": level,
47 "filters": ["ctx"],
48 "formatter": "standard",
49 "filename": to_file or os.getenv("MS_LOG_FILE"),
50 "maxBytes": 10 * 1024 * 1024,
51 "backupCount": 3,
52 "encoding": "utf-8",
53 }
55 config = {
56 "version": 1,
57 "disable_existing_loggers": False,
58 "filters": {"ctx": {"()": _ContextFilter}},
59 "formatters": {"standard": {"format": fmt}},
60 "handlers": handlers,
61 "root": {"level": level, "handlers": list(handlers)},
62 }
64 logging.config.dictConfig(config)