==== Why… ==== …Structured Logging? ==================== I believe the widespread use of format strings in logging is based on two presumptions: - The first level consumer of a log message is a human. - The programmer knows what information is needed to debug an issue. I believe these presumptions are **no longer correct** in server side software. ---`Paul Querna `_ Structured logging means that you don't write hard-to-parse and hard-to-keep-consistent prose in your log. Instead, you log *events* that happen in a *context* of key/value pairs. …structlog? =========== Easier Logging ============== You can stop writing prose and start thinking in terms of an event that happens in the context of key/value pairs: .. code-block:: pycon >>> from structlog import get_logger >>> log = get_logger() >>> log.info("key_value_logging", out_of_the_box=True, effort=0) 2020-11-18 09:17.09 [info ] key_value_logging effort=0 out_of_the_box=True Each log entry is a meaningful dictionary instead of an opaque string now! Data Binding ============ Since log entries are dictionaries, you can start binding and re-binding key/value pairs to your loggers to ensure they are present in every following logging call: .. code-block:: pycon >>> log = log.bind(user="anonymous", some_key=23) >>> log = log.bind(user="hynek", another_key=42) >>> log.info("user.logged_in", happy=True) 2020-11-18 09:18.28 [info ] user.logged_in another_key=42 happy=True some_key=23 user=hynek You can also bind key/value pairs to `thread-local storage `_ and `contextvars `_. Powerful Pipelines ================== Each log entry goes through a `processor pipeline `_ that is just a chain of functions that receive a dictionary and return a new dictionary that gets fed into the next function. That allows for simple but powerful data manipulation: .. code-block:: python def timestamper(logger, log_method, event_dict): """Add a timestamp to each log entry.""" event_dict["timestamp"] = time.time() return event_dict There are `plenty of processors `_ for most common tasks coming with ``structlog``: - Collectors of `call stack information `_ ("How did this log entry happen?"), - …and `exceptions `_ ("What happened‽"). - Unicode encoders/decoders. - Flexible `timestamping `_. Formatting ========== ``structlog`` is completely flexible about *how* the resulting log entry is emitted. Since each log entry is a dictionary, it can be formatted to **any** format: - A colorful key/value format for `local development `_, - `JSON `_ for easy parsing, - or some standard format you have parsers for like nginx or Apache httpd. Internally, formatters are processors whose return value (usually a string) is passed into loggers that are responsible for the output of your message. ``structlog`` comes with multiple useful formatters out-of-the-box. Output ====== ``structlog`` is also flexible with the final output of your log entries: - A **built-in** lightweight printer like in the examples above. Easy to use and fast. - Use the **standard library**'s or **Twisted**'s logging modules for compatibility. In this case ``structlog`` works like a wrapper that formats a string and passes them off into existing systems that won't know that ``structlog`` even exists. Or the other way round: ``structlog`` comes with a ``logging`` formatter that allows for processing third party log records. - Don't format it to a string at all! ``structlog`` passes you a dictionary and you can do with it whatever you want. Reported uses cases are sending them out via network or saving them in a database. Highly Testable =============== ``structlog`` is thoroughly tested and we see it as our duty to help you to achieve the same in *your* applications. That's why it ships with a `bunch of helpers `_ to introspect your application's logging behavior with little-to-no boilerplate.