80 lines
2.6 KiB
Python
80 lines
2.6 KiB
Python
"""User-facing output utilities combining console and log output.
|
|
|
|
Console messages are colorised via ``click.style`` for visual feedback.
|
|
The persistent log file always receives plain text (no ANSI codes).
|
|
|
|
This is a generalisation of grm's ``ui.say()`` function, extracted so
|
|
that any CLI tool can use the same pattern. The logger name and
|
|
console-level env var are configurable.
|
|
|
|
Usage::
|
|
|
|
from devx.utils.ui import say
|
|
|
|
say("Starting deployment...")
|
|
say("Error occurred", level=logging.ERROR, err=True, color="red")
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
|
|
import click
|
|
|
|
# Configurable env var for console verbosity — projects can override
|
|
# via :func:`configure_ui`.
|
|
_LOG_LEVEL_ENV_VAR = "DEVX_LOG_LEVEL"
|
|
_LOGGER_NAME = "devx"
|
|
|
|
|
|
def configure_ui(*, log_level_env_var: str = "DEVX_LOG_LEVEL", logger_name: str = "devx") -> None:
|
|
"""Override the env var name and logger name used by :func:`say`.
|
|
|
|
This allows downstream projects (e.g. grm) to use their own env var
|
|
names (e.g. ``GRM_LOG_LEVEL``) and logger names while still using
|
|
devx's ui module.
|
|
|
|
Args:
|
|
log_level_env_var: Environment variable name for console log level.
|
|
logger_name: Logger name for persistent log file output.
|
|
"""
|
|
global _LOG_LEVEL_ENV_VAR, _LOGGER_NAME
|
|
_LOG_LEVEL_ENV_VAR = log_level_env_var
|
|
_LOGGER_NAME = logger_name
|
|
|
|
|
|
def _console_level() -> int:
|
|
"""Return the minimum level for console output from the configured env var."""
|
|
value = os.getenv(_LOG_LEVEL_ENV_VAR, "INFO")
|
|
try:
|
|
return getattr(logging, value.upper())
|
|
except AttributeError:
|
|
return logging.INFO
|
|
|
|
|
|
def say(
|
|
msg: str,
|
|
level: int = logging.INFO,
|
|
err: bool = False,
|
|
color: str | None = None,
|
|
) -> None:
|
|
"""Output a message to the user and also log it for auditing.
|
|
|
|
Console output goes via ``click.echo`` (handles encoding, CliRunner,
|
|
Windows colorama) only when *level* is at least the configured
|
|
console log level (default ``DEVX_LOG_LEVEL``, falls back to INFO).
|
|
The same message is always sent to the configured logger so it
|
|
appears in the persistent log file regardless of console verbosity.
|
|
|
|
Args:
|
|
msg: Message to display.
|
|
level: Logging level (e.g. ``logging.INFO``, ``logging.ERROR``).
|
|
err: If True, output to stderr instead of stdout.
|
|
color: Optional ``click.style`` fg color (e.g. ``"green"``, ``"red"``).
|
|
"""
|
|
if level >= _console_level():
|
|
styled = click.style(msg, fg=color) if color else msg
|
|
click.echo(styled, err=err)
|
|
logging.getLogger(_LOGGER_NAME).log(level, msg)
|