Skip to content

Usage

Setup

The first thing to do is to import and call the setup function:

from stlog import setup

# Set default configuration
setup()

You have to do this once, probably in your main code. It will set up a new python logging configuration.

By default, we:

  • set a default level of INFO
  • set a human output on stderr (with an automatic choice between rich output (with colors...) and standard output depending if rich library is installed and if we write in a real terminal)

You can change these defaults with environment variables or with key/values in this setup function.

Get a logger

Then you can get a stlog logger.

Why a custom logger? Can I use a standard python logging logger?

The stlog logger is a standard LoggerAdapter. But you have to use it (and not a plain logging.getLogger one) to be able to pass extra context when you use your logger.

If you don't need to pass extra context at logging time (because a global context is enough or because you don't want context at all), you can use a standard python logger.

from stlog import setup, getLogger

# Set default configuration
setup()

# Get a logger
my_logger = getLogger("myloggername")

# log something
my_logger.warning("this is a warning")

rich output

Set a context

You have 4 ways of defining some context. Let's describe them starting with the more general ones.

(1) With environment variables

First, you can use JSON inside STLOG_JSON_CONTEXT env var

$ export STLOG_ENV_JSON_CONTEXT='{"foo": "bar", "foo2": 123}'
$ python <<EOF
from stlog import setup, getLogger

setup()
getLogger("myloggername").info("this is a test")

EOF

rich output

Second, you can use STLOG_CONTEXT_* env var

$ export STLOG_ENV_CONTEXT_FOO="bar"
$ export STLOG_ENV_CONTEXT_FOO2="123"
$ python <<EOF
from stlog import setup, getLogger

setup()
getLogger("myloggername").info("this is a test")

EOF

rich output

This is less hacky than the JSON way but:

  • you don't control the case of keys
  • you don't control the type of values (always str) (can be an issue with JSON output)

In both cases, env variables are only read once (at program startup).

You can completly disable this feature by setting STLOG_IGNORE_ENV_CONTEXT=1.

(2) With LogContext static class

You can use the static class LogContext where you want to define some key/values that will be copied to each logger call context.

In a web context for example, a common practice is to set some key/values in a middleware.

As this context is global

from stlog import setup, getLogger, LogContext

# in the main
setup()

# somewhere (wsgi/asgi middlewares...)
LogContext.reset_context()
LogContext.add(request_id=15843224, http_method="GET", authenticated=True)

# elsewhere
getLogger("myloggername").info("this is a test")

rich output

ExecutionGlobalContext is global, what about using it in threads or in async code?

Thanks to contextvars, LogContext is more clever than a global dict, it's a kind of global context but specific for each tread or for each coroutine.

Here is a good introduction about contextvars.

You can considerer that LogContext is just a light wrapper on contextvars.

(3) At the logger instantiation

from stlog import setup, getLogger

# Set default configuration
setup()

# Get a logger with some context
my_logger = getLogger("myloggername", request="foo", id=123456)

# log something
my_logger.warning("this is a warning")

# log something with some extra context
my_logger.critical("this is a critical", xtr="foobar")

rich output

(4) At the logger log call

from stlog import setup, getLogger

# Set default configuration
setup()

# Get a logger
my_logger = getLogger("myloggername")

# log something with some extra context
my_logger.critical("this is a critical", request="foo", id=123456)

rich output

Using the logger

The stlog.Logger is only an adapter on the python standard Logger.

So you can use it as a drop in replacement.

The only thing you get is the ability to pass some extra kwargs as key/values.

from stlog import setup, getLogger

# Set default configuration
setup(level="DEBUG")

# Get a logger
my_logger = getLogger("myloggername")

my_logger.debug("Classic example with %s", "positional arguments")
my_logger.info(
    "Example with both *args (%s, %s) and **kwargs",
    "foo",
    "bar",
    foo="bar",
    foo2="bar2",
)
try:
    1 / 0
except Exception:
    # Note: exc_info is a reserved kwargs on python standard logger
    my_logger.warning("this is a warning with exc_info=True", exc_info=True)

# Note: extra is a reserved kwargs on python standard logger
# to provide some structured logging features
# (=> we recommand to use classic **kwargs instead)
my_logger.error(
    "Using a standard extra",
    foo="bar",
    foo2="bar2",
    extra={"foo3": "bar3", "foo4": "bar4"},
)

rich output

API reference

The public API of the library is available here: API reference.

Public API

Only what is documented on this previous link is considered as a part of the public API So, if it is not documented, it's a part of the private API (and it can change at any time).