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

* Improvements over Stdlib logging
\† Ported or fallthrough to Stdlib logging

Setup

Function Summary
basicConfig()† Configure logging system
getLevelName()* / getLevelNameStr() Convert a log level to its string name (always returns string)
getLevelNumber() Convert a log level name to its numeric value
getLogger()* Return the registered logger instance (auto-infers name when None)
getLoggerOfType() Get a logger of the specified type, creating it if necessary
getRootLogger() Return the root logger instance (primary way to access root logger)
registerLogger() Register a logger for use by getLogger()
setLoggerClass()† Set the class to be used when instantiating a logger
shutdown()† Perform an orderly shutdown of the logging system

Root Logger Convenience API

For applications using the root logger as the single source of truth for log levels:

Function Summary
getRootLevel() Get current root logger level
getRootLevelName() Get current root logger level name
getEffectiveRootLevel() Get effective root logger level
getEffectiveRootLevelName() Get effective root logger level name
setRootLevel() Set root logger level (and optionally children)
setRootLevelMinimum() Set root level only if more verbose
useRootLevel() Context manager to temporarily set root level
useRootLevelMinimum() Context manager to temporarily set root level (minimum)
isRootEnabledFor() Check if root would log at level
logRootDynamic() Log with dynamically provided level

Logging Messages (to Root Logger)

Function Summary
brief() Log a message with severity BRIEF
critical()† / fatal()† Log a message with severity CRITICAL
debug()† Log a message with severity DEBUG
detail() Log a message with severity DETAIL
error()† / exception()† Log a message with severity ERROR
info()† Log a message with severity INFO
log()† Log a message with an explicit level
test() Log a message with severity TEST
trace()† Log a message with severity TRACE
warning()† / warn()† Log a message with severity WARNING

Logging Extras

| captureWarnings()† | Capture warnings issued by the warnings module | | currentframe()† | Return the frame object for the caller’s stack frame | | disable()† | Disable all logging calls of severity ‘level’ and below |

Safety Logging

For when the framework isn’t initialzied yet, or you’re troubleshooting.

Function Summary
makeSafeTrace() Create a test trace function with a custom icon
safeLog() Emergency logger that never fails
safeTrace() Debug tracing function for test development

Settings Registry

The library provides a registry system for configuring default settings that apply when creating loggers or performing operations. These settings allow you to bypass needing to pass parameters to every function call.

Function Purpose Default Value
registerCompatibilityMode() / getCompatibilityMode() Enable stdlib-compatible behavior False (improved behavior)
registerDefaultLogLevel() / getDefaultLogLevel() Default log level when no other source found "INFO"
registerLogLevelEnvVars() / getLogLevelEnvVars() Environment variables to check for log level ["LOG_LEVEL"]
registerLogger() / Register a logger for use by getLogger()  
registerPortHandlers() Whether to port handlers when replacing loggers True
registerPortLevel() Whether to port level when replacing loggers True
registerPropagate() / getDefaultPropagate() Default propagate setting for loggers False
registerReplaceRootLogger() Whether to replace root logger if not correct type True
registerTargetPythonVersion() / getTargetPythonVersion() Target Python version for compatibility checking (3, 10)

Internal

For writing library extensions.

Function Summary
addLevelName()† Associate a level name with a numeric level
getDefaultLoggerName() Get default logger name with optional inference from caller’s frame
getHandlerByName()† 3.12+: Get a handler with the specified name
getHandlerNames()† 3.12+: Return all known handler names
getLevelNamesMapping()† 3.11+: Get mapping of level names to numeric values
getLoggerClass()† Return the class to be used when instantiating a logger
getLogRecordFactory()† Return the factory function used to create LogRecords
getRegisteredLoggerName() Get the registered logger name
getTargetPythonVersion() Get the target Python version
hasLogger() Check if a logger exists in the logging manager’s registry
isRootLoggerInstantiated() Check if the root logger has been instantiated
makeLogRecord()† Create a LogRecord from the given parameters
removeLogger() Remove a logger from the logging manager’s registry
setLogRecordFactory()† Set the factory function used to create LogRecords

Quick Reference: apathetic_logging.Logger Class Methods

* Improvements over Stdlib logging.Logger \† Ported or fallthrough to Stdlib logging.Logger. See the Python logging.Logger documentation for the complete list.

Setup

Method Summary
determineColorEnabled() Return True if colored output should be enabled (classmethod)
determineLogLevel() Resolve log level from CLI → env → root config → default
effectiveLevel Return the effective level (what’s actually used) (property)
effectiveLevelName Return the effective level name (what’s actually used) (property)
extendLoggingModule() Extend Python’s logging module with TRACE and SILENT levels (classmethod)
getEffectiveLevelName() Return the effective level name (what’s actually used)
getLevel() Return the explicit level set on this logger
getLevelName() Return the explicit level name set on this logger
levelName Return the explicit level name set on this logger (property)
setLevel()* Set the logging level (accepts string names and has minimum parameter)
setLevelInherit() Set logger to inherit level from parent
setLevelMinimum() Set level only if it’s more verbose than current level
useLevel() Context manager to temporarily change log level
useLevelMinimum() Context manager to temporarily change log level (only if more verbose)

Advanced Setup

Logging Levels

Method Summary
brief() Log a message at BRIEF level
criticalIfNotDebug() Log a critical error with full traceback only if debug/trace is enabled
detail() Log a message at DETAIL level
errorIfNotDebug() Log an error with full traceback only if debug/trace is enabled
logDynamic() Log a message at a dynamically specified level
test() Log a message at TEST level
trace() Log a message at TRACE level

Logging Extras

| colorize() | Apply ANSI color codes to text |

Internal

Method Summary
_log()* Log a message with the specified level (automatically ensures handlers)
addLevelName()* Associate a level name with a numeric level (validates level > 0) (staticmethod)
ensureHandlers() Ensure handlers are attached to this logger
validateLevel() Validate that a level value is positive (> 0) (staticmethod)

apathetic_logging Function Reference

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.

basicConfig

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

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

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

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)

captureWarnings

captureWarnings(capture: bool, *args: Any, **kwargs: Any) -> None

Wrapper for logging.captureWarnings(). Redirect warnings to the logging package.

If capture is True, redirect all warnings issued by the warnings module to the logging package. If capture is False, ensure that warnings are not redirected to logging but to their original destinations.

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

Parameters:

Parameter Type Description
capture bool If True, redirect warnings to logging. If False, restore original warning behavior.
*args Any Additional positional arguments (passed through to stdlib)
**kwargs Any Additional keyword arguments (passed through to stdlib)

Example:

import apathetic_logging
import warnings

# Capture warnings and log them
apathetic_logging.captureWarnings(True)
warnings.warn("This warning will be logged")

# Stop capturing warnings
apathetic_logging.captureWarnings(False)

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.

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.

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.

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)

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.

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 on the root logger.

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

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

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 an info message
apathetic_logging.info("Application started")
apathetic_logging.info("Processing %d items", count)

log

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

Wrapper for logging.log(). Log a message with an explicit level on the root logger.

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

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

Parameters:

Parameter Type Description
level int The numeric logging level (e.g., logging.DEBUG, logging.INFO)
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
import logging

# Log with explicit level
apathetic_logging.log(logging.INFO, "Info message")
apathetic_logging.log(logging.DEBUG, "Debug message: %s", variable)
apathetic_logging.log(logging.WARNING, "Warning: %d items processed", count)

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)

getDefaultLogLevel

getDefaultLogLevel() -> str

Get the default log level.

Returns the registered default log level, or the default value if not registered.

Returns:

Example:

from apathetic_logging import getDefaultLogLevel

default_level = getDefaultLogLevel()
print(default_level)  # "INFO" (default)

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:

getDefaultPropagate

getDefaultPropagate() -> bool

Get the default propagate setting.

Returns the registered propagate setting, or the default value if not registered.

Returns:

Example:

from apathetic_logging import getDefaultPropagate

propagate = getDefaultPropagate()
print(propagate)  # False (default)

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.

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

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.

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:

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. These environment variables are checked in order by determineLogLevel() when resolving the log level.

Returns:

Example:

from apathetic_logging import getLogLevelEnvVars, registerLogLevelEnvVars

# Get default environment variables
env_vars = getLogLevelEnvVars()
print(env_vars)  # ["LOG_LEVEL"]

# Register custom environment variables
registerLogLevelEnvVars(["MYAPP_LOG_LEVEL", "LOG_LEVEL"])
env_vars = getLogLevelEnvVars()
print(env_vars)  # ["MYAPP_LOG_LEVEL", "LOG_LEVEL"]

See Also:

getLogRecordFactory

getLogRecordFactory() -> Callable

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

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

getLogger

getLogger(
    name: str | None = None,
    *args: Any,
    level: str | int | None = None,
    minimum: bool | None = None,
    extend: bool | None = None,
    **kwargs: Any
) -> Logger

Return a logger with the specified name, creating it if necessary.

Changed from stdlib: This function has several improvements over logging.getLogger():

For detailed documentation on the base functionality, see the Python logging.getLogger() documentation.

Parameters:

Parameter Type Description
name str | None The name of the logger to get. If None, the logger name will be auto-inferred from the calling module’s __package__. If an empty string (""), returns the root logger.
*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.
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.
extend bool | None If True (default), extend the logging module. If False, skip extension.
**kwargs Any Additional keyword arguments (for future-proofing)

Returns:

Example:

from apathetic_logging import getLogger

# Auto-infer logger name from calling module
logger = getLogger()  # Uses __package__ from caller

# Explicit logger name
logger = getLogger("mymodule")

# Get root logger
root = getLogger("")

# Create logger with level
logger = getLogger("mymodule", level="DEBUG")

# Create logger with minimum level (won't downgrade from TRACE)
logger = getLogger("mymodule", level="DEBUG", minimum=True)

See Also:

getLoggerOfType

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

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

This function is similar to getLogger(), but allows you to specify a custom logger class type. This is useful when you have a custom logger subclass and want to ensure you get an instance of that specific type.

Key Features:

Parameters:

Parameter Type Description
name str | None The name of the logger to get. If None, the logger name will be auto-inferred from the calling module’s __package__. If an empty string (""), returns the root logger.
class_type type[Logger] The logger class type to use (e.g., AppLogger, CustomLogger).
skip_frames int Number of frames to skip when inferring logger name. Prefer using as a keyword argument (e.g., skip_frames=2) for clarity. Defaults to 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.
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.
extend bool | None If True (default), extend the logging module. If False, skip extension.
replace_root bool | None Whether to replace the root logger if it’s not the correct type. If None (default), uses registry setting or constant default. Only used when extend=True.
**kwargs Any Additional keyword arguments (for future-proofing)

Returns:

Example:

from apathetic_logging import getLoggerOfType, Logger

# Get a standard apathetic logger
logger = getLoggerOfType("mymodule", Logger)

# Get a custom logger type (e.g., AppLogger)
from myapp.logs import AppLogger
app_logger = getLoggerOfType("myapp", AppLogger)

# Auto-infer name with custom type
app_logger = getLoggerOfType(None, AppLogger)

# Create logger with level
app_logger = getLoggerOfType("myapp", AppLogger, level="DEBUG")

See Also:

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.

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:

Note: This is a registry getter function. For other registry getters, see the subsections above.

getRootLogger

getRootLogger() -> Logger | logging.RootLogger

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("").

The root logger may be either:

Returns:

Example:

from apathetic_logging import getRootLogger

# Get the root logger
root = getRootLogger()

# Configure the root logger
root.setLevel("debug")
root.info("This logs to the root logger")

See Also:

getTargetPythonVersion

getTargetPythonVersion() -> tuple[int, int] | None

Get the target Python version.

Returns the registered target Python version, or the minimum supported version if none is registered. This version is used by checkPythonVersionRequirement() to determine if version-gated functions should be available.

Returns:

Example:

from apathetic_logging import getTargetPythonVersion, registerTargetPythonVersion

# Get default target version
version = getTargetPythonVersion()
print(version)  # (3, 10) or None if checks are disabled

# Register a custom target version
registerTargetPythonVersion((3, 11))
version = getTargetPythonVersion()
print(version)  # (3, 11)

See Also:

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:

isRootLoggerInstantiated

isRootLoggerInstantiated() -> bool

Check if the root logger has been instantiated/accessed yet.

The root logger is created lazily by Python’s logging module. This function checks if it has been instantiated without creating it as a side effect.

Returns:

Use Cases:

This is useful to distinguish between:

Note:

Calling logging.getLogger("") after this returns False will instantiate the root logger, so timing matters. This check should be done before any code that accesses the root logger.

Example:

if not isRootLoggerInstantiated():
    # Root logger is fresh, apply our defaults
    root.setLevel(determineLogLevel())
else:
    # Root logger already exists, port its state
    portLoggerState(old_root, new_root, port_level=True)

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

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:

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.

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)

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)

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.

registerCompatibilityMode

registerCompatibilityMode(*, compat_mode: bool | None) -> 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).

When compat_mode is True, restores stdlib-compatible behavior where possible (e.g., getLogger(None) returns root logger instead of auto-inferring).

Parameters:

Parameter Type Description
compat_mode bool | None Compatibility mode setting (True or False). If None, returns immediately without making any changes.

Example:

from apathetic_logging import registerCompatibilityMode, getLogger

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

# Disable compatibility mode (improved behavior, default)
registerCompatibilityMode(compat_mode=False)
# Now getLogger(None) auto-infers logger name

See Also:

registerDefaultLogLevel

registerDefaultLogLevel(default_level: str | None) -> None

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

This sets the default log level that will be used by determineLogLevel() when no other source (CLI args, environment variables, root logger level) provides a log level.

Parameters:

Parameter Type Description
default_level str | None Default log level name (e.g., "info", "warning"). If None, returns immediately without making any changes.

Example:

from apathetic_logging import registerDefaultLogLevel, getLogger

# Set default log level
registerDefaultLogLevel("warning")

# Now determineLogLevel() will use "warning" as fallback
logger = getLogger("mymodule")
level = logger.determineLogLevel()
print(level)  # "WARNING"

See Also:

registerLogLevelEnvVars

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

Register environment variable names to check for log level.

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

Parameters:

Parameter Type Description
env_vars list[str] | None List of environment variable names to check (e.g., ["MYAPP_LOG_LEVEL", "LOG_LEVEL"]). If None, returns immediately without making any changes.

Example:

from apathetic_logging import registerLogLevelEnvVars, getLogger
import os

# Register custom environment variables
registerLogLevelEnvVars(["MYAPP_LOG_LEVEL", "LOG_LEVEL"])

# Set environment variable
os.environ["MYAPP_LOG_LEVEL"] = "debug"

# Now determineLogLevel() will check MYAPP_LOG_LEVEL first, then LOG_LEVEL
logger = getLogger("mymodule")
level = logger.determineLogLevel()
print(level)  # "DEBUG" (from MYAPP_LOG_LEVEL)

See Also:

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,
    replace_root: bool | None = None
) -> None

Register a logger for use by getLogger().

This is the public API for registering a logger. It 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 provided but does not have extendLoggingModule(), logging.setLoggerClass() will be called directly to set the logger class. If logger_class is not provided, nothing is done with the logger class (the default Logger is already extended at import time).

Important: If you’re using a custom logger class that has extendLoggingModule(), do not call logging.setLoggerClass() directly. Instead, pass the class to registerLogger() and let extendLoggingModule() handle setting the logger class. This ensures consistent behavior and avoids class identity issues in stitched mode.

Parameters:

Parameter Type Description
logger_name str | None The name of the logger to retrieve (e.g., "myapp"). If None, extracts the top-level package from __package__.
logger_class type[Logger] | None Optional logger class to use. If provided and the class has an extendLoggingModule() method, it will be called. If the class doesn’t have that method, logging.setLoggerClass() will be called directly. If None, nothing is done (default Logger is already set up at import time).
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. 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, restores stdlib-compatible behavior where possible (e.g., getLogger(None) returns root logger). Defaults to None (no change).
replace_root bool | None Optional setting for whether to replace root logger. If provided, passes this value to extendLoggingModule() when extending the logging module. If None, uses registry setting or constant default. Only used when logger_class has an extendLoggingModule() method.

Example:

from apathetic_logging import registerLogger, getLogger

# Explicit registration with default Logger (already extended)
registerLogger("myapp")
logger = getLogger()  # Uses "myapp" as logger name

# Auto-infer from __package__
registerLogger()  # Uses top-level package from __package__

# Register with custom logger class (has extendLoggingModule)
from apathetic_logging import Logger

class AppLogger(Logger):
    pass

# Don't call AppLogger.extendLoggingModule() or
# logging.setLoggerClass() directly - registerLogger() handles it
registerLogger("myapp", AppLogger)

# Register with convenience parameters
registerLogger(
    "myapp",
    AppLogger,
    target_python_version=(3, 10),
    log_level_env_vars=["MYAPP_LOG_LEVEL", "LOG_LEVEL"],
    default_log_level="info",
    propagate=False,
    compat_mode=False
)

See Also:

registerPortHandlers

registerPortHandlers(*, port_handlers: bool | None) -> None

Register whether to port handlers when replacing a logger.

This sets whether logger replacement should port handlers from the old logger to the new logger. If not set, the library defaults to DEFAULT_PORT_HANDLERS from constants.py (True by default - port handlers to preserve existing configuration).

When port_handlers is True, handlers from the old logger are ported to the new logger. When False, the new logger manages its own handlers via manageHandlers().

Parameters:

Parameter Type Description
port_handlers bool | None Whether to port handlers (True or False). If None, the registration is skipped.

Example:

from apathetic_logging import registerPortHandlers

# Enable handler porting to preserve existing handlers
registerPortHandlers(port_handlers=True)
# Now logger replacement will port handlers

registerPortLevel

registerPortLevel(*, port_level: bool | None) -> None

Register whether to port level when replacing a logger.

This sets whether logger replacement should port the log level from the old logger to the new logger. If not set, the library defaults to DEFAULT_PORT_LEVEL from constants.py (True by default - port level to preserve existing configuration).

When port_level is True, the log level is ported from the old logger. When False, the new logger uses apathetic defaults (determineLogLevel() for root logger, INHERIT_LEVEL for leaf loggers).

Note: User-provided level parameters in getLogger()/getLoggerOfType() take precedence over ported level.

Parameters:

Parameter Type Description
port_level bool | None Whether to port level (True or False). If None, the registration is skipped.

Example:

from apathetic_logging import registerPortLevel

# Enable level porting to preserve existing level
registerPortLevel(port_level=True)
# Now logger replacement will port level

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 from constants.py (True by default - propagate to parent loggers).

When propagate is True, loggers propagate messages to parent loggers, allowing centralized control via root logger. When False, messages only go to the logger’s own handlers.

Parameters:

Parameter Type Description
propagate bool | None Propagate setting (True or False). If None, returns immediately without making any changes.

Example:

from apathetic_logging import registerPropagate, getLogger

# Enable propagation (default)
registerPropagate(propagate=True)
logger = getLogger("mymodule")
# Messages will propagate to root logger

# Disable propagation (isolated logging)
registerPropagate(propagate=False)
logger = getLogger("mymodule")
# Messages only go to logger's own handlers

See Also:

registerReplaceRootLogger

registerReplaceRootLogger(*, replace_root: bool | None) -> None

Register whether to replace root logger if it’s not the correct type.

This sets whether extendLoggingModule() should replace the root logger if it’s not an instance of the apathetic logger class. If not set, the library defaults to DEFAULT_REPLACE_ROOT_LOGGER from constants.py (True by default - replace root logger to ensure it’s an apathetic logger).

When replace_root is False, extendLoggingModule() will not replace the root logger, allowing applications to use their own custom logger class for the root logger.

Parameters:

Parameter Type Description
replace_root bool | None Whether to replace root logger (True or False). If None, the registration is skipped.

Example:

from apathetic_logging import registerReplaceRootLogger

# Disable root logger replacement to use your own logger class
registerReplaceRootLogger(replace_root=False)
# Now extendLoggingModule() won't replace the root logger

registerTargetPythonVersion

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

Register the target Python version for compatibility checking.

This sets the target Python version that will be used to validate function calls. If a function requires a Python version newer than the target version, it will raise a NotImplementedError even if the runtime version is sufficient.

If not set, the library defaults to TARGET_PYTHON_VERSION from constants.py (3, 10). This allows developers to catch version incompatibilities during development even when running on a newer Python version than their target.

Note: The runtime version is still checked as a safety net. If the runtime version is older than required, the function will still raise an error even if the target version is sufficient.

Parameters:

Parameter Type Description
version tuple[int, int] | None Target Python version as (major, minor) tuple (e.g., (3, 10) or (3, 11)). If None, returns immediately without making any changes.

Example:

from apathetic_logging import registerTargetPythonVersion, getLevelNamesMapping

# Register target Python version
registerTargetPythonVersion((3, 10))

# Now functions requiring 3.11+ will raise even if running on 3.12
try:
    mapping = getLevelNamesMapping()  # Requires Python 3.11+
except NotImplementedError as e:
    print(f"Function not available: {e}")
    # Error message suggests raising target version if needed

Why this matters:

See Also:

setRootLevel

setRootLevel(
    level: str | int,
    *,
    apply_to_children: bool = True,
    set_children_to_level: bool = True,
    root: logging.Logger | None = None
) -> None

Set the log level on the root logger and optionally on child loggers.

This is the recommended way to set log levels in a CLI application, as it ensures all loggers (including those from libraries) use the same level. When propagation is enabled (default), child loggers inherit from root, so setting root level affects all loggers.

Parameters:

Parameter Type Description
level str | int Log level to set (string name or numeric value). Supports standard levels (DEBUG, INFO, etc.) and custom levels (TRACE, DETAIL, BRIEF, SILENT).
apply_to_children bool If True (default), also sets level on any child loggers with explicit levels. Useful for loggers created before root level was set.
set_children_to_level bool If True (default), sets child loggers to same level as root. If False, sets child loggers to NOTSET to inherit from root.
root logging.Logger | None Logger to use as root. If None, uses actual root logger. Can pass any logger to work on its children.

Example:

from apathetic_logging import setRootLevel

# Set root level - all loggers inherit
setRootLevel("debug")

# Set root level and reset children to NOTSET to inherit
setRootLevel("info", set_children_to_level=False)

See Also:

getRootLevel

getRootLevel() -> int

Return the current explicit log level set on the root logger.

Returns the level explicitly set on the root logger, not the effective level (which would be the same for root since it has no parent).

Returns:

Example:

from apathetic_logging import setRootLevel, getRootLevel

setRootLevel("DEBUG")
print(getRootLevel())  # 10

See Also:

getRootLevelName

getRootLevelName() -> str

Return the name of the current explicit log level set on the root logger.

Returns the name of the level explicitly set on the root logger (e.g., “DEBUG”, “INFO”, “NOTSET”).

Returns:

Example:

from apathetic_logging import setRootLevel, getRootLevelName

setRootLevel("DEBUG")
print(getRootLevelName())  # "DEBUG"

See Also:

getEffectiveRootLevel

getEffectiveRootLevel() -> int

Return the effective log level on the root logger.

For the root logger, this is the same as the explicit level since the root logger has no parent to inherit from. This method exists for API completeness and symmetry with Logger.getEffectiveLevel().

Returns:

Example:

from apathetic_logging import getEffectiveRootLevel

print(getEffectiveRootLevel())  # Effective level value

See Also:

getEffectiveRootLevelName

getEffectiveRootLevelName() -> str

Return the name of the effective log level on the root logger.

For the root logger, this is the same as the explicit level name. This method exists for API completeness and symmetry with Logger.getEffectiveLevelName().

Returns:

Example:

from apathetic_logging import getEffectiveRootLevelName

print(getEffectiveRootLevelName())  # "INFO"

See Also:

setRootLevelMinimum

setRootLevelMinimum(level: str | int) -> None

Set root logger level only if more verbose than current level.

This is a convenience method that prevents downgrades from more verbose to less verbose levels. Useful for ensuring you don’t accidentally reduce logging verbosity.

Parameters:

Parameter Type Description
level str | int Log level to potentially set

Example:

from apathetic_logging import setRootLevelMinimum, setRootLevel

setRootLevel("INFO")
setRootLevelMinimum("DEBUG")  # Sets to DEBUG (more verbose)
setRootLevelMinimum("WARNING")  # Does nothing (already DEBUG)

See Also:

setRootLevelInherit

setRootLevelInherit() -> None

Set root logger to inherit level (NOTSET).

For the root logger, this sets level to NOTSET (0). Since the root logger has no parent, this effectively means the root will not filter by level - all messages are accepted.

Useful when you want child loggers to have explicit levels but the root logger should accept everything and let children decide what to log.

Example:

from apathetic_logging import setRootLevelInherit, getRootLevel

setRootLevelInherit()
print(getRootLevel())  # 0 (NOTSET/INHERIT_LEVEL)

See Also:

useRootLevel

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

Context manager to temporarily set root logger level.

Sets the root logger to a specific level for the duration of the with block, then restores the previous level.

Parameters:

Parameter Type Description
level str | int Temporary log level to use
minimum bool If True, only applies level if more verbose than current. Defaults to False.

Returns:

Example:

from apathetic_logging import setRootLevel, useRootLevel

setRootLevel("INFO")
with useRootLevel("DEBUG"):
    # Root is at DEBUG level here
    pass
# Back to INFO level

See Also:

useRootLevelMinimum

useRootLevelMinimum(level: str | int) -> ContextManager

Context manager to temporarily set root level only if more verbose.

Convenience context manager equivalent to useRootLevel(level, minimum=True). Only applies the level if it’s more verbose than the current effective level.

Parameters:

Parameter Type Description
level str | int Temporary log level to potentially use

Returns:

Example:

from apathetic_logging import setRootLevel, useRootLevelMinimum

setRootLevel("DEBUG")
with useRootLevelMinimum("TRACE"):
    # Root upgraded to TRACE (more verbose)
    pass

setRootLevel("TRACE")
with useRootLevelMinimum("DEBUG"):
    # Stays at TRACE (already more verbose)
    pass

See Also:

isRootEnabledFor

isRootEnabledFor(level: str | int) -> bool

Check if root logger would process messages at the given level.

Returns True if the root logger’s effective level would allow messages at the given level to be processed. Useful for conditional expensive operations that should only run if logging is enabled.

Parameters:

Parameter Type Description
level str | int Log level to check (string name or numeric value)

Returns:

Example:

from apathetic_logging import setRootLevel, isRootEnabledFor

setRootLevel("INFO")
if isRootEnabledFor("DEBUG"):
    # Expensive debug info generation
    pass

See Also:

logRootDynamic

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

Log a message to root logger with dynamically provided level.

Allows logging with a level that is determined at runtime rather than compile time. Useful when the logging level comes from user input, configuration, or other dynamic sources.

Parameters:

Parameter Type Description
level str | int Log level as string name or integer value
msg str The log message
*args Any Arguments for message formatting
**kwargs Any Additional keyword arguments (e.g., exc_info, stacklevel)

Example:

from apathetic_logging import logRootDynamic

level = "DEBUG"  # From config
logRootDynamic(level, "Message at %s level", level)
logRootDynamic(10, "Direct integer level")

See Also:

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.

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.

shutdown

shutdown() -> None

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

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

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.

_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:

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

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:

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

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

determineColorEnabled

determineColorEnabled() -> bool

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

Checks:

Returns:

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)

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)

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")

extendLoggingModule

extendLoggingModule(
    *,
    replace_root: bool | None = None,
    port_handlers: bool | None = None,
    port_level: bool | None = None
) -> bool

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

This method:

Parameters:

Parameter Type Description
replace_root bool | None Whether to replace the root logger if it’s not the correct type. If None (default), checks the registry setting (set via registerReplaceRootLogger()). If not set in registry, defaults to True for backward compatibility. When False, the root logger will not be replaced, allowing applications to use their own custom logger class for the root logger.
port_handlers bool | None Whether to port handlers from the old root logger to the new logger. If None (default), checks the registry setting (set via registerPortHandlers()). If not set in registry, defaults to True. When True, handlers from the old logger are ported. When False, the new logger manages its own handlers via manageHandlers().
port_level bool | None Whether to port level from the old root logger to the new logger. If None (default), checks the registry setting (set via registerPortLevel()). If not set in registry, defaults to True. When True, the old level is preserved. When False, the new root logger uses determineLogLevel() to get a sensible default.

Returns:

Note: When the root logger is replaced, its state is preserved by default (handlers and level are ported, propagate and disabled are always ported). Child loggers are automatically reconnected to the new root logger instance.

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.

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)

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"

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.

Note: This method is automatically called by _log() when logging. You typically don’t need to call it directly.

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"

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")

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

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")

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

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

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

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)

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

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 set to inherit level and propagate to root
with logger.useLevelAndPropagate(INHERIT_LEVEL, allow_inherit=True):
    logger.info("This will propagate to root")

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

# Both settings are 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

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

Note: This method is automatically called by setLevel() and addLevelName(). You typically don’t need to call it directly unless you’re implementing custom level validation logic.