Skip to content

__methods

Functions to add in creating/working with the logging config classes in this module.

FileHandlerConfig dataclass

Bases: BaseHandlerConfig

Define a logging FileHandler.

Parameters:

Name Type Description Default
filename str

The name of the file to log messages to.

'app.log'
Source code in src/red_logging/config_classes/handlers/_handlers.py
@dataclass
class FileHandlerConfig(BaseHandlerConfig):
    """Define a logging FileHandler.

    Params:
        filename (str): The name of the file to log messages to.
    """

    filename: str | None = field(default="app.log")

    def get_configdict(self) -> dict[str, dict[str, str]]:
        """Return a dict representation of the handler described by this class."""
        handler_dict: dict[str, dict[str, str]] = {
            self.name: {
                "class": self.get_handler_class(),
                "level": self.level,
                "formatter": self.formatter,
                "filename": self.filename,
            }
        }
        return handler_dict

    def get_handler_class(self) -> str:
        """Return the logging handler class this class represents.

        Returns:
            (str): `logging.FileHandler`.

        """
        return "logging.FileHandler"

get_configdict()

Return a dict representation of the handler described by this class.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_configdict(self) -> dict[str, dict[str, str]]:
    """Return a dict representation of the handler described by this class."""
    handler_dict: dict[str, dict[str, str]] = {
        self.name: {
            "class": self.get_handler_class(),
            "level": self.level,
            "formatter": self.formatter,
            "filename": self.filename,
        }
    }
    return handler_dict

get_handler_class()

Return the logging handler class this class represents.

Returns:

Type Description
str

logging.FileHandler.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_handler_class(self) -> str:
    """Return the logging handler class this class represents.

    Returns:
        (str): `logging.FileHandler`.

    """
    return "logging.FileHandler"

FormatterConfig dataclass

Bases: BaseLoggingConfig

Define a logging formatter.

Parameters:

Name Type Description Default
name str

The name of the formatter.

None
fmt str

The string formatting to use for log messages.

MESSAGE_FMT_STANDARD
datefmt str

The string formatting to use for log message timestamps.

DATE_FMT_STANDARD
style str

The string substitution style to use for log formats. Default is %, which means formats need to be written like %(asctime)s %(levelname)s %(message)s. If you change this style, make sure the fmt you pass uses the correct formatting style.

'%'
validate bool

When True, the configuration dict this formatter returns will be validated by the logging module.

True
Source code in src/red_logging/config_classes/formatters/_formatters.py
@dataclass
class FormatterConfig(BaseLoggingConfig):
    """Define a logging formatter.

    Params:
        name (str): The name of the formatter.
        fmt (str): The string formatting to use for log messages.
        datefmt (str): The string formatting to use for log message timestamps.
        style (str): The string substitution style to use for log formats. Default is `%`, which
            means formats need to be written like `%(asctime)s %(levelname)s %(message)s`. If
            you change this style, make sure the `fmt` you pass uses the correct formatting style.
        validate (bool): When `True`, the configuration dict this formatter returns will be validated by the logging module.

    """

    name: str = None
    fmt: str = MESSAGE_FMT_STANDARD
    datefmt: str = DATE_FMT_STANDARD
    style: str = "%"
    validate: bool = True

    def get_configdict(self) -> dict[str, dict[str, str]]:
        """Return a dict representation of the formatter described by this class."""
        formatter_dict: dict[str, dict[str, str]] = {self.name: {"format": self.fmt}}
        if self.datefmt:
            formatter_dict[self.name]["datefmt"] = self.datefmt
        if self.style:
            formatter_dict[self.name]["style"] = self.style
        formatter_dict[self.name]["validate"] = self.validate

        return formatter_dict

get_configdict()

Return a dict representation of the formatter described by this class.

Source code in src/red_logging/config_classes/formatters/_formatters.py
def get_configdict(self) -> dict[str, dict[str, str]]:
    """Return a dict representation of the formatter described by this class."""
    formatter_dict: dict[str, dict[str, str]] = {self.name: {"format": self.fmt}}
    if self.datefmt:
        formatter_dict[self.name]["datefmt"] = self.datefmt
    if self.style:
        formatter_dict[self.name]["style"] = self.style
    formatter_dict[self.name]["validate"] = self.validate

    return formatter_dict

LoggerConfig dataclass

Bases: BaseLoggingConfig

Define a logging Logger.

Parameters:

Name Type Description Default
name str

The name of the logger.

required
level str

The level of log messages this logger should show (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).

required
handlers list[str]

List of handler names this logger should use. These handlers must exist in the logging dictConfig.

required
propagate bool

If True, messages will be propagated up/down to the root logger.

False
Source code in src/red_logging/config_classes/loggers/_loggers.py
@dataclass
class LoggerConfig(BaseLoggingConfig):
    """Define a logging Logger.

    Params:
        name (str): The name of the logger.
        level (str): The level of log messages this logger should show (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).
        handlers (list[str]): List of handler names this logger should use. These handlers must exist in the logging dictConfig.
        propagate (bool): If `True`, messages will be propagated up/down to the root logger.
    """

    name: str
    level: str
    handlers: list[str]
    propagate: bool = False

    def get_configdict(self) -> dict:
        """Return a dict representation of the logger described by this class."""
        logger_dict: dict[str, dict[str, t.Any]] = {
            self.name: {
                "level": self.level,
                "handlers": self.handlers,
                "propagate": self.propagate,
            }
        }
        return logger_dict

get_configdict()

Return a dict representation of the logger described by this class.

Source code in src/red_logging/config_classes/loggers/_loggers.py
def get_configdict(self) -> dict:
    """Return a dict representation of the logger described by this class."""
    logger_dict: dict[str, dict[str, t.Any]] = {
        self.name: {
            "level": self.level,
            "handlers": self.handlers,
            "propagate": self.propagate,
        }
    }
    return logger_dict

LoggerFactory

Generate loggers based on LoggerFactory's config.

Source code in src/red_logging/config_classes/loggers/_factory.py
class LoggerFactory:
    """Generate loggers based on LoggerFactory's config."""

    _LOG: logging.Logger | None = None

    @staticmethod
    def __create_logger(
        name: str,
        log_level: str,
        handlers: dict[str, dict],
        formatters: dict[str, dict],
        loggers: dict[str, dict],
    ) -> logging.Logger:
        """Create a logger cnofig from inputs.

        Params:
            name (str): The name of the logger.
            log_level (str): The log levels to show (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).
            handlers (dict[str, dict[str, Any]]): A dict describing the handlers for this logger config.
            formatters (dict[str, dict[str, Any]]): A dict describing the formatters for this logger config.
            loggers (dict[str, dict[str, Any]]): A dict describing the loggers for this logger config.
        """
        log_level = log_level.upper()

        # Configure logging using dictConfig
        logging_config = {
            "version": 1,
            "handlers": handlers,
            "formatters": formatters,
            "loggers": loggers,
            "root": {
                "level": log_level,
                "handlers": list(handlers.keys()),
            },
        }

        try:
            logging.config.dictConfig(logging_config)
        except Exception as exc:
            msg = Exception(f"Unhandled exception configuring logger. Details: {exc}")
            # log.error(msg)

            raise msg

        # Get or create logger
        LoggerFactory._LOG = logging.getLogger(name)

        return LoggerFactory._LOG

    @staticmethod
    def get_logger(
        name: str,
        log_level: str,
        handlers: dict[str, dict],
        formatters: dict[str, dict],
        loggers: dict[str, dict],
    ) -> logging.Logger:
        """Initialize a logger.

        Params:
            name (str): The name of the logger.
            log_level (str): The log levels to show (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).
            handlers (dict[str, dict[str, Any]]): A dict describing the handlers for this logger config.
            formatters (dict[str, dict[str, Any]]): A dict describing the formatters for this logger config.
            loggers (dict[str, dict[str, Any]]): A dict describing the loggers for this logger config.
        """
        logger = LoggerFactory.__create_logger(
            name=name,
            log_level=log_level,
            handlers=handlers,
            formatters=formatters,
            loggers=loggers,
        )

        return logger

get_logger(name, log_level, handlers, formatters, loggers) staticmethod

Initialize a logger.

Parameters:

Name Type Description Default
name str

The name of the logger.

required
log_level str

The log levels to show (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).

required
handlers dict[str, dict[str, Any]]

A dict describing the handlers for this logger config.

required
formatters dict[str, dict[str, Any]]

A dict describing the formatters for this logger config.

required
loggers dict[str, dict[str, Any]]

A dict describing the loggers for this logger config.

required
Source code in src/red_logging/config_classes/loggers/_factory.py
@staticmethod
def get_logger(
    name: str,
    log_level: str,
    handlers: dict[str, dict],
    formatters: dict[str, dict],
    loggers: dict[str, dict],
) -> logging.Logger:
    """Initialize a logger.

    Params:
        name (str): The name of the logger.
        log_level (str): The log levels to show (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).
        handlers (dict[str, dict[str, Any]]): A dict describing the handlers for this logger config.
        formatters (dict[str, dict[str, Any]]): A dict describing the formatters for this logger config.
        loggers (dict[str, dict[str, Any]]): A dict describing the loggers for this logger config.
    """
    logger = LoggerFactory.__create_logger(
        name=name,
        log_level=log_level,
        handlers=handlers,
        formatters=formatters,
        loggers=loggers,
    )

    return logger

QueueHandlerConfig dataclass

Bases: BaseHandlerConfig

Define a logging QueueHandler.

Parameters:

Name Type Description Default
queue Queue

The queue to send log messages to.

None
Source code in src/red_logging/config_classes/handlers/_handlers.py
@dataclass
class QueueHandlerConfig(BaseHandlerConfig):
    """Define a logging QueueHandler.

    Params:
        queue (queue.Queue): The queue to send log messages to.
    """

    queue: Queue = field(default=None)

    def get_configdict(self) -> dict[str, dict[str, t.Any]]:
        """Return a dict representation of the handler described by this class."""
        handler_dict: dict[str, dict[str, t.Any]] = {
            self.name: {
                "class": self.get_handler_class(),
                "level": self.level,
                "formatter": self.formatter,
                "queue": self.queue,
            }
        }
        return handler_dict

    def get_handler_class(self) -> str:
        """Return the logging handler class this class represents.

        Returns:
            (str): `logging.handlers.QueueHandler`.

        """
        return "logging.handlers.QueueHandler"

get_configdict()

Return a dict representation of the handler described by this class.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_configdict(self) -> dict[str, dict[str, t.Any]]:
    """Return a dict representation of the handler described by this class."""
    handler_dict: dict[str, dict[str, t.Any]] = {
        self.name: {
            "class": self.get_handler_class(),
            "level": self.level,
            "formatter": self.formatter,
            "queue": self.queue,
        }
    }
    return handler_dict

get_handler_class()

Return the logging handler class this class represents.

Returns:

Type Description
str

logging.handlers.QueueHandler.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_handler_class(self) -> str:
    """Return the logging handler class this class represents.

    Returns:
        (str): `logging.handlers.QueueHandler`.

    """
    return "logging.handlers.QueueHandler"

QueueListenerConfig dataclass

Bases: BaseLoggingConfig

Define a logging QueueListener.

Parameters:

Name Type Description Default
name str

The name of the handler.

required
queue Queue

The queue to listen for log messages in.

required
handlers list[str]

List of handler names to apply to this listener.

required
Source code in src/red_logging/config_classes/handlers/_handlers.py
@dataclass
class QueueListenerConfig(BaseLoggingConfig):
    """Define a logging QueueListener.

    Params:
        name (str): The name of the handler.
        queue (queue.Queue): The queue to listen for log messages in.
        handlers (list[str]): List of handler names to apply to this listener.

    """

    name: str
    queue: Queue
    handlers: list

    def get_configdict(self) -> dict[str, dict[str, t.Any]]:
        """Return a dict representation of the handler described by this class."""
        listener_dict: dict[str, dict[str, t.Any]] = {
            self.name: {
                "class": self.get_handler_class(),
                "queue": self.queue,
                "handlers": self.handlers,
            }
        }
        return listener_dict

    def get_handler_class(self) -> str:
        """Return the logging handler class this class represents.

        Returns:
            (str): `logging.handlers.QueueListener`.

        """
        return "logging.handleres.QueueListener"

get_configdict()

Return a dict representation of the handler described by this class.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_configdict(self) -> dict[str, dict[str, t.Any]]:
    """Return a dict representation of the handler described by this class."""
    listener_dict: dict[str, dict[str, t.Any]] = {
        self.name: {
            "class": self.get_handler_class(),
            "queue": self.queue,
            "handlers": self.handlers,
        }
    }
    return listener_dict

get_handler_class()

Return the logging handler class this class represents.

Returns:

Type Description
str

logging.handlers.QueueListener.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_handler_class(self) -> str:
    """Return the logging handler class this class represents.

    Returns:
        (str): `logging.handlers.QueueListener`.

    """
    return "logging.handleres.QueueListener"

RotatingFileHandlerConfig dataclass

Bases: BaseHandlerConfig

Define a logging RotatingFileHandler.

Parameters:

Name Type Description Default
filename str | None

The name/path of the file to log messages to.

'app.log'
maxBytes int

The maximum size of the file (in bytes) before a new file is rotated.

0
backupCount int

Number of rotated log files to keep.

0
Source code in src/red_logging/config_classes/handlers/_handlers.py
@dataclass
class RotatingFileHandlerConfig(BaseHandlerConfig):
    """Define a logging RotatingFileHandler.

    Params:
        filename (str | None): The name/path of the file to log messages to.
        maxBytes (int): The maximum size of the file (in bytes) before a new file is rotated.
        backupCount (int): Number of rotated log files to keep.

    """

    filename: str | None = field(default="app.log")
    maxBytes: int = 0
    backupCount: int = 0

    def get_configdict(self) -> dict[str, dict[str, t.Any]]:
        """Return a dict representation of the handler described by this class."""
        handler_dict: dict[str, dict[str, t.Any]] = {
            self.name: {
                "class": self.get_handler_class(),
                "level": self.level,
                "formatter": self.formatter,
                "filename": f"{self.filename}",
                "maxBytes": self.maxBytes,
                "backupCount": self.backupCount,
            }
        }
        return handler_dict

    def get_handler_class(self) -> str:
        """Return the logging handler class this class represents.

        Returns:
            (str): `logging.RotatingFileHandler`.

        """
        return "logging.handlers.RotatingFileHandler"

get_configdict()

Return a dict representation of the handler described by this class.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_configdict(self) -> dict[str, dict[str, t.Any]]:
    """Return a dict representation of the handler described by this class."""
    handler_dict: dict[str, dict[str, t.Any]] = {
        self.name: {
            "class": self.get_handler_class(),
            "level": self.level,
            "formatter": self.formatter,
            "filename": f"{self.filename}",
            "maxBytes": self.maxBytes,
            "backupCount": self.backupCount,
        }
    }
    return handler_dict

get_handler_class()

Return the logging handler class this class represents.

Returns:

Type Description
str

logging.RotatingFileHandler.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_handler_class(self) -> str:
    """Return the logging handler class this class represents.

    Returns:
        (str): `logging.RotatingFileHandler`.

    """
    return "logging.handlers.RotatingFileHandler"

SocketHandlerConfig dataclass

Bases: BaseHandlerConfig

Define a logging SocketHandler.

Parameters:

Name Type Description Default
host str

Host IP/FQDN.

'localhost'
port int

Host port where log messages should be sent.

0
Source code in src/red_logging/config_classes/handlers/_handlers.py
@dataclass
class SocketHandlerConfig(BaseHandlerConfig):
    """Define a logging SocketHandler.

    Params:
        host (str): Host IP/FQDN.
        port (int): Host port where log messages should be sent.
    """

    host: str = "localhost"
    port: int = 0

    def get_configdict(self) -> dict[str, dict[str, t.Any]]:
        """Return a dict representation of the handler described by this class."""
        handler_dict: dict[str, dict[str, t.Any]] = {
            self.name: {
                "class": self.get_handler_class(),
                "level": self.level,
                "formatter": self.formatter,
                "host": self.host,
                "port": self.port,
            }
        }
        return handler_dict

    def get_handler_class(self) -> str:
        """Return the logging handler class this class represents.

        Returns:
            (str): `logging.handlers.SocketHandler`.

        """
        return "logging.handlers.SocketHandler"

get_configdict()

Return a dict representation of the handler described by this class.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_configdict(self) -> dict[str, dict[str, t.Any]]:
    """Return a dict representation of the handler described by this class."""
    handler_dict: dict[str, dict[str, t.Any]] = {
        self.name: {
            "class": self.get_handler_class(),
            "level": self.level,
            "formatter": self.formatter,
            "host": self.host,
            "port": self.port,
        }
    }
    return handler_dict

get_handler_class()

Return the logging handler class this class represents.

Returns:

Type Description
str

logging.handlers.SocketHandler.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_handler_class(self) -> str:
    """Return the logging handler class this class represents.

    Returns:
        (str): `logging.handlers.SocketHandler`.

    """
    return "logging.handlers.SocketHandler"

StreamHandlerConfig dataclass

Bases: BaseHandlerConfig

Define a logging StreamHandler.

Parameters:

Name Type Description Default
stream Any

The stream this handler controls, i.e. ext://sys.stdout, ext://sys.stderr, etc.

'ext://sys.stdout'
Source code in src/red_logging/config_classes/handlers/_handlers.py
@dataclass
class StreamHandlerConfig(BaseHandlerConfig):
    """Define a logging StreamHandler.

    Params:
        stream (Any): The stream this handler controls, i.e. `ext://sys.stdout`, `ext://sys.stderr`, etc.
    """

    level: str = "DEBUG"
    stream: t.Any | None = "ext://sys.stdout"

    def get_configdict(self) -> dict[str, dict[str, str]]:
        """Return a dict representation of the handler described by this class."""
        handler_dict: dict[str, dict[str, str]] = {
            self.name: {
                "class": self.get_handler_class(),
                "level": self.level,
                "formatter": self.formatter,
                "stream": self.stream,
            }
        }
        if self.filters:
            handler_dict["filters"] = self.filters
        return handler_dict

    def get_handler_class(self) -> str:
        """Return the logging handler class this class represents.

        Returns:
            (str): `logging.StreamHandler`.

        """
        return "logging.StreamHandler"

get_configdict()

Return a dict representation of the handler described by this class.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_configdict(self) -> dict[str, dict[str, str]]:
    """Return a dict representation of the handler described by this class."""
    handler_dict: dict[str, dict[str, str]] = {
        self.name: {
            "class": self.get_handler_class(),
            "level": self.level,
            "formatter": self.formatter,
            "stream": self.stream,
        }
    }
    if self.filters:
        handler_dict["filters"] = self.filters
    return handler_dict

get_handler_class()

Return the logging handler class this class represents.

Returns:

Type Description
str

logging.StreamHandler.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_handler_class(self) -> str:
    """Return the logging handler class this class represents.

    Returns:
        (str): `logging.StreamHandler`.

    """
    return "logging.StreamHandler"

TimedRotatingFileHandlerConfig dataclass

Bases: BaseHandlerConfig

Define a logging TimedRotatingFileHandler.

Parameters:

Name Type Description Default
filename str

The name/path of the file to log messages to.

'app.log'
when str

Time of day to rotate log files, i.e. midnight.

'midnight'
interval int

When to rotate the file as the interval defined in when occurs. 1=every occurrence, 2=every other occurrence, etc.

1
backupCount int

The number of rotated log files to save.

0
Source code in src/red_logging/config_classes/handlers/_handlers.py
@dataclass
class TimedRotatingFileHandlerConfig(BaseHandlerConfig):
    """Define a logging TimedRotatingFileHandler.

    Params:
        filename (str): The name/path of the file to log messages to.
        when (str): Time of day to rotate log files, i.e. `midnight`.
        interval (int): When to rotate the file as the interval defined in `when` occurs.
            `1=every occurrence`, `2=every other occurrence`, etc.
        backupCount (int): The number of rotated log files to save.
    """

    filename: str | None = field(default="app.log")
    when: str | None = field(default="midnight")
    interval: int = 1
    backupCount: int = 0

    def get_configdict(self) -> dict[str, dict[str, t.Any]]:
        """Return a dict representation of the handler described by this class."""
        handler_dict: dict[str, dict[str, t.Any]] = {
            self.name: {
                "class": self.get_handler_class(),
                "level": self.level,
                "formatter": self.formatter,
                "filename": self.filename,
                "when": self.when,
                "interval": self.interval,
                "backupCount": self.backupCount,
            }
        }
        return handler_dict

    def get_handler_class(self) -> str:
        """Return the logging handler class this class represents.

        Returns:
            (str): `logging.handlers.TimedRotatingFileHandler`.

        """
        return "logging.handlers.TimedRotatingFileHandler"

get_configdict()

Return a dict representation of the handler described by this class.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_configdict(self) -> dict[str, dict[str, t.Any]]:
    """Return a dict representation of the handler described by this class."""
    handler_dict: dict[str, dict[str, t.Any]] = {
        self.name: {
            "class": self.get_handler_class(),
            "level": self.level,
            "formatter": self.formatter,
            "filename": self.filename,
            "when": self.when,
            "interval": self.interval,
            "backupCount": self.backupCount,
        }
    }
    return handler_dict

get_handler_class()

Return the logging handler class this class represents.

Returns:

Type Description
str

logging.handlers.TimedRotatingFileHandler.

Source code in src/red_logging/config_classes/handlers/_handlers.py
def get_handler_class(self) -> str:
    """Return the logging handler class this class represents.

    Returns:
        (str): `logging.handlers.TimedRotatingFileHandler`.

    """
    return "logging.handlers.TimedRotatingFileHandler"

assemble_configdict(disable_existing_loggers=False, propagate=False, root_handlers=['console'], root_level='DEBUG', formatters=None, handlers=None, loggers=None)

Build a logging dictConfig dict.

Description
Example logging config dict
1
2
3
4
5
6
7
8
9
logging_config: dict = {
    "version": 1,
    "disable_existing_loggers": False,
    "propagate": True,
    "root": {},
    "formatters": {},
    "handlers": {},
    "loggers": {},
}

Parameters:

Name Type Description Default
disable_existing_loggers bool

When True, disables all currently configured loggers to "start fresh."

False
propagate bool

When True, log messages will propagate up/down to the root logger.

False
root_handlers list[str]

List of handlers for the root logger. These handler configs must exist in the logging dictConfig.

['console']
root_level str

The log level for the root logger.

'DEBUG'
formatters list[FormatterConfig] | list[dict[str, dict[str, Any]]] | None

List of logging formatter config objects.

None
handlers list[BaseHandlerConfig | dict[str, dict[str, Any]]] | None

List of logging handler config objects.

None
loggers list[LoggerConfig | LoggerFactory | dict[str, dict[str, t.Any]]]] | None

List of logging logger config objects.

None

Returns:

Type Description
dict[str, Any]

An initialized logging config dict created from inputs. Used with logging.config.dictConfig()

Source code in src/red_logging/helpers/__methods.py
def assemble_configdict(
    disable_existing_loggers: bool = False,
    propagate: bool = False,
    root_handlers: list[str] = ["console"],
    root_level: str = "DEBUG",
    formatters: (
        t.Union[list[FormatterConfig], list[LOGGING_CONFIG_DICT_TYPE_ANNOTATION]] | None
    ) = None,
    handlers: (
        t.Union[HANDLER_CLASSES_TYPE_ANNOTATION, LOGGING_CONFIG_DICT_TYPE_ANNOTATION]
        | None
    ) = None,
    loggers: (
        t.Union[
            list[t.Union[LoggerConfig, LoggerFactory]],
            list[LOGGING_CONFIG_DICT_TYPE_ANNOTATION],
        ]
        | None
    ) = None,
) -> dict[str, t.Any]:
    """Build a logging dictConfig dict.

    Description:
        ```python title="Example logging config dict" linenums="1"
        logging_config: dict = {
            "version": 1,
            "disable_existing_loggers": False,
            "propagate": True,
            "root": {},
            "formatters": {},
            "handlers": {},
            "loggers": {},
        }
        ```

    Params:
        disable_existing_loggers (bool): When `True`, disables all currently configured loggers to "start fresh."
        propagate (bool): When `True`, log messages will propagate up/down to the root logger.
        root_handlers (list[str]): List of handlers for the root logger. These handler configs must exist in the logging dictConfig.
        root_level (str): The log level for the root logger.
        formatters (list[FormatterConfig] | list[dict[str, dict[str, t.Any]]] | None): List of logging formatter config objects.
        handlers (list[BaseHandlerConfig | dict[str, dict[str, t.Any]]] | None): List of logging handler config objects.
        loggers (list[LoggerConfig | LoggerFactory | dict[str, dict[str, t.Any]]]] | None): List of logging logger config objects.

    Returns:
        (dict[str, Any]): An initialized logging config dict created from inputs. Used with `logging.config.dictConfig()`

    """
    ## Get base logging configDict object, with empty formatters, loggers, etc
    logging_config: dict[str, t.Any] = BASE_LOGGING_CONFIG_DICT

    ## Set logging config options
    logging_config["disable_existing_loggers"] = disable_existing_loggers
    logging_config["propagate"] = propagate

    ## Build root logger
    config_key_root = {
        ## Set handlers
        "handlers": root_handlers,
        ## Set log level string
        "level": root_level.upper(),
    }

    ## Update config dict's `root` key
    logging_config["root"] = config_key_root

    ## Initialize formatter, handler, logger config dicts
    formatter_configdicts: LOGGING_CONFIG_DICT_TYPE = {}
    handler_configdicts: LOGGING_CONFIG_DICT_TYPE = {}
    logger_configdicts: LOGGING_CONFIG_DICT_TYPE = {}

    if formatters is not None:
        ## Formatters passed to function, parse and add to config
        for formatter_dict in formatters:
            if isinstance(formatter_dict, dict):
                pass
            elif isinstance(formatter_dict, FormatterConfig):
                try:
                    formatter_dict: dict = formatter_dict.get_configdict()
                except Exception as exc:
                    msg = Exception(
                        f"Unhandled exception getting config dict for FormatterConfig object. Details: {exc}"
                    )
                    log.error(msg)

                    raise exc

            formatter_configdicts.update(formatter_dict)

    if handlers is not None:
        ## Handlers passed to function, parse and add to config
        for handler_dict in handlers:
            if isinstance(handler_dict, dict):
                pass
            elif isinstance(handler_dict, HANDLER_CLASSES_TYPE):
                try:
                    handler_dict = handler_dict.get_configdict()
                except Exception as exc:
                    msg = Exception(
                        f"Unhandled exception getting config dict for *HandlerConfig object. Details: {exc}"
                    )
                    log.error(msg)

                    raise exc

            handler_configdicts.update(handler_dict)

    if loggers:
        ## Loggers passed to function, parse and add to config
        for logger_dict in loggers:
            if isinstance(logger_dict, dict):
                pass
            elif isinstance(logger_dict, LoggerConfig):
                try:
                    logger_dict = logger_dict.get_configdict()
                except Exception as exc:
                    msg = Exception(
                        f"Unhandled exception getting config dict for LoggerConfig object. Details: {exc}"
                    )
                    log.error(msg)

                    raise exc

            logger_configdicts.update(logger_dict)

    ## Create a copy of the original config
    try:
        return_dict = deepcopy(logging_config)
    except Exception as exc:
        msg = Exception(
            f"Unhandled exception copying original logging config. Proceeding with original logging config"
        )
        log.warning(msg)

        return_dict = logging_config

    ## Update formatters, handlers, loggers in logging config copy
    return_dict["formatters"] = formatter_configdicts
    return_dict["handlers"] = handler_configdicts
    return_dict["loggers"] = logger_configdicts

    ## Return initialized logging config
    return return_dict

ensure_logdir(p=None)

Ensure a directory exists.

Used by logging FileHandlers (RotatingFileHandler, TimedRotatingFileHandler, etc) if a logging file path is nested, like logs/example/test.log.

Parameters:

Name Type Description Default
p str | Path

A path to a logging directory, i.e. logs/, logs/app/, logs/app/dev/. This should not be the full path to a logging config, like logs/app/test.log; instead, call this function like ensure_logdir(p=log_filename.parent).

None

Raises:

Type Description
PermissionError

When permission to create the path in p is denied.

Exception

When any unhandled exception occurs.

Source code in src/red_logging/helpers/__methods.py
def ensure_logdir(p: t.Union[str, Path] = None) -> None:
    """Ensure a directory exists.

    Used by logging FileHandlers (RotatingFileHandler, TimedRotatingFileHandler, etc) if
    a logging file path is nested, like `logs/example/test.log`.

    Params:
        p (str | Path): A path to a logging directory, i.e. `logs/`, `logs/app/`, `logs/app/dev/`.
            This should *not* be the full path to a logging config, like `logs/app/test.log`; instead,
            call this function like `ensure_logdir(p=log_filename.parent)`.

    Raises:
        PermissionError: When permission to create the path in `p` is denied.
        Exception: When any unhandled exception occurs.

    """
    p: Path = Path(f"{p}")
    if "~" in f"{p}":
        p = p.expanduser()

    if not p.exists():
        try:
            p.mkdir(parents=True, exist_ok=True)
        except PermissionError as perm_err:
            msg = Exception(f"Permission denied creating path '{p}'. Details: {exc}")
            log.error(msg)

            raise perm_err
        except Exception as exc:
            msg = Exception(
                f"Unhandled exception creating directory '{p}'. Details: {exc}"
            )
            log.error(msg)

            raise exc
    else:
        return

get_formatter_config(name='default', fmt=MESSAGE_FMT_STANDARD, datefmt=DATE_FMT_STANDARD, style='%', validate=True, as_dict=False)

Return a FormatterConfig, or a dict representing a Formatter.

Parameters:

Name Type Description Default
name str

The name for the formatter. Reference this formatter by name in a LoggerConfig.

'default'
fmt str

The string format for log messages. Python docs: Log Record Attributes

MESSAGE_FMT_STANDARD
datefmt str

The format for log message timestamps, if %(asctime)s is used in the logging fmt.

DATE_FMT_STANDARD
style str

The style of string substitution to use for the formatter. Options include % for '%', some_var, { for '{some_var}, etc.

'%'
validate bool

If True, the handler will be validated by the logging module before fully initializing.

True
as_dict bool

If True, return the configuration as a dict that can be joined into dictConfig().

False

Returns:

Type Description
dict[str, dict[str, Any]]

If as_dict=True, return a config dict instead of a FormatterConfig object.

FormatterConfig

If as_dict=False, return a FormatterConfig object.

Source code in src/red_logging/helpers/__methods.py
def get_formatter_config(
    name: str = "default",
    fmt: str = MESSAGE_FMT_STANDARD,
    datefmt: str = DATE_FMT_STANDARD,
    style: str = "%",
    validate: bool = True,
    as_dict: bool = False,
) -> dict[str, dict[str, str]] | FormatterConfig:
    """Return a FormatterConfig, or a dict representing a Formatter.

    Params:
        name (str): The name for the formatter. Reference this formatter by name in a LoggerConfig.
        fmt (str): The string format for log messages.
            [Python docs: Log Record Attributes](https://docs.python.org/3/library/logging.html#logrecord-attributes)
        datefmt (str): The format for log message timestamps, if `%(asctime)s` is used in the logging `fmt`.
        style (str): The style of string substitution to use for the formatter. Options include `%` for `'%', some_var`,
            `{` for `'{some_var}`, etc.
        validate (bool): If `True`, the handler will be validated by the logging module before fully initializing.
        as_dict (bool): If `True`, return the configuration as a dict that can be joined into `dictConfig()`.

    Returns:
        (dict[str, dict[str, Any]]): If `as_dict=True`, return a config dict instead of a FormatterConfig object.
        (FormatterConfig): If `as_dict=False`, return a FormatterConfig object.

    """
    try:
        ## Initialize formatter object
        _formatter: FormatterConfig = FormatterConfig(
            name=name, fmt=fmt, datefmt=datefmt, style=style, validate=validate
        )

        if as_dict:
            ## Return formatter representation as a dict
            return _formatter.get_configdict()
        else:
            ## Return FormatterConfig object
            return _formatter
    except Exception as exc:
        msg = Exception(f"Unhandled exception building FormatterConfig. Details: {exc}")
        log.error(msg)

        raise exc

get_logger_config(name='app', handlers=['console'], level='DEBUG', propagate=False, as_dict=False)

Return a LoggerConfig, or a dict representing a Logger.

Parameters:

Name Type Description Default
name str

The name for the logger. Reference this logger by name in a LoggerConfig.

'app'
level str

The logging level for this handler (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).

'DEBUG'
handlers list[str]

List of handler names that exist in the logging configDict that this logger should use.

['console']
propagate bool

If True, log messages will be propagated up/down to the root logger.

False
as_dict bool

If True, return the configuration as a dict that can be joined into dictConfig().

False

Returns:

Type Description
dict[str, dict[str, Any]]

If as_dict=True, return a config dict instead of a RotatingFileHandlerConfig object.

RotatingFileHandlerConfig

If as_dict=False, return a RotatingFileHandlerConfig object.

Source code in src/red_logging/helpers/__methods.py
def get_logger_config(
    name: str = "app",
    handlers: list[str] = ["console"],
    level: str = "DEBUG",
    propagate: bool = False,
    as_dict: bool = False,
) -> dict[str, dict[str, str]] | LoggerConfig:
    """Return a LoggerConfig, or a dict representing a Logger.

    Params:
        name (str): The name for the logger. Reference this logger by name in a LoggerConfig.
        level (str): The logging level for this handler (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).
        handlers (list[str]): List of handler names that exist in the logging configDict that this logger should use.
        propagate (bool): If `True`, log messages will be propagated up/down to the root logger.
        as_dict (bool): If `True`, return the configuration as a dict that can be joined into `dictConfig()`.

    Returns:
        (dict[str, dict[str, Any]]): If `as_dict=True`, return a config dict instead of a RotatingFileHandlerConfig object.
        (RotatingFileHandlerConfig): If `as_dict=False`, return a RotatingFileHandlerConfig object.

    """
    try:
        ## Initialize logger object
        _logger: LoggerConfig = LoggerConfig(
            name=name, level=level.upper(), handlers=handlers, propagate=propagate
        )

        if as_dict:
            ## Return logger representation as a dict
            return _logger.get_configdict()
        else:
            ## Return LoggerConfig object
            return _logger

    except Exception as exc:
        msg = Exception(f"Unhandled exception initializing logger. Details: {exc}")
        log.error(msg)

        raise exc

get_rotatingfilehandler_config(name='rotating_app_file', level='DEBUG', formatter='default', filters=None, filename=None, maxBytes=100000, backupCount=3, as_dict=False)

Return a RotatingFileHandlerConfig, or a dict representing a RotatingFileHandler.

Parameters:

Name Type Description Default
name str

The name for the rotating file handler. Reference this handler by name in a LoggerConfig.

'rotating_app_file'
level str

The logging level for this handler (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).

'DEBUG'
formatter str

The name of a formatter that exists in the overall logging dictConfig.

'default'
filters list[str] | None

A list of function names for logging filters.

None
filename str | Path

The full path to the log file you want to create. If parent directories do not exist, this method will handle creating them.

None
maxBytes int

The maximum size (in bytes) before a logfile is rotated.

100000
backupCount int

The number of backups to keep as log files rotate.

3
as_dict bool

If True, return the configuration as a dict that can be joined into dictConfig().

False

Returns:

Type Description
dict[str, dict[str, Any]]

If as_dict=True, return a config dict instead of a RotatingFileHandlerConfig object.

RotatingFileHandlerConfig

If as_dict=False, return a RotatingFileHandlerConfig object.

Source code in src/red_logging/helpers/__methods.py
def get_rotatingfilehandler_config(
    name: str = "rotating_app_file",
    level: str = "DEBUG",
    formatter: str = "default",
    filters: list | None = None,
    filename: t.Union[str, Path] = None,
    maxBytes: int = 100000,
    backupCount: int = 3,
    as_dict: bool = False,
) -> dict[str, dict[str, t.Any]] | RotatingFileHandlerConfig:
    """Return a RotatingFileHandlerConfig, or a dict representing a RotatingFileHandler.

    Params:
        name (str): The name for the rotating file handler. Reference this handler by name in a LoggerConfig.
        level (str): The logging level for this handler (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).
        formatter (str): The name of a formatter that exists in the overall logging dictConfig.
        filters (list[str] | None): A list of function names for logging filters.
        filename (str | Path): The full path to the log file you want to create. If parent directories do not exist,
            this method will handle creating them.
        maxBytes (int): The maximum size (in bytes) before a logfile is rotated.
        backupCount (int): The number of backups to keep as log files rotate.
        as_dict (bool): If `True`, return the configuration as a dict that can be joined into `dictConfig()`.

    Returns:
        (dict[str, dict[str, Any]]): If `as_dict=True`, return a config dict instead of a RotatingFileHandlerConfig object.
        (RotatingFileHandlerConfig): If `as_dict=False`, return a RotatingFileHandlerConfig object.

    """
    ## Convert & optionally expand input path
    filename: Path = Path(f"{filename}")
    if "~" in f"{filename}":
        filename = filename.expanduser()

    ## Create parent dirs for logging file, if they don't exist
    ensure_logdir(p=filename.parent)

    try:
        ## Initialize handler object
        _handler: RotatingFileHandlerConfig = RotatingFileHandlerConfig(
            name=name,
            level=level,
            formatter=formatter,
            filters=filters,
            filename=filename,
            maxBytes=maxBytes,
            backupCount=backupCount,
        )

        if as_dict:
            ## Return handler representation as a dict
            return _handler.get_configdict()
        else:
            ## Return RotatingFileHandlerConfig object
            return _handler

    except Exception as exc:
        msg = Exception(
            f"Unhandled exception building RotatingFileHandlerConfig. Details: {exc}"
        )
        log.error(msg)

        raise exc

get_streamhandler_config(name='console', level='INFO', formatter='default', filters=None, stream='ext://sys.stdout', as_dict=False)

Return a StreamHandlerConfig, or a dict representing a StreamingHandler.

Parameters:

Name Type Description Default
name str

The name for the stream handler. Reference this handler by name in a LoggerConfig.

'console'
level str

The logging level for this handler (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).

'INFO'
formatter str

The name of a formatter that exists in the overall logging dictConfig.

'default'
filters list[str] | None

A list of function names for logging filters.

None
stream str

The stream this handler should use, i.e. ext://sys.stdout, ext://sys.stderr, etc.

'ext://sys.stdout'
as_dict bool

If True, return the configuration as a dict that can be joined into dictConfig().

False

Returns:

Type Description
dict[str, dict[str, Any]]

If as_dict=True, return a config dict instead of a StreamHandlerConfig object.

StreamHandlerConfig

If as_dict=False, return a StreamHandlerConfig object.

Source code in src/red_logging/helpers/__methods.py
def get_streamhandler_config(
    name: str = "console",
    level: str = "INFO",
    formatter: str = "default",
    filters: list | None = None,
    stream: str = "ext://sys.stdout",
    as_dict: bool = False,
) -> dict[str, dict[str, str]] | StreamHandlerConfig:
    """Return a StreamHandlerConfig, or a dict representing a StreamingHandler.

    Params:
        name (str): The name for the stream handler. Reference this handler by name in a LoggerConfig.
        level (str): The logging level for this handler (NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL).
        formatter (str): The name of a formatter that exists in the overall logging dictConfig.
        filters (list[str] | None): A list of function names for logging filters.
        stream (str): The stream this handler should use, i.e. `ext://sys.stdout`, `ext://sys.stderr`, etc.
        as_dict (bool): If `True`, return the configuration as a dict that can be joined into `dictConfig()`.

    Returns:
        (dict[str, dict[str, Any]]): If `as_dict=True`, return a config dict instead of a StreamHandlerConfig object.
        (StreamHandlerConfig): If `as_dict=False`, return a StreamHandlerConfig object.

    """
    try:
        ## Initialize handler object
        _handler: StreamHandlerConfig = StreamHandlerConfig(
            name=name, level=level, formatter=formatter, filters=filters, stream=stream
        )

        if as_dict:
            ## Return handler representation as a dict
            return _handler.get_configdict()
        else:
            ## Return StreamHandlerConfig object
            return _handler

    except Exception as exc:
        msg = Exception(
            f"Unhandled exception building StreamHandlerConfig. Details: {exc}"
        )
        log.error(msg)

        raise exc

print_configdict(logging_config=None)

Print a logging config dict as a JSON string.

Source code in src/red_logging/helpers/__methods.py
def print_configdict(logging_config: dict = None) -> None:
    """Print a logging config dict as a JSON string."""
    assert logging_config, ValueError("Missing a logging dictConfig to print.")

    print_msg: str = json.dumps(logging_config)

    print(f"Logging config dict:\n{print_msg}")

save_configdict(logging_config=None, output_file=Path('logging_config.json'), overwrite=False)

Save a logging dictConfig to a JSON file.

Source code in src/red_logging/helpers/__methods.py
def save_configdict(
    logging_config: dict = None,
    output_file: t.Union[str, Path] = Path("logging_config.json"),
    overwrite: bool = False,
) -> None:
    """Save a logging dictConfig to a JSON file."""
    output_file: Path = Path(f"{output_file}")
    if "~" in f"{output_file}":
        output_file = output_file.expanduser()

    ensure_logdir(p=output_file.parent)

    if output_file.exists() and not overwrite:
        log.warning(
            f"Logging dictConfig already saved to file '{output_file}' and overwrite=False. Skipping."
        )

        return

    try:
        config_json = json.dumps(logging_config, indent=2)
    except Exception as exc:
        msg = Exception(
            f"Unhandled exception converting logging dict to JSON. Details: {exc}"
        )
        log.error(msg)

        raise exc

    try:
        with open(output_file, "w") as f:
            f.write(config_json)
    except PermissionError as perm_err:
        msg = Exception(
            f"Permission denied saving logging dictConfig to file '{output_file}'. Details: {perm_err}"
        )
        log.error(msg)

        raise perm_err
    except Exception as exc:
        msg = Exception(
            f"Unhandled exception saving logging dictConfig to JSON file '{output_file}'. Details: {exc}"
        )
        log.error(msg)

        raise exc