Skip to content

Utilities

Utility functions and enums for working with log levels, including parsing level names and registering custom log levels.

Quick Reference

Python
from pylogshield import LogLevel, add_log_level, PyLogShield

# Parse log level
level = LogLevel.parse("INFO")      # Returns 20
level = LogLevel.parse("warn")      # Returns 30 (alias for WARNING)
level = LogLevel.parse(10)          # Returns 10

# Get valid levels
levels = LogLevel.valid_levels()
# ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']

# Register custom level
add_log_level("SECURITY", 35, logger_cls=PyLogShield)

LogLevel Enum

Type-safe enum for standard log levels, ordered by severity.

Level Values

Level Value Description
CRITICAL 50 Critical errors, system failure
ERROR 40 Errors that need attention
WARNING 30 Warning conditions
INFO 20 Informational messages
DEBUG 10 Debug information
NOTSET 0 No level set

Parsing Levels

Python
from pylogshield import LogLevel

# Parse string names (case-insensitive)
LogLevel.parse("INFO")      # 20
LogLevel.parse("error")     # 40
LogLevel.parse("Debug")     # 10

# "WARN" is aliased to "WARNING"
LogLevel.parse("warn")      # 30
LogLevel.parse("WARN")      # 30

# Parse numeric values
LogLevel.parse(20)          # 20
LogLevel.parse("30")        # 30 (string number)

# Invalid values raise ValueError
LogLevel.parse("INVALID")   # ValueError

Comparing Levels

Python
from pylogshield import LogLevel

# Levels can be compared
LogLevel.ERROR > LogLevel.WARNING  # True
LogLevel.DEBUG < LogLevel.INFO     # True

# Check if a level is enabled
current_level = LogLevel.INFO
if LogLevel.DEBUG >= current_level:
    print("Debug is enabled")

Getting Valid Levels

Python
from pylogshield import LogLevel

# Get all valid level names
levels = LogLevel.valid_levels()
print(levels)
# ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']

# Use for validation
user_input = "INFO"
if user_input.upper() in LogLevel.valid_levels():
    level = LogLevel.parse(user_input)

Custom Log Levels

Register custom log levels with add_log_level.

Basic Usage

Python
from pylogshield import get_logger, add_log_level, PyLogShield

# Register SECURITY level (between WARNING=30 and ERROR=40)
add_log_level("SECURITY", 35, logger_cls=PyLogShield)

# Now use it
logger = get_logger("secure_app")
logger.security("Unauthorized access attempt blocked")

# With masking
logger.security({"user": "admin", "token": "abc123"}, mask=True)

Custom Levels Examples

Python
from pylogshield import PyLogShield, add_log_level, get_logger

# AUDIT level for compliance logging
add_log_level("AUDIT", 25, logger_cls=PyLogShield)

# TRACE level for detailed debugging
add_log_level("TRACE", 5, logger_cls=PyLogShield)

# ALERT level for urgent notifications
add_log_level("ALERT", 45, logger_cls=PyLogShield)

logger = get_logger("app")
logger.trace("Entering function xyz")
logger.audit("User 123 accessed resource ABC")
logger.alert("System approaching capacity limit!")

Level Method Signature

Custom level methods support the same parameters as standard methods:

Python
# Generated method signature:
# logger.security(msg, *args, mask=False, **kwargs)

logger.security("Event occurred")
logger.security("Event: %s", event_name)
logger.security({"data": "value"}, mask=True)
logger.security("Event", extra={"user_id": 123})

Utility Functions

ensure_log_dir

Create parent directories for a log file path:

Python
from pylogshield.utils import ensure_log_dir

# Creates /var/log/myapp/ if it doesn't exist
ensure_log_dir("/var/log/myapp/app.log")

# Handles None safely
ensure_log_dir(None)  # Does nothing

# Expands user home directory
ensure_log_dir("~/logs/app.log")

API Reference

utils

Utilities and LogLevel enum, plus dynamic level registration.

This module provides utility functions and enums for working with log levels, including parsing level names/values and registering custom log levels.

CLASS DESCRIPTION
LogLevel

Standard log levels as an IntEnum for type-safe level handling.

FUNCTION DESCRIPTION
add_log_level

Register a custom log level and attach a method to a logger class.

ensure_log_dir

Create the parent directory for a file path if it doesn't exist.

LogLevel

Bases: IntEnum

Standard log levels as an IntEnum for type-safe level handling.

The levels are ordered by severity (higher value = more severe): CRITICAL (50) > ERROR (40) > WARNING (30) > INFO (20) > DEBUG (10) > NOTSET (0)

ATTRIBUTE DESCRIPTION
CRITICAL

Critical level (50).

TYPE: int

ERROR

Error level (40).

TYPE: int

WARNING

Warning level (30).

TYPE: int

INFO

Info level (20).

TYPE: int

DEBUG

Debug level (10).

TYPE: int

NOTSET

Not set level (0).

TYPE: int

Examples:

Python Console Session
>>> LogLevel.parse("INFO")
20
>>> LogLevel.parse("warn")  # case-insensitive, accepts "warn" alias
30
>>> LogLevel.INFO > LogLevel.DEBUG
True
METHOD DESCRIPTION
valid_levels

Return list of valid level names in descending severity order.

parse

Parse a level name or number to an integer.

from_name

Parse a level name or number to an integer.

valid_levels() -> List[str] classmethod

Return list of valid level names in descending severity order.

RETURNS DESCRIPTION
list of str

List of level names from most to least severe.

Examples:

Python Console Session
>>> LogLevel.valid_levels()
['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']
Source code in src/pylogshield/utils.py
Python
@classmethod
def valid_levels(cls) -> List[str]:
    """Return list of valid level names in descending severity order.

    Returns
    -------
    list of str
        List of level names from most to least severe.

    Examples
    --------
    >>> LogLevel.valid_levels()
    ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']
    """
    return [level.name for level in sorted(cls, reverse=True)]

parse(value: str | int) -> int classmethod

Parse a level name or number to an integer.

PARAMETER DESCRIPTION
value

Level name (e.g., "INFO", "debug") or numeric value.

TYPE: str or int

RETURNS DESCRIPTION
int

The numeric log level.

RAISES DESCRIPTION
ValueError

If the value cannot be parsed as a valid log level.

Examples:

Python Console Session
>>> LogLevel.parse("INFO")
20
>>> LogLevel.parse("warn")  # "warn" is aliased to "WARNING"
30
>>> LogLevel.parse(10)
10
Source code in src/pylogshield/utils.py
Python
@classmethod
def parse(cls, value: str | int) -> int:
    """Parse a level name or number to an integer.

    Parameters
    ----------
    value : str or int
        Level name (e.g., "INFO", "debug") or numeric value.

    Returns
    -------
    int
        The numeric log level.

    Raises
    ------
    ValueError
        If the value cannot be parsed as a valid log level.

    Examples
    --------
    >>> LogLevel.parse("INFO")
    20
    >>> LogLevel.parse("warn")  # "warn" is aliased to "WARNING"
    30
    >>> LogLevel.parse(10)
    10
    """
    if isinstance(value, int):
        if not (logging.NOTSET <= value <= logging.CRITICAL + 100):
            raise ValueError(f"Invalid log level integer: {value}")
        return value
    val = value.strip().upper()
    if val == "WARN":
        val = "WARNING"
    if val in cls.__members__:
        return int(cls.__members__[val].value)

    try:
        return int(val)
    except ValueError:
        raise ValueError(
            f"Unknown log level: {value!r}. Valid options: {', '.join(cls.valid_levels())}"
        ) from None

from_name(value: str | int) -> int classmethod

Parse a level name or number to an integer.

This is an alias for :meth:parse kept for backward compatibility.

PARAMETER DESCRIPTION
value

Level name or numeric value.

TYPE: str or int

RETURNS DESCRIPTION
int

The numeric log level.

Source code in src/pylogshield/utils.py
Python
@classmethod
def from_name(cls, value: str | int) -> int:
    """Parse a level name or number to an integer.

    This is an alias for :meth:`parse` kept for backward compatibility.

    Parameters
    ----------
    value : str or int
        Level name or numeric value.

    Returns
    -------
    int
        The numeric log level.
    """
    return cls.parse(value)

add_log_level(name: str, value: int, *, logger_cls: Type[logging.Logger]) -> None

Register a custom log level and attach a method to a logger class.

The generated method will have the lowercase name of the level (e.g., "SECURITY" becomes .security(...)) and supports the mask parameter for sensitive data redaction.

PARAMETER DESCRIPTION

name

The name of the custom level (e.g., "SECURITY", "TRACE").

TYPE: str

value

The numeric value of the level. Should be unique and follow the convention where higher values indicate more severe levels.

TYPE: int

logger_cls

The logger class to add the method to.

TYPE: type[Logger]

RAISES DESCRIPTION
ValueError

If the logger class already has an attribute with the method name.

Examples:

Python Console Session
>>> from pylogshield import PyLogShield, add_log_level
>>> add_log_level("SECURITY", 35, logger_cls=PyLogShield)
>>> logger = get_logger("app")
>>> logger.security("Security event detected", mask=True)
Notes

This function is thread-safe. The generated method signature is: (self, msg, *args, mask: bool = False, **kwargs)

Source code in src/pylogshield/utils.py
Python
def add_log_level(name: str, value: int, *, logger_cls: Type[logging.Logger]) -> None:
    """Register a custom log level and attach a method to a logger class.

    The generated method will have the lowercase name of the level (e.g., "SECURITY"
    becomes ``.security(...)``) and supports the ``mask`` parameter for sensitive
    data redaction.

    Parameters
    ----------
    name : str
        The name of the custom level (e.g., "SECURITY", "TRACE").
    value : int
        The numeric value of the level. Should be unique and follow the
        convention where higher values indicate more severe levels.
    logger_cls : type[logging.Logger]
        The logger class to add the method to.

    Raises
    ------
    ValueError
        If the logger class already has an attribute with the method name.

    Examples
    --------
    >>> from pylogshield import PyLogShield, add_log_level
    >>> add_log_level("SECURITY", 35, logger_cls=PyLogShield)
    >>> logger = get_logger("app")
    >>> logger.security("Security event detected", mask=True)

    Notes
    -----
    This function is thread-safe. The generated method signature is:
    ``(self, msg, *args, mask: bool = False, **kwargs)``
    """
    lname = name.upper()
    method_name = lname.lower()

    with LEVEL_REGISTRY_LOCK:
        if hasattr(logger_cls, method_name):
            raise ValueError(f"Logger already has attribute {method_name!r}")

        logging.addLevelName(value, lname)

        def _log_method(
            self: logging.Logger,
            msg: Any,
            *args: Any,
            mask: bool = False,
            **kwargs: Any,
        ) -> None:
            if hasattr(self, "_log_with_processing"):
                self._log_with_processing(value, msg, *args, mask=mask, **kwargs)
            else:
                kwargs.pop("mask", None)
                if self.isEnabledFor(value):
                    self._log(value, msg, args, **kwargs)

        setattr(logger_cls, method_name, _log_method)

ensure_log_dir(path: str | None) -> None

Create the parent directory for a file path if it doesn't exist.

PARAMETER DESCRIPTION

path

The file path whose parent directory should be created. If None or empty, this function does nothing.

TYPE: str or None

Examples:

Python Console Session
>>> ensure_log_dir("/var/log/myapp/app.log")
# Creates /var/log/myapp/ if it doesn't exist
Source code in src/pylogshield/utils.py
Python
def ensure_log_dir(path: str | None) -> None:
    """Create the parent directory for a file path if it doesn't exist.

    Parameters
    ----------
    path : str or None
        The file path whose parent directory should be created.
        If None or empty, this function does nothing.

    Examples
    --------
    >>> ensure_log_dir("/var/log/myapp/app.log")
    # Creates /var/log/myapp/ if it doesn't exist
    """
    if not path:
        return
    from pathlib import Path

    p = Path(path).expanduser().resolve()
    p.parent.mkdir(parents=True, exist_ok=True)