Skip to content

Getting started

Installation

pip install stlog

Very minimal usage

import stlog

stlog.info("It works", foo="bar", x=123)
stlog.critical("Houston, we have a problem!")

Output (without rich library installed):

2023-03-29T14:48:37Z root [   INFO   ] It works {foo=bar x=123}
2023-03-29T14:48:37Z root [ CRITICAL ] Houston, we have a problem!

Output (with rich library installed):

rich output

Basic usage

from stlog import getLogger, setup

# Set the logging default configuration (human output on stderr)
setup()

# Get a logger
logger = getLogger(__name__)
logger.info("It works", foo="bar", x=123)
logger.critical("Houston, we have a problem!")

Output (without rich library installed):

2023-03-29T14:48:37Z __main__ [   INFO   ] It works {foo=bar x=123}
2023-03-29T14:48:37Z __main__ [ CRITICAL ] Houston, we have a problem!

Output (with rich library installed):

rich output

Usage with context

from stlog import LogContext, getLogger, setup

# Set the logging default configuration (human output on stderr)
setup()

# Get a logger
logger = getLogger(__name__)

# ...

# Set the (kind of) global execution context
# (thread, worker, async friendly: one context by execution)
# (for example in a wsgi/asgi middleware)

# You can do it through the logger with its `context` attribute (recommended):
logger.context.reset_context()
logger.context.add(request_id="4c2383f5")

# ...or through the LogContext static class directly (both are equivalent,
# logger.context *is* the LogContext static class):
LogContext.add(client_id=456, http_method="GET")

# ... in another file/class/...

logger.info("It works", foo="bar", x=123)
logger.critical("Houston, we have a problem!")

Output (without rich library installed):

2023-03-29T14:48:37Z __main__ [   INFO   ] It works {client_id=456 foo=bar http_method=GET request_id=4c2383f5 x=123}
2023-03-29T14:48:37Z __main__ [ CRITICAL ] Houston, we have a problem! {client_id=456 http_method=GET request_id=4c2383f5}

Output (with rich library installed):

rich output

What about if you want to get a more parsing friendly output (for example in JSON on stdout) while keeping the human output on stderr (without any context)?

import sys
from stlog import getLogger, setup
from stlog.output import StreamOutput
from stlog.formatter import HumanFormatter, JsonFormatter

setup(
    outputs=[
        StreamOutput(
            stream=sys.stderr,
            formatter=HumanFormatter(exclude_extras_keys_fnmatchs=["*"]),
        ),
        StreamOutput(stream=sys.stdout, formatter=JsonFormatter(indent=4)),
    ]
)

logger = getLogger(__name__)

# See previous example for details
logger.context.reset_context()
logger.context.add(request_id="4c2383f5")
logger.context.add(client_id=456, http_method="GET")
logger.info("It works", foo="bar", x=123)
logger.critical("Houston, we have a problem!")

Human output (on stderr):

2023-03-29T14:48:37Z __main__ [   INFO   ] It works
2023-03-29T14:48:37Z __main__ [ CRITICAL ] Houston, we have a problem!

JSON ouput (on stdout) for machines:

{
    "client_id": 456,
    "foo": "bar",
    "http_method": "GET",
    "level": "INFO",
    "logger": "__main__",
    "message": "It works",
    "request_id": "4c2383f5",
    "source": {
        "funcName": "<module>",
        "lineno": 22,
        "module": "qs3",
        "path": "/path/filename.py",
        "process": 6789,
        "processName": "MainProcess",
        "thread": 12345,
        "threadName": "MainThread"
    },
    "time": "2023-03-29T14:48:37.000Z",
    "x": 123
}
{
    "client_id": 456,
    "http_method": "GET",
    "level": "CRITICAL",
    "logger": "__main__",
    "message": "Houston, we have a problem!",
    "request_id": "4c2383f5",
    "source": {
        "funcName": "<module>",
        "lineno": 23,
        "module": "qs3",
        "path": "/path/filename.py",
        "process": 6789,
        "processName": "MainProcess",
        "thread": 12345,
        "threadName": "MainThread"
    },
    "time": "2023-03-29T14:48:37.000Z"
}