Source code for janus_core.helpers.log

"""Configure logger with yaml-styled format."""

from __future__ import annotations

import json
import logging
from typing import Literal

from codecarbon import OfflineEmissionsTracker
from codecarbon.output import LoggerOutput

from janus_core.helpers.janus_types import LogLevel


[docs] class YamlFormatter(logging.Formatter): # numpydoc ignore=PR02 """ Custom formatter to convert multiline messages into yaml list. Parameters ---------- fmt : str | None A format string in the given style for the logged output as a whole. Default is defined by `FORMAT`. datefmt : str | None A format string in the given style for the date/time portion of the logged output. Default is taken from logging.Formatter.formatTime(). style : Literal['%', '{', '$'] Determines how the format string will be merged with its data. Can be one of '%', '{' or '$'. Default is '%'. validate : bool If True, incorrect or mismatched fmt and style will raise a ValueError. Default is True. defaults : dict[str, logging.Any] | None A dictionary with default values to use in custom fields. Default is None. *args Arguments to pass to logging.Formatter.__init__. **kwargs Keyword arguments to pass to logging.Formatter.__init__. Methods ------- format(record) Format log message to convert new lines into a yaml list. """ FORMAT = """ - timestamp: %(asctime)s level: %(levelname)s message: %(message)s trace: %(module)s line: %(lineno)d """
[docs] def __init__(self, *args, **kwargs) -> None: """ Set default string format to yaml style. Parameters ---------- *args Arguments to pass to logging.Formatter.__init__. **kwargs Keyword arguments to pass to logging.Formatter.__init__. """ kwargs.setdefault("fmt", self.FORMAT) super().__init__(*args, **kwargs)
[docs] def format(self, record: logging.LogRecord) -> str: """ Format log message to convert new lines into a yaml list. Parameters ---------- record : logging.LogRecord Log record to be formatted. Returns ------- str Formatted log message. """ # Parse JSON dump from codecarbon record.msg = str(record.msg) if len(record.msg) > 1 and (record.msg[0] == "{" and record.msg[-1] == "}"): msg_dict = json.loads(record.msg) record.msg = "" for key, value in msg_dict.items(): record.msg += f"\n {key}: {value}" else: # Replace "" with '' to prevent invalid wrapping record.msg = record.msg.replace('"', "'") # Convert new lines into yaml list record.msg = "\n" + "\n".join( f' - "{line.strip()}"' for line in record.msg.splitlines() if line.strip() ) return super().format(record)
[docs] def config_logger( name: str, filename: str | None = None, level: LogLevel = logging.INFO, capture_warnings: bool = True, filemode: Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+"] = "w", force: bool = True, ) -> logging.Logger | None: """ Configure logger with yaml-styled format. Parameters ---------- name : str Name of logger. Default is None. filename : str | None Name of log file if writing logs. Default is None. level : LogLevel Threshold for logger. Default is logging.INFO. capture_warnings : bool Whether to capture warnings in log. Default is True. filemode : str Mode of file to open. Default is "w". force : bool If true, remove and close existing handlers attached to the root logger before configuration. Default is True. Returns ------- logging.Logger | None Configured logger if `filename` has been specified. """ if filename: logger = logging.getLogger(name) handler = logging.FileHandler( filename, filemode, encoding="utf-8", ) handler.setLevel(level) formatter = YamlFormatter() handler.setFormatter(formatter) logging.basicConfig( level=level, force=force, handlers=[handler], ) logging.captureWarnings(capture=capture_warnings) else: logger = None return logger
[docs] def config_tracker( janus_logger: logging.Logger | None, track_carbon: bool = True, *, country_iso_code: str = "GBR", save_to_file: bool = False, log_level: Literal["debug", "info", "warning", "error", "critical"] = "critical", ) -> OfflineEmissionsTracker | None: """ Configure codecarbon tracker to log outputs. Parameters ---------- janus_logger : logging.Logger | None Logger for codecarbon output. track_carbon : bool Whether to track carbon emissions for calculation. Default is True. country_iso_code : str 3 letter ISO Code of the country where the code is running. Default is "GBR". save_to_file : bool Whether to also output results to a csv file. Default is False. log_level : Literal["debug", "info", "warning", "error", "critical"] Log level of internal carbon tracker log. Default is "critical". Returns ------- OfflineEmissionsTracker | None Configured offline codecarbon tracker, if logger is specified. """ if janus_logger and track_carbon: carbon_logger = LoggerOutput(janus_logger) tracker = OfflineEmissionsTracker( country_iso_code=country_iso_code, save_to_file=save_to_file, save_to_logger=True, logging_logger=carbon_logger, project_name="janus-core", log_level=log_level, allow_multiple_runs=True, ) # Suppress further logging from codecarbon carbon_logger = logging.getLogger("codecarbon") while carbon_logger.hasHandlers(): carbon_logger.removeHandler(carbon_logger.handlers[0]) else: tracker = None return tracker