Skip to content

log_config

log_config

Global logging configuration for the Diapason CLI.

Classes

SanitizingFormatter

Bases: Formatter

Formatter that redacts credentials from log messages.

Functions:

setup_logging

setup_logging(
    verbose: bool = False,
    quiet: bool = False,
    log_file: Optional[Union[str, Path]] = None,
) -> Logger

Configure the diapason logger.

PARAMETER DESCRIPTION
verbose

Set log level to DEBUG.

TYPE: bool DEFAULT: False

quiet

Set log level to ERROR (overrides verbose if both set).

TYPE: bool DEFAULT: False

log_file

Path for a rotating file handler. When verbose is True and no log_file is given, defaults to ~/.diapason/cli.log.

TYPE: Optional[Union[str, Path]] DEFAULT: None

RETURNS DESCRIPTION
The configured ``diapason`` logger.
Source code in src/diapason/cli/log_config.py
def setup_logging(
    verbose: bool = False,
    quiet: bool = False,
    log_file: Optional[Union[str, Path]] = None,
) -> logging.Logger:
    """Configure the ``diapason`` logger.

    Parameters
    ----------
    verbose:
        Set log level to DEBUG.
    quiet:
        Set log level to ERROR (overrides verbose if both set).
    log_file:
        Path for a rotating file handler.  When *verbose* is ``True``
        and no *log_file* is given, defaults to
        ``~/.diapason/cli.log``.

    Returns
    -------
    The configured ``diapason`` logger.
    """
    logger = logging.getLogger("diapason")

    # Clear existing handlers to avoid duplication across calls
    logger.handlers.clear()

    if quiet:
        level = logging.ERROR
    elif verbose:
        level = logging.DEBUG
    else:
        level = logging.WARNING

    logger.setLevel(level)

    # Console handler
    console_handler = logging.StreamHandler()
    console_handler.setLevel(level)
    fmt = SanitizingFormatter("%(levelname)s %(name)s: %(message)s")
    console_handler.setFormatter(fmt)
    logger.addHandler(console_handler)

    # Les lignes « local voice timing » sont le SEUL instrument de latence de
    # la voix — au niveau WARNING par défaut, elles n'atteignaient jamais les
    # journaux du serveur : l'avant et l'après d'une optimisation étaient
    # inconstatables (Atlas, 24 août 2026). Le canal vocal parle en INFO ;
    # le handler laisse passer l'INFO sauf en mode quiet.
    if not quiet:
        console_handler.setLevel(min(level, logging.INFO))
        logging.getLogger("diapason.speech.realtime.local_voice").setLevel(logging.INFO)
        # Même défaut, même remède, le 20 septembre 2026 : la ligne
        # « chat_performance » du 19 (phases, jetons, attente d'admission, sans
        # contenu) n'a jamais atteint serve.err.log — les 89 s d'une traduction
        # ont dû être reconstituées depuis le journal d'Ollama.
        logging.getLogger("diapason.telemetry.chat_latency").setLevel(logging.INFO)
        # Une ligne toutes les dix minutes qui dit si le préfixe était en
        # cache : sans elle, une pause coûteuse ne laisse aucune trace.
        logging.getLogger("diapason.server.prechauffage").setLevel(logging.INFO)

    # File handler (verbose or explicit path)
    if verbose or log_file is not None:
        if log_file is None:
            from diapason.security.file_utils import secure_mkdir

            log_dir = get_config_dir()
            secure_mkdir(log_dir)
            log_file = log_dir / "cli.log"
        file_handler = RotatingFileHandler(
            str(log_file),
            maxBytes=5 * 1024 * 1024,
            backupCount=3,
        )
        file_handler.setLevel(logging.DEBUG)
        file_fmt = SanitizingFormatter(
            "%(asctime)s %(process)d %(levelname)s %(name)s: %(message)s"
        )
        file_handler.setFormatter(file_fmt)
        logger.addHandler(file_handler)

    return logger