API Reference

Complete API documentation for Apathetic Python Logger.

Note: This library uses camelCase naming to match Python’s standard library logging module conventions.

By default, this library provides improved behavior with enhancements over the standard library. For stdlib-compatible behavior with no breaking changes, enable Compatibility Mode.

Quick Reference: apathetic_logging Module Functions

New Functions

Function Summary
getLoggerOfType() Get a logger of the specified type, creating it if necessary
registerLogger() Register a logger for use by getLogger()
registerCompatibilityMode() Register the compatibility mode setting for stdlib drop-in replacement
getCompatibilityMode() Get the compatibility mode setting
registerLogLevelEnvVars() Register environment variable names to check for log level
registerDefaultLogLevel() Register the default log level to use when no other source is found
registerTargetPythonVersion() Register the target Python version for compatibility checking
registerPropagate() Register the propagate setting for loggers
safeLog() Emergency logger that never fails
safeTrace() Debug tracing function for test development
makeSafeTrace() Create a test trace function with a custom icon
hasLogger() Check if a logger exists in the logging manager’s registry
removeLogger() Remove a logger from the logging manager’s registry
getDefaultLoggerName() Get default logger name with optional inference from caller’s frame
getLogLevelEnvVars() Get the environment variable names to check for log level
getDefaultLogLevel() Get the default log level
getRegisteredLoggerName() Get the registered logger name
getRootLogger() Return the root logger instance (primary way to access root logger)
getTargetPythonVersion() Get the target Python version
getDefaultPropagate() Get the default propagate setting
getLevelNumber() Convert a log level name to its numeric value
getLevelNameStr() Convert a log level to its string name (always returns string)
test() Log a message at TEST level

Changed Functions

Function Summary
getLogger() Return the registered logger instance (auto-infers name when None)
getLevelName() Get the level name for a numeric level (extended version that always returns string)

Unchanged Functions

Function Summary
basicConfig() Configure logging system
addLevelName() Associate a level name with a numeric level
getLevelNamesMapping() 3.11+: Get mapping of level names to numeric values
getLoggerClass() Return the class to be used when instantiating a logger
setLoggerClass() Set the class to be used when instantiating a logger
getLogRecordFactory() Return the factory function used to create LogRecords
setLogRecordFactory() Set the factory function used to create LogRecords
shutdown() Perform an orderly shutdown of the logging system
disable() Disable all logging calls of severity ‘level’ and below
captureWarnings() Capture warnings issued by the warnings module
critical() Log a message with severity CRITICAL
debug() Log a message with severity DEBUG
detail() Log a message with severity DETAIL
error() Log a message with severity ERROR
exception() Log a message with severity ERROR with exception info
fatal() Log a message with severity CRITICAL
info() Log a message with severity INFO
log() Log a message with an explicit level
brief() Log a message with severity BRIEF
test() Log a message with severity TEST
trace() Log a message with severity TRACE
warn() Log a message with severity WARNING
warning() Log a message with severity WARNING
getHandlerByName() 3.12+: Get a handler with the specified name
getHandlerNames() 3.12+: Return all known handler names
makeLogRecord() Create a LogRecord from the given parameters
currentframe() Return the frame object for the caller’s stack frame

Quick Reference: apathetic_logging.Logger Class Methods

New Methods

Method Summary
determineLogLevel() Resolve log level from CLI → env → root config → default
determineColorEnabled() Return True if colored output should be enabled (classmethod)
extendLoggingModule() Extend Python’s logging module with TRACE and SILENT levels (classmethod)
trace() Log a message at TRACE level
detail() Log a message at DETAIL level
brief() Log a message at BRIEF level
test() Log a message at TEST level
errorIfNotDebug() Log an error with full traceback only if debug/trace is enabled
criticalIfNotDebug() Log a critical error with full traceback only if debug/trace is enabled
colorize() Apply ANSI color codes to text
logDynamic() Log a message at a dynamically specified level
useLevel() Context manager to temporarily change log level
useLevelMinimum() Context manager to temporarily change log level (only if more verbose)
usePropagate() Context manager to temporarily change propagate setting
setLevelAndPropagate() Set level and propagate together with smart defaults
useLevelAndPropagate() Context manager to temporarily set level and propagate together
setLevelMinimum() Set level only if it’s more verbose than current level
setLevelInherit() Set logger to inherit level from parent
levelName Return the explicit level name set on this logger (property)
effectiveLevel Return the effective level (what’s actually used) (property)
effectiveLevelName Return the effective level name (what’s actually used) (property)
getLevel() Return the explicit level set on this logger
getLevelName() Return the explicit level name set on this logger
getEffectiveLevelName() Return the effective level name (what’s actually used)
ensureHandlers() Ensure handlers are attached to this logger
validateLevel() Validate that a level value is positive (> 0) (staticmethod)

Changed Methods

Method Summary
setLevel() Set the logging level (accepts string names and has minimum parameter)
_log() Log a message with the specified level (automatically ensures handlers)
addLevelName() Associate a level name with a numeric level (validates level > 0) (staticmethod)

Unchanged Methods

All other methods from logging.Logger are inherited unchanged. See the Python logging.Logger documentation for the complete list.

Version-specific methods:

apathetic_logging Function Reference

getLogger

getLogger(
    logger_name: str | None = None,
    *,
    level: str | int | None = None,
    minimum: bool | None = None
) -> Logger

Return the registered logger instance.

Uses Python’s built-in logging registry (logging.getLogger()) to retrieve the logger. If no logger name is provided, uses the registered logger name or attempts to auto-infer the logger name from the calling module’s top-level package.

Behavior: When logger_name is None, the logger name is auto-inferred from the calling module (improved behavior). To get the root logger, use getLogger("") instead. In Compatibility Mode, getLogger(None) returns the root logger (stdlib behavior).

Parameters:

Parameter Type Description
logger_name str | None Optional logger name. If not provided, uses the registered logger name or auto-infers from the calling module. Use "" to get the root logger.
level str | int | None Exact log level to set on the logger. Accepts both string names (case-insensitive) and numeric values. If provided, sets the logger’s level to this value. Defaults to None (no change).
minimum bool | None If True, only set the level if it’s more verbose (lower numeric value) than the current level. This prevents downgrading from a more verbose level (e.g., TRACE) to a less verbose one (e.g., DEBUG). If None, defaults to False. Only used when level is provided.

Returns:

Raises:

Example:

from apathetic_logging import getLogger, registerLogger

# Using registered logger name
registerLogger("my_app")
logger = getLogger()  # Gets "my_app" logger

# Or specify logger name directly
logger = getLogger("my_app")  # Gets "my_app" logger

# Set exact log level
logger = getLogger("my_app", level="debug")  # Sets level to DEBUG

# Set minimum log level (only if current is less verbose)
logger = getLogger("my_app", level="info", minimum=True)  # At least INFO

# To get root logger (use "" instead of None)
logger = getLogger("")  # Returns root logger

getRootLogger

getRootLogger() -> Logger

Return the root logger instance.

This is the primary way to access the root logger. It’s more explicit and discoverable than using logging.getLogger("") or getLogger("").

Returns:

Example:

from apathetic_logging import getRootLogger

# Get root logger
root = getRootLogger()

# Set root level
root.setLevel("debug")

# Log to root logger
root.info("This logs to the root logger")

# Access root properties
print(root.levelName)

getLoggerOfType

getLoggerOfType(
    name: str | None,
    class_type: type[Logger],
    skip_frames: int = 1,
    *args: Any,
    level: str | int | None = None,
    minimum: bool | None = None,
    **kwargs: Any
) -> Logger

Get a logger of the specified type, creating it if necessary.

Parameters:

Parameter Type Description
name str | None The name of the logger to get. If None, auto-infers from the calling module. Use "" for root logger.
class_type type[Logger] The logger class type to use.
skip_frames int Number of frames to skip when inferring logger name (default: 1).
*args Any Additional positional arguments (for future-proofing)
level str | int | None Exact log level to set on the logger. Accepts both string names (case-insensitive) and numeric values. If provided, sets the logger’s level to this value. Defaults to None (no change).
minimum bool | None If True, only set the level if it’s more verbose (lower numeric value) than the current level. This prevents downgrading from a more verbose level (e.g., TRACE) to a less verbose one (e.g., DEBUG). If None, defaults to False. Only used when level is provided.
**kwargs Any Additional keyword arguments (for future-proofing)

Returns:

Raises:

Example:

from apathetic_logging import Logger, getLoggerOfType

class AppLogger(Logger):
    pass

logger = getLoggerOfType("my_app", AppLogger)

# Set exact log level
logger = getLoggerOfType("my_app", AppLogger, level="debug")

# Set minimum log level
logger = getLoggerOfType("my_app", AppLogger, level="info", minimum=True)

registerLogger

registerLogger(
    logger_name: str | None = None,
    logger_class: type[Logger] | None = None,
    *,
    target_python_version: tuple[int, int] | None = None,
    log_level_env_vars: list[str] | None = None,
    default_log_level: str | None = None,
    propagate: bool | None = None,
    compat_mode: bool | None = None
) -> None

Register a logger for use by getLogger(). This registers the logger name and extends the logging module with custom levels if needed.

If logger_name is not provided, the top-level package is automatically extracted from the calling module’s __package__ attribute.

If logger_class is provided and has an extendLoggingModule() method, it will be called to extend the logging module with custom levels and set the logger class. If logger_class is not provided, the default Logger class will be used.

Parameters:

Parameter Type Description
logger_name str | None The logger name to register. If None, auto-infers from the calling module.
logger_class type[Logger] | None Optional logger class to use. If provided and the class has an extendLoggingModule() method, it will be called. If None, defaults to the standard Logger class.
target_python_version tuple[int, int] | None Optional target Python version (major, minor) tuple. If provided, sets the target Python version in the registry permanently. Defaults to None (no change).
log_level_env_vars list[str] | None Optional list of environment variable names to check for log level. If provided, sets the log level environment variables in the registry permanently. Defaults to None (no change).
default_log_level str | None Optional default log level name. If provided, sets the default log level in the registry permanently. Defaults to None (no change).
propagate bool | None Optional propagate setting. If provided, sets the propagate value in the registry permanently. If None, uses registered propagate setting or falls back to DEFAULT_PROPAGATE from constants.py. Defaults to None (no change).
compat_mode bool | None Optional compatibility mode setting. If provided, sets the compatibility mode in the registry permanently. When True, enables stdlib-compatible behavior with no breaking changes (e.g., getLogger(None) returns root logger). When False (default), uses improved behavior with enhancements. If None, uses registered compatibility mode setting or defaults to False. Defaults to None (no change).

Example:

from apathetic_logging import registerLogger

registerLogger("my_app")
# Or let it auto-infer:
registerLogger()  # Uses top-level package name

# Or with a custom logger class:
from apathetic_logging import Logger

class AppLogger(Logger):
    pass

registerLogger("my_app", AppLogger)

# Or with convenience parameters to configure registry settings:
registerLogger(
    "my_app",
    target_python_version=(3, 10),
    log_level_env_vars=["MYAPP_LOG_LEVEL", "LOG_LEVEL"],
    default_log_level="info",
    propagate=False,
    compat_mode=True,  # Enable stdlib compatibility
)

registerCompatibilityMode

registerCompatibilityMode(compat_mode: bool) -> None

Register the compatibility mode setting for stdlib drop-in replacement.

This sets the compatibility mode that will be used when creating loggers. If not set, the library defaults to False (improved behavior with enhancements).

Compatibility Modes:

Parameters:

Parameter Type Description
compat_mode bool Compatibility mode setting. When True, enables stdlib-compatible behavior with no breaking changes. When False (default), uses improved behavior with enhancements.

Example:

from apathetic_logging import registerCompatibilityMode

# Enable stdlib-compatible behavior (no breaking changes)
registerCompatibilityMode(compat_mode=True)
# Now getLogger(None) returns root logger (stdlib behavior)

# Use improved behavior with enhancements (default)
registerCompatibilityMode(compat_mode=False)
# Now getLogger(None) auto-infers logger name (improved behavior)

getCompatibilityMode

getCompatibilityMode() -> bool

Get the compatibility mode setting.

Returns the registered compatibility mode setting, or False (improved behavior) if not registered.

Returns:

Example:

from apathetic_logging import getCompatibilityMode

compat_mode = getCompatibilityMode()
print(compat_mode)  # False (default: improved behavior)

registerLogLevelEnvVars

registerLogLevelEnvVars(env_vars: list[str]) -> None

Register environment variable names to check for log level.

The environment variables will be checked in order, and the first non-empty value found will be used.

Parameters:

Parameter Type Description
env_vars list[str] List of environment variable names to check (e.g., ["MYAPP_LOG_LEVEL", "LOG_LEVEL"])

Example:

from apathetic_logging import registerLogLevelEnvVars

registerLogLevelEnvVars(["MYAPP_LOG_LEVEL", "LOG_LEVEL"])

registerDefaultLogLevel

registerDefaultLogLevel(default_level: str) -> None

Register the default log level to use when no other source is found.

Parameters:

Parameter Type Description
default_level str Default log level name (e.g., "info", "warning")

Example:

from apathetic_logging import registerDefaultLogLevel

registerDefaultLogLevel("warning")

registerTargetPythonVersion

registerTargetPythonVersion(version: tuple[int, int] | None) -> None

Register the target Python version for compatibility checking.

This sets the global target Python version that the library will use when checking for feature availability. Functions requiring a newer Python version than the registered target will raise a NotImplementedError, even if the runtime Python version is sufficient.

If not set, the library defaults to TARGET_PYTHON_VERSION (3.10) from constants.py.

Parameters:

Parameter Type Description
version tuple[int, int] | None A tuple (major, minor) representing the target Python version (e.g., (3, 10) for Python 3.10). If None, the registration is skipped.

Example:

from apathetic_logging import registerTargetPythonVersion

# Target Python 3.10
registerTargetPythonVersion((3, 10))

# Target Python 3.11
registerTargetPythonVersion((3, 11))

Note: This is useful when developing on a newer Python version (e.g., 3.12) but targeting an older version (e.g., 3.10). The library will validate function calls against your target version, preventing accidental use of features that don’t exist in your target environment.

registerPropagate

registerPropagate(*, propagate: bool | None) -> None

Register the propagate setting for loggers.

This sets the default propagate value that will be used when creating loggers. If not set, the library defaults to DEFAULT_PROPAGATE (False) from constants.py.

When propagate is False, loggers do not propagate messages to parent loggers, avoiding duplicate root logs.

Parameters:

Parameter Type Description
propagate bool | None Propagate setting (True or False). If None, the registration is skipped.

Example:

from apathetic_logging import registerPropagate

# Enable propagation (messages propagate to parent loggers)
registerPropagate(propagate=True)

# Disable propagation (default, avoids duplicate logs)
registerPropagate(propagate=False)

safeLog

safeLog(msg: str) -> None

Emergency logger that never fails.

This function bypasses the normal logging system and writes directly to sys.__stderr__. It’s designed for use in error handlers where the logging system itself might be broken.

Parameters:

Parameter Type Description
msg str Message to log

Example:

from apathetic_logging import safeLog

try:
    # Some operation
    pass
except Exception:
    safeLog("Critical error: logging system may be broken")

safeTrace

safeTrace(label: str, *args: Any, icon: str = "đŸ§Ș") -> None

Debug tracing function for test development. Only active when safe trace is enabled via environment variables.

Safe trace is enabled when any of the following conditions are met:

  1. SAFE_TRACE environment variable is set to "1", "true", or "yes" (case insensitive)
  2. LOG_LEVEL environment variable (case insensitive) is set to "TRACE" or "TEST"
  3. LOG_LEVEL numeric value is less than or equal to TRACE_LEVEL (supports both numeric strings like "5" and standard logging level names like "DEBUG")

Parameters:

Parameter Type Description
label str Trace label
*args Any Additional arguments to trace
icon str Icon to use (default: "đŸ§Ș")

Example:

from apathetic_logging import safeTrace
import os

# Enable via SAFE_TRACE
os.environ["SAFE_TRACE"] = "1"
safeTrace("debug", "message")  # Will output

# Enable via LOG_LEVEL
os.environ["LOG_LEVEL"] = "TRACE"
safeTrace("debug", "message")  # Will output

# Or via numeric LOG_LEVEL
os.environ["LOG_LEVEL"] = "5"  # TRACE_LEVEL is 5
safeTrace("debug", "message")  # Will output

makeSafeTrace

makeSafeTrace(icon: str = "đŸ§Ș") -> Callable

Create a test trace function with a custom icon.

Parameters:

Parameter Type Description
icon str Icon to use

Returns:

hasLogger

hasLogger(logger_name: str) -> bool

Check if a logger exists in the logging manager’s registry.

Parameters:

Parameter Type Description
logger_name str The name of the logger to check

Returns:

removeLogger

removeLogger(logger_name: str) -> None

Remove a logger from the logging manager’s registry.

Parameters:

Parameter Type Description
logger_name str The name of the logger to remove

getDefaultLoggerName

getDefaultLoggerName(
    logger_name: str | None = None,
    *,
    check_registry: bool = True,
    skip_frames: int = 1,
    raise_on_error: bool = False,
    infer: bool = True,
    register: bool = False
) -> str | None

Get default logger name with optional inference from caller’s frame.

Parameters:

Parameter Type Description
logger_name str | None Explicit logger name, or None to infer (default: None)
check_registry bool If True, check registry before inferring (default: True)
skip_frames int Number of frames to skip (default: 1)
raise_on_error bool If True, raise RuntimeError if logger name cannot be resolved. If False (default), return None instead
infer bool If True (default), attempt to infer logger name from caller’s frame when not found in registry. If False, skip inference
register bool If True, store inferred name in registry. If False (default), do not modify registry. Note: Explicit names are never stored regardless of this parameter

Returns:

Raises:

getLogLevelEnvVars

getLogLevelEnvVars() -> list[str]

Get the environment variable names to check for log level.

Returns the registered environment variable names, or the default environment variables if none are registered.

Returns:

getDefaultLogLevel

getDefaultLogLevel() -> str

Get the default log level.

Returns the registered default log level, or the module default if none is registered.

Returns:

getRegisteredLoggerName

getRegisteredLoggerName() -> str | None

Get the registered logger name.

Returns the registered logger name, or None if no logger name has been registered. Unlike getDefaultLoggerName(), this does not perform inference - it only returns the explicitly registered value.

Returns:

getTargetPythonVersion

getTargetPythonVersion() -> tuple[int, int]

Get the target Python version.

Returns the registered target Python version, or the minimum supported version if none is registered.

Returns:

getDefaultPropagate

getDefaultPropagate() -> bool

Get the default propagate setting.

Returns the registered propagate setting, or the module default if none is registered.

Returns:

basicConfig

basicConfig(*args: Any, **kwargs: Any) -> None

Wrapper for logging.basicConfig(). Configure the logging system.

For detailed documentation, see the Python logging.basicConfig() documentation.

addLevelName

addLevelName(level: int, level_name: str) -> None

Wrapper for logging.addLevelName(). Associate a level name with a numeric level.

For detailed documentation, see the Python logging.addLevelName() documentation.

getLevelName

getLevelName(level: int | str, *, strict: bool = False) -> str | int

Return the textual or numeric representation of a logging level.

Behavior depends on compatibility mode (set via registerCompatibilityMode()):

Compatibility mode enabled (compat_mode=True):

Compatibility mode disabled (compat_mode=False, default):

Parameters:

Parameter Type Description
level int | str Log level as integer or string name
strict bool If True, raise ValueError for unknown levels. If False (default), returns “Level {level}” format for unknown integer levels (matching stdlib behavior). Only used when compatibility mode is disabled and level is an integer.

Returns:

Raises:

Example:

from apathetic_logging import getLevelName, getLevelNumber, registerCompatibilityMode

# Compatibility mode enabled (stdlib-like behavior):
registerCompatibilityMode(compat_mode=True)
getLevelName(10)  # "DEBUG" (str)
getLevelName("DEBUG")  # 10 (int)
getLevelName("debug")  # 10 (int, case-insensitive)

# Compatibility mode disabled (improved behavior):
registerCompatibilityMode(compat_mode=False)
getLevelName(10)  # "DEBUG"
getLevelName("DEBUG")  # "DEBUG" (uppercased and returned)
getLevelName("debug")  # "DEBUG" (uppercased)
getLevelName(999, strict=True)  # ValueError: Unknown log level: 999

# For string→int conversion when compat mode disabled, use getLevelNumber()
getLevelNumber("DEBUG")  # 10

getLevelNumber

getLevelNumber(level: str | int) -> int

Convert a log level name to its numeric value.

Recommended way to convert string level names to integers. This function explicitly performs string→int conversion, unlike getLevelName() which has bidirectional behavior for backward compatibility.

Handles all levels registered via logging.addLevelName() (including standard library levels, custom apathetic levels, and user-registered levels).

Parameters:

Parameter Type Description
level str | int Log level as string name (case-insensitive) or integer

Returns:

Raises:

Example:

from apathetic_logging import getLevelNumber

# Known levels return int
getLevelNumber("DEBUG")  # 10
getLevelNumber("TRACE")  # 5
getLevelNumber(20)       # 20

# Unknown level raises ValueError
getLevelNumber("UNKNOWN")  # ValueError: Unknown log level: 'UNKNOWN'

See Also:

getLevelNameStr

getLevelNameStr(level: int | str, *, strict: bool = False) -> str

Convert a log level to its string name representation.

Unidirectional function that always returns a string. This is the recommended way to convert log levels to strings when you want guaranteed string output without compatibility mode behavior.

Unlike getLevelName() which has compatibility mode and bidirectional behavior, this function always returns a string:

Handles all levels registered via logging.addLevelName() (including standard library levels, custom apathetic levels, and user-registered levels).

Parameters:

Parameter Type Description
level int | str Log level as integer or string name (case-insensitive)
strict bool If True, raise ValueError for unknown integer levels. If False (default), returns “Level {level}” format for unknown integer levels (matching stdlib behavior).

Returns:

Raises:

Example:

from apathetic_logging import getLevelNameStr

# Integer input converts to level name
getLevelNameStr(10)  # "DEBUG"
getLevelNameStr(5)   # "TRACE"
getLevelNameStr(20)  # "INFO"

# String input validates and returns uppercased string
getLevelNameStr("DEBUG")  # "DEBUG"
getLevelNameStr("debug")  # "DEBUG"
getLevelNameStr("Info")  # "INFO"

# Unknown integer levels return "Level {level}" format (strict=False, default)
getLevelNameStr(999)  # "Level 999"

# Unknown integer levels raise ValueError when strict=True
getLevelNameStr(999, strict=True)  # ValueError: Unknown log level: 999

# Unknown string input raises ValueError
getLevelNameStr("UNKNOWN")  # ValueError: Unknown log level: 'UNKNOWN'

See Also:

getLevelNamesMapping

getLevelNamesMapping() -> dict[int, str]

Requires Python 3.11+

Wrapper for logging.getLevelNamesMapping(). Get mapping of level names to numeric values.

For detailed documentation, see the Python logging.getLevelNamesMapping() documentation.

getLoggerClass

getLoggerClass() -> type[logging.Logger]

Wrapper for logging.getLoggerClass(). Return the class to be used when instantiating a logger.

For detailed documentation, see the Python logging.getLoggerClass() documentation.

setLoggerClass

setLoggerClass(klass: type[logging.Logger]) -> None

Wrapper for logging.setLoggerClass(). Set the class to be used when instantiating a logger.

For detailed documentation, see the Python logging.setLoggerClass() documentation.

getLogRecordFactory

getLogRecordFactory() -> Callable

Wrapper for logging.getLogRecordFactory(). Return the factory function used to create LogRecords.

For detailed documentation, see the Python logging.getLogRecordFactory() documentation.

setLogRecordFactory

setLogRecordFactory(factory: Callable) -> None

Wrapper for logging.setLogRecordFactory(). Set the factory function used to create LogRecords.

For detailed documentation, see the Python logging.setLogRecordFactory() documentation.

shutdown

shutdown() -> None

Wrapper for logging.shutdown(). Perform an orderly shutdown of the logging system.

For detailed documentation, see the Python logging.shutdown() documentation.

disable

disable(level: int) -> None

Wrapper for logging.disable(). Disable all logging calls of severity ‘level’ and below.

For detailed documentation, see the Python logging.disable() documentation.

captureWarnings

captureWarnings(capture: bool) -> None

Wrapper for logging.captureWarnings(). Capture warnings issued by the warnings module.

For detailed documentation, see the Python logging.captureWarnings() documentation.

critical

critical(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.critical(). Log a message with severity CRITICAL.

For detailed documentation, see the Python logging.critical() documentation.

debug

debug(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.debug(). Log a message with severity DEBUG.

For detailed documentation, see the Python logging.debug() documentation.

error

error(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.error(). Log a message with severity ERROR.

For detailed documentation, see the Python logging.error() documentation.

exception

exception(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.exception(). Log a message with severity ERROR with exception info.

For detailed documentation, see the Python logging.exception() documentation.

fatal

fatal(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.fatal(). Log a message with severity CRITICAL.

For detailed documentation, see the Python logging.fatal() documentation.

info

info(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.info(). Log a message with severity INFO.

For detailed documentation, see the Python logging.info() documentation.

log

log(level: int, msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.log(). Log a message with an explicit level.

For detailed documentation, see the Python logging.log() documentation.

warn

warn(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.warn(). Log a message with severity WARNING.

For detailed documentation, see the Python logging.warn() documentation.

warning

warning(msg: str, *args: Any, **kwargs: Any) -> None

Wrapper for logging.warning(). Log a message with severity WARNING.

For detailed documentation, see the Python logging.warning() documentation.

trace

trace(msg: str, *args: Any, **kwargs: Any) -> None

Log a message with severity TRACE on the root logger.

TRACE is more verbose than DEBUG. This function gets an apathetic_logging.Logger instance (ensuring the root logger is an apathetic logger) and calls its trace() method.

If the logger has no handlers, basicConfig() is automatically called to add a console handler with a pre-defined format.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments for message formatting
**kwargs Any Additional keyword arguments (e.g., exc_info, stacklevel)

Example:

import apathetic_logging

# Log a trace message
apathetic_logging.trace("Detailed trace information: %s", variable)

detail

detail(msg: str, *args: Any, **kwargs: Any) -> None

Log a message with severity DETAIL on the root logger.

DETAIL is more detailed than INFO. This function gets an apathetic_logging.Logger instance (ensuring the root logger is an apathetic logger) and calls its detail() method.

If the logger has no handlers, basicConfig() is automatically called to add a console handler with a pre-defined format.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments for message formatting
**kwargs Any Additional keyword arguments (e.g., exc_info, stacklevel)

Example:

import apathetic_logging

# Log a detail message
apathetic_logging.detail("Additional detail: %s", information)

brief

brief(msg: str, *args: Any, **kwargs: Any) -> None

Log a message with severity BRIEF on the root logger.

BRIEF is less detailed than INFO. This function gets an apathetic_logging.Logger instance (ensuring the root logger is an apathetic logger) and calls its brief() method.

If the logger has no handlers, basicConfig() is automatically called to add a console handler with a pre-defined format.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments for message formatting
**kwargs Any Additional keyword arguments (e.g., exc_info, stacklevel)

Example:

import apathetic_logging

# Log a brief message
apathetic_logging.brief("brief information: %s", summary)

test

test(msg: str, *args: Any, **kwargs: Any) -> None

Log a message at TEST level.

TEST is the most verbose level and bypasses capture. This function gets an apathetic_logging.Logger instance (ensuring the root logger is an apathetic logger) and calls its test() method.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments
**kwargs Any Additional keyword arguments

Example:

import apathetic_logging

# Log a test message
apathetic_logging.test("Test message: %s", variable)

getHandlerByName

getHandlerByName(name: str) -> logging.Handler | None

Requires Python 3.12+

Wrapper for logging.getHandlerByName(). Get a handler with the specified name.

For detailed documentation, see the Python logging.getHandlerByName() documentation.

getHandlerNames

getHandlerNames() -> list[str]

Requires Python 3.12+

Wrapper for logging.getHandlerNames(). Return all known handler names.

For detailed documentation, see the Python logging.getHandlerNames() documentation.

makeLogRecord

makeLogRecord(
    name: str,
    level: int,
    fn: str,
    lno: int,
    msg: str,
    args: tuple,
    exc_info: Any
) -> logging.LogRecord

Wrapper for logging.makeLogRecord(). Create a LogRecord from the given parameters.

For detailed documentation, see the Python logging.makeLogRecord() documentation.

currentframe

currentframe(*args: Any, **kwargs: Any) -> FrameType | None

Wrapper for logging.currentframe(). Return the frame object for the caller’s stack frame.

For detailed documentation, see the Python logging.currentframe() documentation.

4. apathetic_logging.Logger Function Reference

Constructor

Logger(
    name: str,
    level: int = logging.NOTSET,
    *,
    enable_color: bool | None = None
)

Logger class for all Apathetic tools. Extends Python’s standard logging.Logger.

Parameters:

Parameter Type Description
name str Logger name
level int Initial log level (defaults to INHERIT_LEVEL (i.e. logging.NOTSET))
enable_color bool | None Whether to enable colorized output. If None, auto-detects based on TTY and environment variables.

setLevel

setLevel(level: int | str, *, minimum: bool | None = False, allow_inherit: bool = False) -> None

Set the logging level. Accepts both string names (case-insensitive) and numeric values.

Changed from stdlib: Accepts string level names and has minimum and allow_inherit parameters. In improved mode (default), validates that levels are > 0 to prevent accidental INHERIT_LEVEL (i.e. NOTSET) inheritance. In compatibility mode, accepts any level value (including 0 and negative) matching stdlib behavior.

Parameters:

Parameter Type Description
level int | str Log level name or numeric value
minimum bool | None If True, only set the level if it’s more verbose (lower numeric value) than the current level. Defaults to False.
allow_inherit bool If True, allows setting level to 0 (INHERIT_LEVEL, i.e. NOTSET) in improved mode. In compatibility mode, this parameter is ignored and 0 is always accepted. Defaults to False.

Behavior:

Example:

logger.setLevel("debug")
logger.setLevel(logging.DEBUG)

# Set to INHERIT_LEVEL (i.e. NOTSET) (inherits from parent) - requires explicit allow_inherit=True
logger.setLevel(0, allow_inherit=True)

# In compatibility mode, INHERIT_LEVEL (i.e. NOTSET) is accepted without allow_inherit
from apathetic_logging import registerCompatibilityMode
registerCompatibilityMode(compat_mode=True)
logger.setLevel(0)  # Works in compat mode

setLevelMinimum

setLevelMinimum(level: int | str) -> None

Set the logging level only if it’s more verbose than the current level.

This convenience method is equivalent to calling setLevel(level, minimum=True). It prevents downgrading from a more verbose level (e.g., TRACE) to a less verbose one (e.g., DEBUG).

Parameters:

Parameter Type Description
level int | str Log level name or numeric value. Only set if it’s more verbose (lower numeric value) than the current effective level.

Example:

logger.setLevel("TRACE")
# This won't downgrade from TRACE to DEBUG
logger.setLevelMinimum("DEBUG")
assert logger.levelName == "TRACE"  # Still TRACE

# This will upgrade from INFO to DEBUG
logger.setLevel("INFO")
logger.setLevelMinimum("DEBUG")
assert logger.levelName == "DEBUG"  # Upgraded to DEBUG

setLevelInherit

setLevelInherit() -> None

Set the logger to inherit its level from the parent logger.

This convenience method is equivalent to calling setLevel(NOTSET, allow_inherit=True). It explicitly sets the logger to INHERIT_LEVEL (i.e. NOTSET) so it inherits its effective level from the root logger or parent logger.

Example:

logger.setLevel("DEBUG")
# Set to inherit from root logger
logger.setLevelInherit()
assert logger.levelName == "NOTSET"
assert logger.effectiveLevel == root.level  # Inherits from root

determineLogLevel

determineLogLevel(
    *,
    args: argparse.Namespace | None = None,
    root_log_level: str | None = None
) -> str

Resolve log level from CLI → env → root config → default.

Parameters:

Parameter Type Description
args argparse.Namespace | None Parsed command-line arguments (checks for args.log_level)
root_log_level str | None Root logger level to use as fallback

Returns:

Example:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--log-level", default="info")
args = parser.parse_args()

level = logger.determineLogLevel(args=args)
logger.setLevel(level)

determineColorEnabled

determineColorEnabled() -> bool

Return True if colored output should be enabled. (classmethod)

Checks:

Returns:

extendLoggingModule

extendLoggingModule() -> bool

Extend Python’s logging module with TRACE and SILENT levels. (classmethod)

This method:

Returns:

trace

trace(msg: str, *args: Any, **kwargs: Any) -> None

Log a message at TRACE level.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments
**kwargs Any Additional keyword arguments (e.g., exc_info, stacklevel)

detail

detail(msg: str, *args: Any, **kwargs: Any) -> None

Log a message at DETAIL level.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments
**kwargs Any Additional keyword arguments

brief

brief(msg: str, *args: Any, **kwargs: Any) -> None

Log a message at BRIEF level.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments
**kwargs Any Additional keyword arguments

test

test(msg: str, *args: Any, **kwargs: Any) -> None

Log a message at TEST level.

Parameters:

Parameter Type Description
msg str Message to log
*args Any Format arguments
**kwargs Any Additional keyword arguments

errorIfNotDebug

errorIfNotDebug(msg: str, *args: Any, **kwargs: Any) -> None

Log an error with full traceback only if debug/trace is enabled.

If debug mode is enabled, shows full exception traceback. Otherwise, shows only the error message.

Parameters:

Parameter Type Description
msg str Error message
*args Any Format arguments
**kwargs Any Additional keyword arguments

Example:

try:
    risky_operation()
except Exception:
    logger.errorIfNotDebug("Operation failed")

criticalIfNotDebug

criticalIfNotDebug(msg: str, *args: Any, **kwargs: Any) -> None

Log a critical error with full traceback only if debug/trace is enabled.

Similar to errorIfNotDebug() but logs at CRITICAL level.

Parameters:

Parameter Type Description
msg str Error message
*args Any Format arguments
**kwargs Any Additional keyword arguments

colorize

colorize(text: str, color: str, *, enable_color: bool | None = None) -> str

Apply ANSI color codes to text.

Parameters:

Parameter Type Description
text str Text to colorize
color str ANSI color code (e.g., ANSIColors.CYAN, ANSIColors.RED)
enable_color bool | None Override color enablement. If None, uses instance setting.

Returns:

logDynamic

logDynamic(level: str | int, msg: str, *args: Any, **kwargs: Any) -> None

Log a message at a dynamically specified level.

Parameters:

Parameter Type Description
level str | int Log level name or numeric value
msg str Message to log
*args Any Format arguments
**kwargs Any Additional keyword arguments

Example:

logger.logDynamic("warning", "This is a warning")
logger.logDynamic(logging.ERROR, "This is an error")

useLevel

useLevel(level: str | int, *, minimum: bool = False) -> ContextManager

Context manager to temporarily change log level.

Parameters:

Parameter Type Description
level str | int Log level to use
minimum bool If True, only set the level if it’s more verbose (lower numeric value) than the current level. Prevents downgrading from a more verbose level.

Returns:

Example:

with logger.useLevel("debug"):
    logger.debug("This will be shown")

# Level is restored after the context

useLevelMinimum

useLevelMinimum(level: str | int) -> ContextManager

Context manager to temporarily change log level, only if it’s more verbose than the current level.

This convenience method is equivalent to calling useLevel(level, minimum=True). It prevents downgrading from a more verbose level (e.g., TRACE) to a less verbose one (e.g., DEBUG).

Parameters:

Parameter Type Description
level str | int Log level to use. Only applied if it’s more verbose than the current effective level.

Returns:

Example:

logger.setLevel("TRACE")
# This won't downgrade from TRACE to DEBUG
with logger.useLevelMinimum("DEBUG"):
    assert logger.levelName == "TRACE"  # Still TRACE

# This will upgrade from INFO to DEBUG
logger.setLevel("INFO")
with logger.useLevelMinimum("DEBUG"):
    assert logger.levelName == "DEBUG"  # Upgraded to DEBUG

usePropagate

usePropagate(propagate: bool, *, manage_handlers: bool | None = None) -> ContextManager

Context manager to temporarily change propagate setting.

Parameters:

Parameter Type Description
propagate bool If True, messages propagate to parent loggers. If False, messages only go to this logger’s handlers.
manage_handlers bool | None If True, automatically manage apathetic handlers based on propagate setting. If None, uses DEFAULT_MANAGE_HANDLERS from constants. If False, only sets propagate without managing handlers. In compat_mode, this may default to False.

Returns:

Example:

# Temporarily disable propagation to capture logs locally
with logger.usePropagate(False):
    logger.info("This message only goes to logger's handlers")
    # Do something that should not propagate to root

# Propagate setting is restored after the context

setLevelAndPropagate

setLevelAndPropagate(
    level: int | str,
    *,
    minimum: bool | None = False,
    allow_inherit: bool = False,
    manage_handlers: bool | None = None
) -> None

Set the logging level and propagate setting together in a smart way.

This convenience method combines setLevel() and setPropagate() with intelligent defaults:

This matches common use cases: when inheriting level, you typically want to propagate to parent handlers. When setting an explicit level, you typically want isolated logging with your own handler.

Parameters:

Parameter Type Description
level int | str Log level name or numeric value. Use INHERIT_LEVEL (0) or “NOTSET” to inherit.
minimum bool | None If True, only set the level if it’s more verbose (lower numeric value) than the current level. Defaults to False.
allow_inherit bool If True, allows setting level to 0 (INHERIT_LEVEL, i.e. NOTSET) in improved mode. Defaults to False.
manage_handlers bool | None If True, automatically manage apathetic handlers based on propagate setting. If None, uses DEFAULT_MANAGE_HANDLERS from constants. If False, only sets propagate without managing handlers.

Example:

logger = getLogger("mymodule")

# Set to inherit level and propagate to root
from apathetic_logging import INHERIT_LEVEL
logger.setLevelAndPropagate(INHERIT_LEVEL, allow_inherit=True)

# Set explicit level and disable propagation (isolated logging)
logger.setLevelAndPropagate("debug")

useLevelAndPropagate

useLevelAndPropagate(
    level: str | int,
    *,
    minimum: bool = False,
    manage_handlers: bool | None = None
) -> ContextManager

Context manager to temporarily set level and propagate together.

This convenience context manager combines useLevel() and usePropagate() with intelligent defaults:

Both settings are restored when the context exits.

Parameters:

Parameter Type Description
level str | int Log level to use (string name or numeric value). Use INHERIT_LEVEL (0) or “NOTSET” to inherit.
minimum bool If True, only set the level if it’s more verbose (lower numeric value) than the current effective level. Prevents downgrading from a more verbose level. Defaults to False.
manage_handlers bool | None If True, automatically manage apathetic handlers based on propagate setting. If None, uses DEFAULT_MANAGE_HANDLERS from constants. If False, only sets propagate without managing handlers.

Returns:

Example:

logger = getLogger("mymodule")

# Temporarily inherit level and propagate
from apathetic_logging import INHERIT_LEVEL
with logger.useLevelAndPropagate(INHERIT_LEVEL):
    logger.info("This propagates to root")

# Temporarily set explicit level with isolated logging
with logger.useLevelAndPropagate("debug"):
    logger.debug("This only goes to logger's handlers")

# Both level and propagate are restored after the context

levelName

levelName: str

Return the explicit level name set on this logger (read-only property).

This property returns the name of the level explicitly set on this logger. For the effective level name (what’s actually used, considering inheritance), use effectiveLevelName instead.

Example:

logger.setLevel("debug")
print(logger.levelName)  # "DEBUG"

effectiveLevel

effectiveLevel: int

Return the effective level (what’s actually used) (read-only property).

This property returns the effective logging level for this logger, considering inheritance from parent loggers. This is the preferred way to get the effective level. Also available via getEffectiveLevel() for stdlib compatibility.

Example:

parent = getLogger("parent")
parent.setLevel("info")

child = getLogger("parent.child")
print(child.level)  # 0 (INHERIT_LEVEL, i.e. NOTSET - explicit)
print(child.effectiveLevel)  # 20 (INFO - effective, from parent)

effectiveLevelName

effectiveLevelName: str

Return the effective level name (what’s actually used) (read-only property).

This property returns the name of the effective logging level for this logger, considering inheritance from parent loggers. This is the preferred way to get the effective level name. Also available via getEffectiveLevelName() for consistency.

Example:

parent = getLogger("parent")
parent.setLevel("info")

child = getLogger("parent.child")
print(child.levelName)  # "NOTSET" (explicit - INHERIT_LEVEL, i.e. NOTSET)
print(child.effectiveLevelName)  # "INFO" (effective, from parent)

getLevel

getLevel() -> int

Return the explicit level set on this logger.

This method returns the level explicitly set on this logger (same as level property). For the effective level, use getEffectiveLevel() or the effectiveLevel property.

Returns:

Example:

logger.setLevel("debug")
print(logger.getLevel())  # 10

getLevelName

getLevelName() -> str

Return the explicit level name set on this logger.

This method returns the name of the level explicitly set on this logger (same as levelName property). For the effective level name, use getEffectiveLevelName() or the effectiveLevelName property.

Returns:

Example:

logger.setLevel("debug")
print(logger.getLevelName())  # "DEBUG"

getEffectiveLevelName

getEffectiveLevelName() -> str

Return the effective level name (what’s actually used).

This method returns the name of the effective logging level for this logger, considering inheritance from parent loggers. Prefer the effectiveLevelName property for convenience, or use this method for consistency with getEffectiveLevel().

Returns:

Example:

parent = getLogger("parent")
parent.setLevel("info")

child = getLogger("parent.child")
print(child.getEffectiveLevelName())  # "INFO" (from parent)

getChildren

getChildren() -> set[logging.Logger]

Requires Python 3.12+

Return a set of loggers that are immediate children of this logger.

This method returns only the direct children of the logger (loggers whose names are one level deeper in the hierarchy). For example, if you have loggers named foo, foo.bar, and foo.bar.baz, calling getLogger("foo").getChildren() will return a set containing only the foo.bar logger, not foo.bar.baz.

Returns:

Example:

from apathetic_logging import getLogger

root = getLogger("")
foo = getLogger("foo")
bar = getLogger("foo.bar")
baz = getLogger("foo.bar.baz")

# Get immediate children of root logger
children = root.getChildren()  # {foo logger}

# Get immediate children of foo logger
children = foo.getChildren()  # {bar logger}

# Get immediate children of bar logger
children = bar.getChildren()  # {baz logger}

For detailed documentation, see the Python logging.Logger.getChildren() documentation.

ensureHandlers

ensureHandlers() -> None

Ensure handlers are attached to this logger.

DualStreamHandler is what will ensure logs go to the write channel. Rebuilds handlers if they’re missing or if stdout/stderr have changed.

validateLevel

validateLevel(level: int, *, level_name: str | None = None, allow_inherit: bool = False) -> None

Validate that a level value is positive (> 0). (staticmethod)

Custom levels with values <= 0 will inherit from the root logger, causing INHERIT_LEVEL (i.e. NOTSET) inheritance issues. In compatibility mode, validation is skipped.

Parameters:

Parameter Type Description
level int The numeric level value to validate
level_name str | None Optional name for the level (for error messages). If None, will attempt to get from getLevelName()
allow_inherit bool If True, allows level 0 (INHERIT_LEVEL, i.e. NOTSET). Defaults to False.

Raises:

Example:

Logger.validateLevel(5, level_name="TRACE")
Logger.validateLevel(0, level_name="TEST")
# ValueError: setLevel(0) sets the logger to INHERIT_LEVEL (i.e. NOTSET)...

Logger.validateLevel(0, level_name="TEST", allow_inherit=True)
# Passes validation

_log

_log(level: int, msg: str, args: tuple[Any, ...], **kwargs: Any) -> None

Log a message with the specified level.

Changed from stdlib: Automatically ensures handlers are attached via ensureHandlers().

Parameters:

Parameter Type Description
level int The numeric logging level
msg str The message format string
args tuple[Any, 
] Arguments for the message format string
**kwargs Any Additional keyword arguments passed to the base implementation

addLevelName

addLevelName(level: int, level_name: str) -> None

Associate a level name with a numeric level. (staticmethod)

Changed from stdlib: Validates that level value is positive (> 0) to prevent INHERIT_LEVEL (i.e. NOTSET) inheritance issues. Sets logging.<LEVEL_NAME> attribute for convenience.

Parameters:

Parameter Type Description
level int The numeric level value (must be > 0 for custom levels)
level_name str The name to associate with this level

Raises:

Classes

TagFormatter

Custom formatter that adds colored tags to log messages.

Constructor

TagFormatter(format: str)

Parameters:

Parameter Type Description
format str Format string (typically "%(message)s")

The formatter automatically adds tags based on log level:

DualStreamHandler

Stream handler that routes messages to stdout or stderr based on log level.

Constructor

DualStreamHandler()

Properties

enable_color: bool

Whether to enable colorized output for this handler.

Constants

Log Levels

ANSI Colors

Access via ANSIColors class:

Example:

from apathetic_logging import ANSIColors

message = f"{ANSIColors.CYAN}Colored text{ANSIColors.RESET}"

Tag Styles

Defaults

Testing Utilities

SAFE_TRACE(label: str, *args: Any, icon: str = â€œđŸ§”â€) -> None

Debug tracing function for test development. Only active when safe trace is enabled via environment variables.

Safe trace is enabled when any of the following conditions are met:

  1. SAFE_TRACE environment variable is set to "1", "true", or "yes" (case insensitive)
  2. LOG_LEVEL environment variable (case insensitive) is set to "TRACE" or "TEST"
  3. LOG_LEVEL numeric value is less than or equal to TRACE_LEVEL (supports both numeric strings like "5" and standard logging level names like "DEBUG")

Parameters:

Parameter Type Description
label str Trace label
*args Any Additional arguments to trace
icon str Icon to use (default: "đŸ§”")

make_test_trace(icon: str = â€œđŸ§”â€) -> Callable

Create a test trace function with a custom icon.

Parameters:

Parameter Type Description
icon str Icon to use

Returns:

SAFE_TRACE_ENABLED: bool

Boolean flag indicating if safe trace is enabled. This is determined by checking environment variables at module import time.

Safe trace is enabled when any of the following conditions are met:

  1. SAFE_TRACE environment variable is set to "1", "true", or "yes" (case insensitive)
  2. LOG_LEVEL environment variable (case insensitive) is set to "TRACE" or "TEST"
  3. LOG_LEVEL numeric value is less than or equal to TRACE_LEVEL (supports both numeric strings like "5" and standard logging level names like "DEBUG")

Note: This value is computed once at module import time. To change it at runtime, you can directly assign to this attribute (useful for testing).

Example:

from apathetic_logging import SAFE_TRACE_ENABLED, safeTrace
import os

# Check if safe trace is enabled
if SAFE_TRACE_ENABLED:
    safeTrace("debug", "message")  # Will output

# Enable via environment variable before import
os.environ["SAFE_TRACE"] = "1"
# Note: You need to re-import for the change to take effect

# Or override directly (useful in tests)
import apathetic_logging
apathetic_logging.SAFE_TRACE_ENABLED = True
safeTrace("debug", "message")  # Will output

Compatibility Mode

This library provides two modes of operation:

Default Mode (Improved Behavior)

When compatibility mode is disabled (compat_mode=False, the default), the library provides improved behavior with enhancements over the standard library:

Compatibility Mode (Standard Library Compatible)

When compatibility mode is enabled (compat_mode=True), the library maintains standard library compatible behavior with no breaking changes:

Enabling Compatibility Mode

To enable compatibility mode for stdlib-compatible behavior:

from apathetic_logging import registerCompatibilityMode

registerCompatibilityMode(compat_mode=True)
# Now getLogger(None) returns root logger (stdlib behavior)

You can also set it when registering a logger:

from apathetic_logging import registerLogger

registerLogger("my_app", compat_mode=True)

Behavior Differences

getLogger(None) behavior:

To get root logger in default mode:

logger = getLogger("")  # Returns root logger (works in both modes)

See registerCompatibilityMode() for more details.