structlog: structured logging for Python without leaving the stdlib behind
Simple, powerful, and fast logging for Python.
At a glance
- What is it?
- structlog turns log records into dictionaries that pass through a chain of functions, and lets you decide whether it renders them or hands them to the standard logging module. It fits teams who need machine-readable logs and are willing to learn a small pipeline model.
- Who is it for?
- Adopt structlog if you need machine-readable logs and want control over the processor chain, especially in an existing stdlib logging setup you cannot replace. Do not adopt it if you want a single import with no configuration, or if you need a logging library that is not tied to the Python ecosystem.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 2, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem structlog solves: logs as data, not as sentences
A conventional Python log call produces a string. Once that string is written, the fields inside it exist only as text, and anything that wants to query them has to parse them back out. structlog's premise is that a log entry should be a dictionary first and a rendered line second. The README states the design plainly: everything is about functions that take and return dictionaries, hidden behind familiar APIs.
The intended audience is teams running Python services where logs are consumed by something other than a human reading a terminal. If your logs end up in a search system, an alerting rule, or a request trace, the dictionary model pays off. If you are writing a short script whose output a person reads once, the extra layer is overhead you will not recover.
The project has been in production use since 2013 according to the README, and the package metadata classifies it as Production/Stable. That history matters less than the shape of the API, which is what you will live with.
How the processor chain and bound loggers actually work
structlog's mechanism is a pipeline. A log call produces an event dictionary, and each processor is a callable that receives that dictionary and returns a dictionary, so processors compose in sequence. Typical stages add a timestamp, add a log level, attach context, and finally render. The renderer at the end of the chain decides the output format, and the README names JSON, logfmt and pretty console output as supported out of the box.
Bound loggers are the second half of the design. Calling bind on a logger returns a new logger carrying extra key-value pairs, and those pairs travel with every subsequent event from that logger. The README credits the idea of bound loggers to earlier work by Jean-Paul Calderone and David Reid. This is what makes per-request context practical: bind a request id once, and every log line in that request carries it without threading an argument through each call site.
The output decision is explicitly yours. The README says you choose whether structlog takes care of the output or forwards entries to an existing logging system such as the standard library's logging module. That forwarding path is the reason structlog can be introduced into a codebase that already has handlers, formatters and log aggregation configured, rather than requiring a parallel logging stack.
Installing structlog and getting a first structured line out
structlog is distributed on PyPI as the package structlog. The project requires Python 3.10 or newer, and the only declared dependency is typing-extensions, and only on Python versions below 3.11. The README points to PyPI for the package, so the install is the ordinary one for a published distribution:
python -m pip install structlogAfter installing, the documentation's Getting Started tutorial is the canonical walkthrough for configuration. The README does not reproduce that tutorial inline; it links to the Getting Started page and to the Why chapter, and it points to a third-party guide and a conference talk for longer treatments. Work through the Getting Started page before writing your own processor chain, because the order of processors determines what each stage can see.
Once configured, the pattern the README describes is that you obtain a logger and pass keyword arguments that become dictionary keys, with the renderer at the end of the chain turning the event into JSON, logfmt or console output. You should see one line per call containing the event name and the fields you passed. Swap the renderer during local development if you would rather read colored output than parse JSON by eye.
Where structlog gets in your way
The processor chain is the cost of the flexibility. Every field you want in your output has to be produced by something in the chain, and the order of processors determines what each one can see. A processor that needs the timestamp must run after the one that adds it. Debugging a misordered chain produces confusing output rather than an error, because processors are ordinary callables and structlog has no way to know what you intended.
Configuration is global by default. structlog.configure sets the default configuration for the process, which is convenient in an application and awkward in a library. A library that calls configure changes logging behavior for everything else running in the same interpreter. The README does not document a per-library isolation mechanism, so a library author should treat global configuration as something to avoid rather than something to manage.
structlog is also not a log transport. It produces and formats entries. Shipping them somewhere durable is still the job of stdlib logging handlers, a sidecar, or whatever collector you already run. Teams expecting structlog to replace the whole logging stack will find it stops at the renderer.
structlog versus the standard library and versus richer loggers
The most common comparison is structlog against Python's own logging module. The difference is where structure lives. In stdlib logging you pass a format string and arguments, and the structure exists only if your formatter reconstructs it. In structlog the dictionary is the primary object and the string is derived from it. The README's framing is that structlog can forward to stdlib logging, which means the two are not mutually exclusive: you can adopt structlog for the call sites while keeping the handlers and configuration you already trust.
Against loguru, the difference is philosophy rather than features. loguru presents a single ready-made logger with sinks and a fixed formatting model, so you get useful output with almost no configuration. structlog asks you to assemble a processor chain before you get anything. That is more work up front and more control later, particularly when the output format has to match a schema your log pipeline enforces.
Against a JSON formatter bolted onto stdlib logging, structlog's advantage is bound context and the ability to transform events before rendering. A formatter can only change how a record is printed; a processor can add, rename or drop fields. If your requirements stop at JSON-shaped lines, a formatter may be enough.
Maintenance, licensing and the upgrade surface
The repository is not archived, and the last push was on 2026-09-05. Recent releases listed are 26.1.0 on 2026-06-06, 25.5.0 on 2025-10-27 and 25.4.0 on 2025-06-02. The version numbers track the calendar year, and the changelog lives at CHANGELOG.md in the repository, which is where breaking changes between those releases would be recorded.
Licensing is dual: the package metadata declares license = "MIT OR Apache-2.0", the badge in the README reads MIT / Apache 2.0, and the repository carries both LICENSE-MIT and LICENSE-APACHE as separate files. The GitHub metadata reports the licence as NOASSERTION, which reflects that automated detection cannot resolve a dual-licence expression; the authoritative files are in the repository root. Choosing between the two options is a decision for your own legal review, not something the project makes for you.
Upgrade cost is bounded by how much of the processor chain you wrote yourself. Built-in processors and renderers are the project's responsibility. Custom processors are yours, and a signature change in the processor contract would surface there first. The repository includes a bench/ directory and a typing_tests/ directory, and the test dependencies include mypy and pytest, so the project tests both behavior and type stubs; that is a signal about what the maintainer considers part of the contract, not a guarantee about your custom code.
Editorial conclusion
Adopt structlog if you need machine-readable logs and want control over the processor chain, especially in an existing stdlib logging setup you cannot replace. Do not adopt it if you want a single import with no configuration, or if you need a logging library that is not tied to the Python ecosystem. Before committing, check the configuration you intend to ship against the Getting Started tutorial and the Why chapter in the official docs, and confirm the licence terms in LICENSE-MIT and LICENSE-APACHE for your distribution model.
Frequently asked questions
What is structlog and how is it used in Python?
structlog is a structured logging library for Python in which log entries are dictionaries that pass through a chain of processor functions before being rendered or forwarded. You configure the processor chain once, then obtain loggers and call methods such as info with keyword arguments that become event fields.
How do I install structlog?
It is published on PyPI as the package structlog, so it installs with pip. The package metadata requires Python 3.10 or newer.
What is the difference between structlog and Python's standard logging module?
In the standard library the log record is formatted into a string, while in structlog the dictionary is the primary object and the rendered line is derived from it. structlog can also forward its entries to the standard library's logging module, so the two can be used together rather than as replacements for each other.
What does structlog do?
It produces structured log entries as dictionaries and passes them through a processor chain that adds context and renders the result. The README states that structlog can also forward entries to an existing logging system such as the standard library's logging module.
What is structlog used for?
It is used for structured logging in Python: log calls carry key-value data, bound loggers attach context that travels with every event, and a renderer at the end of the chain emits JSON, logfmt or pretty console output.
What is a structured log?
A structured log is a log entry whose fields are data rather than a formatted sentence. In structlog this is literal: an entry is a dictionary that processors transform before it is rendered.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/hynek-structlog)