diff --git a/fastapi-vue/fastapi_vue/logging.py b/fastapi-vue/fastapi_vue/logging.py index dfda6dc..3fe9ee8 100644 --- a/fastapi-vue/fastapi_vue/logging.py +++ b/fastapi-vue/fastapi_vue/logging.py @@ -10,6 +10,7 @@ from __future__ import annotations import io import logging +import os import re import sys from contextlib import suppress @@ -43,6 +44,21 @@ def strip_ansi(text: str) -> str: return ANSI_ESCAPE_RE.sub("", text) +def use_color(stream: io.TextIOBase = sys.stderr) -> bool: + """Test if the stream supports color codes.""" + if os.environ.get("NO_COLOR"): # Non empty means no (no-color.org) + return False + if os.environ.get("FORCE_COLOR", "") not in {"", "0"}: # force-color.org, node + return True + if hasattr(stream, "isatty") and stream.isatty(): + return True + with suppress(KeyError, ValueError, OSError): # Journald does color (-ocat) + dev, ino = map(int, os.environ["JOURNAL_STREAM"].split(":", 1)) + st = os.fstat(stream.fileno()) + return st.st_dev == dev and st.st_ino == ino + return False + + _LEVEL_EMOJI = { logging.DEBUG: "🐛", logging.INFO: "🔷", @@ -94,7 +110,7 @@ class Formatter(logging.Formatter): if use_colors in (True, False): self.use_colors = use_colors else: - self.use_colors = sys.stdout.isatty() + self.use_colors = use_color(sys.stdout) super().__init__(fmt=fmt, datefmt=datefmt, style=style) def formatMessage(self, record: logging.LogRecord) -> str: # noqa: N802 @@ -349,7 +365,9 @@ def patch_log_config(log_config, *, access_log: bool = True): # noqa: ANN001, A # watchfiles logs "N changes detected" to its own logger at INFO; only the # WARNING "Reloading..." line (uvicorn.error) should show. with suppress(Exception): - config.setdefault("loggers", {}).setdefault("watchfiles.main", {}).setdefault("level", "WARNING") + config.setdefault("loggers", {}).setdefault("watchfiles.main", {}).setdefault( + "level", "WARNING" + ) # kanta-style output (diffs, colored headers) prints without prefixes, # like our access log. A user-supplied "kanta" logger entry wins. diff --git a/fastapi-vue/fastapi_vue/server.py b/fastapi-vue/fastapi_vue/server.py index f798e32..95ee99b 100644 --- a/fastapi-vue/fastapi_vue/server.py +++ b/fastapi-vue/fastapi_vue/server.py @@ -18,11 +18,17 @@ from .logging import ( patch_lifespan_logging, patch_log_config, patch_server_error_middleware, + use_color, ) from .startupbox import print_box tracerite.load() # Early load on CLI load (import server); uvicorn workers reload via log config +# Install force color to aid tracerite and any external software to use full color when available +# Define NO_COLOR or FORCE_COLOR beforehand to avoid this +if "FORCE_COLOR" not in os.environ and use_color(): + os.environ["FORCE_COLOR"] = "3" + logger = logging.getLogger(__name__) _WILDCARD_HOSTS = frozenset({"0.0.0.0", "::"}) # noqa: S104