Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

certlib.log

...is a library extending the standard logging toolset. It allows you to introduce structured logging with minimal fuss, and/or leverage some other logging goodies (e.g., {}-style message formatting).

Basic Info

You can install the certlib.log library by running (typically, in a virtual environment) the command:

python3 -m pip install certlib.log

The library is compatible with Python 3.10 and all newer versions of Python. It uses only the Python standard library, i.e., it does not depend on any third-party packages.

Principles and Benefits

The primary reason for creating certlib.log was to make it easier to configure structured logging across various systems created and used by CERT Polska -- in a possibly consistent way and without requiring extensive adjustments.

An important design decision was to build the library on top of the standard logging mechanisms (rather than introducing alternative machinery).

This approach makes it possible to:

  • just start using the library in existing projects -- especially, to enable structured logging (often without changing a single line of code);

  • depending on the needs, gradually introduce other features provided by the library (such as {}-style message formatting, data-only message-less log records, auto-making of log record fields, e.g., from context variables...);

  • retain existing logging configuration methods (whether using an *.ini file or loading a configuration dict).

Examples

Minimal Structured Logging Setup

import logging, certlib.log

some_handler = logging.StreamHandler()
some_handler.setFormatter(
    certlib.log.StructuredLogsFormatter()
)
logging.getLogger().addHandler(some_handler)

More Elaborate Variant, with Auto-Makers and Defaults

import logging.config

logging.config.dictConfig({
    "formatters": {
        "structured": {
            "()": "certlib.log.StructuredLogsFormatter",
            "defaults": {
                # * Each key in this dict should be an *output data* key.
                # * Each value should specify the respective *default value*.
                "system": "MyExample",
                "component": "MyAPI",
                "component_type": "web"
            },
            "auto_makers": {
                # * Each key in this dict should be an *output data* key.
                # * Each value should specify an *argumentless callable*
                #   (for example, the `get()` method of some `ContextVar`).
                "client_ip": "myexample.myapi.client_ip_context_var.get",
                "nano_time": "time.time_ns"
            }
        }
    },
    "handlers": {
        "stderr": {
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stderr",
            "formatter": "structured"
        }
    },
    "root": {
        "level": "INFO",
        "handlers": ["stderr"]
    },
    "disable_existing_loggers": False,
    "version": 1
})

Example log entry:

{
    "client_ip": "10.20.30.40",
    "component": "MyAPI",
    "component_type": "web",
    "func": "handle_request",
    "level": "INFO",
    "levelno": 20,
    "lineno": 1670,
    "logger": "my_api.some_module",
    "message": "Request from '10.20.30.40'",
    "message_base": "Request from %r",
    "nano_time": 1790597103141592064,
    "pid": 3297524,
    "process_name": "MainProcess",
    "src": "/opt/MyExample/my_api/some_module.py",
    "system": "MyExample",
    "thread_id": 274462836183740,
    "thread_name": "MainThread",
    "timestamp": "2026-09-28 12:05:03.141592Z"
}

Logging {}-Formatted Text Messages

import datetime as dt
import ipaddress
import logging
import types
from certlib.log import xm   # Note: `xm` is short for `ExtendedMessage`

logger = logging.getLogger(__name__)

...

def example_with_text_message_formatting(city, humidity, error_summary=None):
    if error_summary:
        logger.error(xm(
            'An error occurred: {!r}', error_summary,
            exc_info=True, stack_info=True, stacklevel=2,
        ))

    logger.warning(xm('Humidity in {} is {:.1%}', city, humidity))

    logger.info(xm(
        # Here: using `{}`-formatting-specific item/attribute
        # lookups as well as `datetime`-specific format codes.
        '{what[a].b[c]} is day #{today:%j} of year {today:%Y}',
        what={'a': types.SimpleNamespace(
            b={'c': 'Today'},
        )},
        today=dt.date.today(),
        # (=> text message like: 'Today is day #052 of year 2026')

        # Arbitrary data items can also be given (which is especially
        # useful when `certlib.log.StructuredLogsFormatter` is in use).
        some_extra_item=42,
        other_arbitrary_stuff={'foo': [
            {'my-ip': ipaddress.IPv4Address('192.168.0.1')},
            dt.time(12, 59),
        ]},
    ))

Logging Pure Data (without Text Messages)

The possibility to focus on pure data -- without the need to pass any text-message-related arguments -- is especially handy when certlib.log.StructuredLogsFormatter is in use (but not only then).

import logging
from certlib.log import xm

logger = logging.getLogger(__name__)

...

def example_with_no_text(temperature, pressure, debug_data_dict, calm=True):
    if calm:
        logger.info(xm(
            # Just data:
            temperature=temperature,
            pressure=pressure,
        ))
    else:
        logger.error(xm(
            # Just data:
            temperature=temperature,
            pressure=pressure,

            # Special arguments:
            exc_info=True,
            stack_info=True,
            stacklevel=2,
        ))

    # Providing data by passing a single dict is also OK:
    logger.debug(xm(debug_data_dict))

You can find more examples in the User's Guide.

Copyright (c) 2026, CERT Polska. All rights reserved.

The certlib.log library is free software; you can redistribute and/or modify it under the terms of the BSD 3-Clause "New" or "Revised" License (see the LICENSE.txt file in the source code repository).

Metadata

Release files for certlib-log 1.0.0b8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for certlib-log 1.0.0b8
File Size Uploaded Provenance Actions
certlib_log-1.0.0b8.tar.gz 143.2 kB Without provenance Details for certlib_log-1.0.0b8.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for certlib-log 1.0.0b8
File Interpreter ABI Platform Actions
certlib_log-1.0.0b8-py3-none-any.whl Python 3 none any Details for certlib_log-1.0.0b8-py3-none-any.whl

Total release size: 208.4 kB

Release files / certlib_log-1.0.0b8.tar.gz

Download URL certlib_log-1.0.0b8.tar.gz
Size 143.2 kB
Tags Source
SHA-256 checksum
How to use checksums
fb08f1aa741303039535657b7b98428a0f8438ac3c28849a4dd23df0f28470a9
BLAKE2b-256 checksum
How to use checksums
3f5a03822cdd0b4d9809164d937726f0dbc25fda494591aa46c12d0a15e8eea9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.13

Release files / certlib_log-1.0.0b8-py3-none-any.whl

Download URL certlib_log-1.0.0b8-py3-none-any.whl
Size 65.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7f1530b9772c2a46451861c1dade5488f177ee585992a1553634691dce078386
BLAKE2b-256 checksum
How to use checksums
19f5403035b57f4e816f85404bde86c4aec919ed58d06ef60a0895489a121db8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.13

Security for certlib-log 1.0.0b8

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page