Simple, powerful, and fast logging for Python.
The main reason for this comparatively timely release is that aiohttp 3.8's new behavior of starting new loops within aiohttp.web.run_app()
led to breakage in apps that use structlog.stdlib.AsyncBoundLogger
.
The one big new feature though is the support for much more powerful processor chains within structlog.stdlib.ProcessorFormatter
. This took me way too long to get right, but I'm excited to share it with you.
This is also the first release without a setup.py
. Invoking it was never tested and never supported, so now it's gone. Please use standard packaging tools like PyPA's build or flit directly if you want to package structlog
yourself.
structlog
switched its packaging to flit. Users shouldn't notice a difference, but (re-)packagers might.none
structlog.dev.ConsoleRenderer
now has sort_keys
boolean parameter that allows to disable the sorting of keys on output. #358
structlog.processors.TimeStamper
now works well with FreezeGun even when it gets applied before the loggers are configured. #364
structlog.stdlib.AsyncBoundLogger
now determines the running loop when logging, not on instantiation. That has a minor performance impact, but makes it more robust when loops change (e.g. aiohttp.web.run_app()
), or you want to use sync_bl
before a loop has started.
structlog.stdlib.ProcessorFormatter
now has a processors argument that allows to define a processor chain to run over all log entries.
Before running the chain, two additional keys are added to the event dictionary: _record
and _from_structlog
. With them it's possible to extract information from logging.LogRecord
s and differentiate between structlog
and logging
log entries while processing them.
The old processor (singular) parameter is now deprecated, but no plans exist to remove it. #365
To implement pretty exceptions (see Changes below), structlog.dev.ConsoleRenderer
now formats exceptions itself.
Make sure to remove format_exc_info
from your processor chain if you configure structlog
manually. This change is not really breaking, because the old use-case will keep working as before. However if you pass pretty_exceptions=True
(which is the default if either rich
or better-exceptions
is installed), a warning will be raised and the exception will be renderered without prettyfication.
none
structlog
is now importable if sys.stdout
is None
(e.g. when running using pythonw
). #313
structlog.threadlocal.get_threadlocal()
and structlog.contextvars.get_contextvars()
can now be used to get a copy of the current thread-local/context-local context that has been bound using structlog.threadlocal.bind_threadlocal()
and structlog.contextvars.bind_contextvars()
. #331 #337
structlog.threadlocal.get_merged_threadlocal(bl)
and structlog.contextvars.get_merged_contextvars(bl)
do the same, but also merge the context from a bound logger bl. Same pull requests as previous change.
structlog.contextvars.bind_contextvars()
now returns a mapping of keys to contextvars.Token
s, allowing you to reset values using the new structlog.contextvars.reset_contextvars()
. #339
Exception rendering in structlog.dev.ConsoleLogger
is now configurable using the exception_formatter
setting. If either the rich or the better-exceptions package is present, structlog
will use them for pretty-printing tracebacks. rich
takes precedence over better-exceptions
if both are present.
This only works if format_exc_info
is absent in the processor chain. #330 #349
All use of colorama
on non-Windows systems has been excised. Thus, colors are now enabled by default in structlog.dev.ConsoleRenderer
on non-Windows systems. You can keep using colorama
to customize colors, of course. #345
The final processor can now return a bytearray
(additionally to str
and bytes
). #344
Python 2.7 and 3.5 aren't supported anymore. The package meta data should ensure that you keep getting 20.1.0 on those versions. #244
structlog
is now fully type-annotated. This won't break your applications, but if you use Mypy, it will most likely break your CI.
Check out the new chapter on typing for details.
_context
attribute of a bound logger is now deprecated. Please use the new structlog.get_context()
.structlog
has now type hints for all of its APIs! Since structlog
is highly dynamic and configurable, this led to a few concessions like a specialized structlog.stdlib.get_logger()
whose only difference to structlog.get_logger()
is that it has the correct type hints.
We consider them provisional for the time being – i.e. the backward compatibility does not apply to them in its full strength until we feel we got it right. Please feel free to provide feedback! #223, #282
Added structlog.make_filtering_logger
that can be used like configure(wrapper_class=make_filtering_bound_logger(logging.INFO))
. It creates a highly optimized bound logger whose inactive methods only consist of a return None
. This is now also the default logger.
As a complement, structlog.stdlib.add_log_level()
can now additionally be imported as structlog.processors.add_log_level
since it just adds the method name to the event dict.
structlog.processors.add_log_level()
is now part of the default configuration.
structlog.stdlib.ProcessorFormatter
no longer uses exceptions for control flow, allowing foreign_pre_chain
processors to use sys.exc_info()
to access the real exception.
Added structlog.BytesLogger
to avoid unnecessary encoding round trips. Concretely this is useful with orjson which returns bytes. #271
The final processor now also may return bytes that are passed untouched to the wrapped logger.
structlog.get_context()
allows you to retrieve the original context of a bound logger. #266,
structlog.PrintLogger
now supports copy.deepcopy()
. #268
Added structlog.testing.CapturingLogger
for more unit testing goodness.
Added structlog.stdlib.AsyncBoundLogger
that executes logging calls in a thread executor and therefore doesn't block. #245