Skip to content

Logger

The Logger class is the primary interface for creating log records. In most cases you use the pre-configured module-level instance.


Getting a Logger

from plainlog import logger

log = logger.new()
log.info("hello world")

The module-level logger is a Logger bound to the global Core singleton with name "root". It is automatically configured on import — by default it uses the "default" profile (writes to stdout).

Prefer creating one child logger per module with logger.new() (it auto-detects the module name); the root logger is mainly the shared entry point and a factory for child loggers.


Basic Logging

from plainlog import logger

log = logger.new()  # auto-named after the current module

log.debug("debug message")
log.info("info message")
log.warning("warning message")
log.error("error message")
log.critical("critical message")

Each level method accepts a message and optional keyword arguments that are merged into the log record as top-level fields (for example record["user"]).

Log with a specific level

from plainlog import logger

log = logger.new()
log.log("INFO", "explicit level")
log.log(20, "level as int")

Callable form

A Logger is also callable. log(level, msg, **kwargs) behaves like log.log() but returns a bool: True if the record was accepted by the Core's processor pipeline, False if it was dropped (no processors configured, or below the minimum level).

from plainlog import logger

log = logger.new()
accepted = log("INFO", "positional form")
also_accepted = log(level="WARNING", msg="keyword form")

Log with exception info

from plainlog import logger

log = logger.new()

try:
    1 / 0
except ZeroDivisionError:
    log.exception("something went wrong")

Configuration

Using a profile

from plainlog import configure_log, logger

configure_log(profile="develop", level="DEBUG")
log = logger.new()
log.info("now with colors and caller info")

Direct processor setup

from plainlog import configure_log
from plainlog.processors import SimpleFormatter, Stream

configure_log(processors=[SimpleFormatter(), Stream()])

See Processors, Formatters & Handlers for everything available.


Binding Variables

Use bind() to create a child logger with additional static key-value pairs attached to every record.

from plainlog import logger

log = logger.new().bind(user="alice", request_id="abc-123")
log.info("user action")

Use unbind() to remove keys.

from plainlog import logger

log = logger.new().bind(user="alice", request_id="abc-123")
log2 = log.unbind("request_id")
log2.info("without request_id")

Context Variables

Use contextualize() to attach variables for the duration of a block.

from plainlog import logger

log = logger.new()
with log.contextualize(request_id="xyz"):
    log.info("inside context")

The context is thread-safe (backed by ContextVar) and works correctly in async code.

For manual control, context() sets the variables and returns a token that reset_context() restores:

from plainlog import logger

log = logger.new()
token = log.context(request_id="xyz")
try:
    log.info("inside context")
finally:
    log.reset_context(token)
log.info("after context")

Child Loggers

Use new() to create a child logger. The name is auto-detected from the caller's module and function.

from plainlog import logger

log = logger.new()

To explicitly derive the logger name from the current module, pass __name__:

from plainlog import logger

log = logger.new(__name__)
log.info("logged with the module name as logger name")

Flushing

Records are processed on a background thread. flush() blocks until every record queued so far has been processed — useful before reading output.

from plainlog import logger

logger.info("processing")
logger.flush()

API Reference

Logger

Logger that sends structured log records to a shared Core.

Each Logger is tied to a single Core instance, a name, and a set of static key-value pairs (passed as keyword arguments) that are attached to every record as top-level fields.

Use bind() / unbind() to derive a new logger with additional or fewer fields. Use new() to create a child logger (optionally with an auto-detected name).

Parameters:

Name Type Description Default
core Core

The shared Core this logger writes to.

required
name str

Logger name (e.g. "root", "mymodule.MyClass").

required
**kwargs Any

Static key-value pairs attached to every record.

{}

Attributes:

Name Type Description
name str

Logger name (e.g. "root", "mymodule.MyClass").

__call__(level=LEVEL_DEBUG, msg='', **kwargs)

Callable interface: logger(level, msg, **kwargs).

Parameters:

Name Type Description Default
level str | int

Log level as int or str. Defaults to DEBUG.

LEVEL_DEBUG
msg Msg

The message to log. Defaults to "".

''
**kwargs Any

Additional record fields.

{}

Returns:

Type Description
bool

True if the record was enqueued, False if it was

bool

dropped (no processors configured or below the minimum level).

bind(**kwargs)

Return a new logger with additional fields.

Parameters:

Name Type Description Default
**kwargs Any

Key-value pairs to merge into the logger's fields.

{}

Returns:

Type Description
'Logger'

A new Logger with the combined fields.

context(**kwargs) staticmethod

Set context variables for the current execution context.

Parameters:

Name Type Description Default
**kwargs Any

Key-value pairs to merge into the context.

{}

Returns:

Type Description
Token[Dict[str, Any]]

A Token that can be passed to reset_context().

contextualize(**kwargs) staticmethod

Context manager that sets kwargs as context variables.

Parameters:

Name Type Description Default
**kwargs Any

Key-value pairs to set as context variables.

{}

Yields:

Type Description
Generator[Token[Dict[str, Any]]]

The token returned by context().

Example

with logger.contextualize(request_id="abc"): logger.info("handling request")

critical(msg, **kwargs)

Log msg at CRITICAL level.

debug(msg, **kwargs)

Log msg at DEBUG level.

error(msg, **kwargs)

Log msg at ERROR level.

exception(msg, **kwargs)

Log msg at ERROR level and attach exception info.

If exc_info is not already set it defaults to True.

Parameters:

Name Type Description Default
msg Msg

The message to log.

required
**kwargs Any

Additional record fields.

{}

flush(timeout=None)

Block until every queued record has been processed.

Records are handled on the background Core thread. Call this before reading side effects such as captured records or written files.

Parameters:

Name Type Description Default
timeout float | None

Maximum seconds to wait. None uses the default.

None

info(msg, **kwargs)

Log msg at INFO level.

log(level, msg, **kwargs)

Log msg at the given level.

Parameters:

Name Type Description Default
level str | int

Log level as int or str.

required
msg Msg

The message to log.

required
**kwargs Any

Additional record fields.

{}

new(name=None, **kwargs)

Create a child logger, optionally auto-detecting the caller name.

Parameters:

Name Type Description Default
name Optional[str]

Explicit logger name. When None the name is auto-detected from the caller's frame (module + qualname).

None

Returns:

Type Description
Logger

A new Logger instance.

reset_context(token) staticmethod

Reset the ContextVar to its previous value.

Parameters:

Name Type Description Default
token Token[Dict[str, Any]]

The token returned by context().

required

unbind(*args)

Return a new logger with the given fields removed.

Parameters:

Name Type Description Default
*args Any

Field keys to remove.

()

Returns:

Type Description
'Logger'

A new Logger without the specified fields.

warning(msg, **kwargs)

Log msg at WARNING level.

Module-Level Instance

Module-level Logger convenience instance.

Usage::

from plainlog import logger

logger.info("hello world")

configure_log

Configure the global Core and optionally apply a named profile.

Keyword-only entry point for configuring processors and level; replaces the removed per-logger configure() method.

Parameters:

Name Type Description Default
profile Optional[str]

Optional profile name. When set, the profile function is called with kwargs and the processors it returns are applied. A profile returning None leaves the pipeline unchanged.

None
processors Optional[Iterable[UniversalProcessorProtocol]]

Explicit processors to install. When both processors and a profile are given, the profile's processors are appended.

None
level Optional[Union[str, int]]

Minimum log level.

None
close_before_configure bool

Close the currently registered processors before installing the new pipeline. Ignored when nothing is being installed (for example a profile that returns None).

False
**kwargs Any

Extra arguments forwarded to the profile function.

{}

Raises:

Type Description
ValueError

If profile is not a valid profile name.