Usage
Setup
The first thing to do is to import and call the setup
function:
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 ifrich
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")
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
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
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")
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")
(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)
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"},
)
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).