45 lines
1.6 KiB
Python
45 lines
1.6 KiB
Python
"""Logging that goes to a file as well as to stdout.
|
|
|
|
WHY BOTH
|
|
|
|
stdout is what `docker compose logs` reads and what Docker's own log
|
|
driver captures, so it has to stay. The file is so that every component
|
|
in this stack has ONE findable place - ./logs, bind-mounted at /logs -
|
|
rather than leaving somebody to work out which compose invocation shows
|
|
the thing they are looking for.
|
|
|
|
Falling back to stdout alone is deliberate. A container run without the
|
|
mount, or with the directory not writable, should keep working and say
|
|
why once; refusing to start because a log file could not be opened would
|
|
be the tail wagging the dog.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
FORMAT = "%(asctime)s %(levelname)-7s %(name)s | %(message)s"
|
|
|
|
|
|
def configure(component: str, level: str = "INFO") -> logging.Logger:
|
|
"""stdout always; ./logs/<component>.log when the directory is there."""
|
|
handlers: list[logging.Handler] = [logging.StreamHandler(sys.stdout)]
|
|
problem = ""
|
|
log_dir = os.environ.get("OWUITOOLS_LOG_DIR", "/logs")
|
|
if log_dir:
|
|
try:
|
|
os.makedirs(log_dir, exist_ok=True)
|
|
handlers.append(logging.FileHandler(
|
|
os.path.join(log_dir, f"{component}.log"), encoding="utf-8"))
|
|
except OSError as exc:
|
|
problem = f"{log_dir} is not writable ({exc}); logging to stdout only"
|
|
|
|
logging.basicConfig(level=getattr(logging, level.upper(), logging.INFO),
|
|
format=FORMAT, handlers=handlers, force=True)
|
|
logger = logging.getLogger(component)
|
|
if problem:
|
|
logger.warning(problem)
|
|
return logger
|