Metadata-Version: 2.4
Name: certlib-log
Version: 1.0.0b4
Summary: A library extending the standard logging toolbox (e.g., facilitating structured logging with minimal fuss).
Maintainer-email: Jan Kaliszewski <jan.kaliszewski@cert.pl>
License-Expression: BSD-3-Clause
Project-URL: Homepage, https://github.com/CERT-Polska/certlib-log
Project-URL: Documentation, https://certlib-log.readthedocs.io/
Project-URL: Repository, https://github.com/CERT-Polska/certlib-log.git
Project-URL: Issues, https://github.com/CERT-Polska/certlib-log/issues
Project-URL: Changelog, https://github.com/CERT-Polska/certlib-log/blob/main/CHANGELOG.md
Keywords: logging,structured logging,formatting,library
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Logging
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Provides-Extra: dev
Requires-Dist: ast-serialize==0.5.0; extra == "dev"
Requires-Dist: babel==2.18.0; extra == "dev"
Requires-Dist: backrefs==7.0; extra == "dev"
Requires-Dist: black==26.5.1; extra == "dev"
Requires-Dist: build==1.5.0; extra == "dev"
Requires-Dist: certifi==2026.4.22; extra == "dev"
Requires-Dist: charset-normalizer==3.4.7; extra == "dev"
Requires-Dist: click==8.4.0; extra == "dev"
Requires-Dist: colorama==0.4.6; extra == "dev"
Requires-Dist: exceptiongroup==1.3.1; extra == "dev"
Requires-Dist: ghp-import==2.1.0; extra == "dev"
Requires-Dist: griffelib==2.0.2; extra == "dev"
Requires-Dist: idna==3.15; extra == "dev"
Requires-Dist: iniconfig==2.3.0; extra == "dev"
Requires-Dist: jinja2==3.1.6; extra == "dev"
Requires-Dist: librt==0.11.0; extra == "dev"
Requires-Dist: markdown==3.10.2; extra == "dev"
Requires-Dist: markupsafe==3.0.3; extra == "dev"
Requires-Dist: mergedeep==1.3.4; extra == "dev"
Requires-Dist: mkdocs==1.6.1; extra == "dev"
Requires-Dist: mkdocs-autorefs==1.4.4; extra == "dev"
Requires-Dist: mkdocs-get-deps==0.2.2; extra == "dev"
Requires-Dist: mkdocs-material==9.7.6; extra == "dev"
Requires-Dist: mkdocs-material-extensions==1.3.1; extra == "dev"
Requires-Dist: mkdocstrings==1.0.4; extra == "dev"
Requires-Dist: mkdocstrings-python==2.0.3; extra == "dev"
Requires-Dist: mypy==2.1.0; extra == "dev"
Requires-Dist: mypy-extensions==1.1.0; extra == "dev"
Requires-Dist: packaging==26.2; extra == "dev"
Requires-Dist: paginate==0.5.7; extra == "dev"
Requires-Dist: pathspec==1.1.1; extra == "dev"
Requires-Dist: pip-tools==7.5.3; extra == "dev"
Requires-Dist: platformdirs==4.9.6; extra == "dev"
Requires-Dist: pluggy==1.6.0; extra == "dev"
Requires-Dist: pygments==2.20.0; extra == "dev"
Requires-Dist: pymdown-extensions==10.21.3; extra == "dev"
Requires-Dist: pyproject-hooks==1.2.0; extra == "dev"
Requires-Dist: pytest==9.0.3; extra == "dev"
Requires-Dist: python-dateutil==2.9.0.post0; extra == "dev"
Requires-Dist: pytokens==0.4.1; extra == "dev"
Requires-Dist: pyyaml==6.0.3; extra == "dev"
Requires-Dist: pyyaml-env-tag==1.1; extra == "dev"
Requires-Dist: requests==2.34.2; extra == "dev"
Requires-Dist: setuptools-scm==10.0.5; extra == "dev"
Requires-Dist: six==1.17.0; extra == "dev"
Requires-Dist: tomli==2.4.1; extra == "dev"
Requires-Dist: typing-extensions==4.15.0; extra == "dev"
Requires-Dist: urllib3==2.7.0; extra == "dev"
Requires-Dist: vcs-versioning==1.1.1; extra == "dev"
Requires-Dist: watchdog==6.0.0; extra == "dev"
Requires-Dist: wheel==0.47.0; extra == "dev"
Requires-Dist: pip==26.1.1; extra == "dev"
Requires-Dist: setuptools==82.0.1; extra == "dev"
Dynamic: license-file

# certlib.log

`certlib.log` is a library that extends the standard [logging](https://docs.python.org/3/library/logging.html)
toolset -- making it possible to introduce _**structured logging**_ with
minimal fuss, and/or use the modern _**`{}`-based style**_ of log message
formatting, among _**other things**_...


## Basic Info

- **Documentation:** [certlib-log.readthedocs.io](https://certlib-log.readthedocs.io)
- **Repository:** [github.com/CERT-Polska/certlib-log](https://github.com/CERT-Polska/certlib-log)
- **Package:** [pypi.org/project/certlib-log](https://pypi.org/project/certlib-log/)

You can install the `certlib.log` library by running (typically, in a
[*virtual environment*](https://packaging.python.org/en/latest/tutorials/installing-packages/#creating-virtual-environments))
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](https://cert.pl/en/) -- in a possibly consistent way
and without spending too much time adjusting the existing stuff.

A **key design decision** was to build the library on top of the
standard [logging](https://docs.python.org/3/library/logging.html)
toolset (rather than introducing some alternative machinery).

In particular, this approach **makes it possible to**:

- start using the library in already existing projects (especially,
  to introduce *structured logging*) without changing a single line
  of code;

- gradually introduce selected features offered by the library
  (such as `{}`-style message formatting, message-less logging of pure
  data, or setting log entry fields automatically, e.g., from [context
  variables](https://docs.python.org/3/library/contextvars.html)...);

- retain existing logging configuration methods (whether using an
  [`*.ini` file](https://docs.python.org/3/library/logging.config.html#configuration-file-format),
  or loading a [configuration dictionary](https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema)).


## Examples

### Configuring *Structured Logging* and *Auto-Makers*

```python
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
})
```

### Logging Stuff With *`{}`-Formatted Text Message* or *No Text Message*

```python
import datetime as dt
import ipaddress
import logging
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: making use of `datetime`-specific format codes...)
        'Today is day #{today:%j} of the year {today:%Y}',
        today=dt.date.today(),

        # 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),
        ]},
    ))

def example_with_no_text(temperature, pressure, debug_data_dict, calm=True):
    # (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.)

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

    # Single dict providing data is also OK:
    logger.debug(xm(debug_data_dict))
```

You can find more examples in the [User's Guide](https://certlib-log.readthedocs.io/page/guide/).


## Copyright and License

Copyright (c) 2026, [CERT Polska](https://cert.pl/en/). All rights reserved.

The [`certlib.log`](https://github.com/CERT-Polska/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`](https://github.com/CERT-Polska/certlib-log/blob/main/LICENSE.txt)
file in the source code repository).
