Eliot: logging that records why, not just what
Eliot: the logging system that tells you *why* it happened
At a glance
- What is it?
- Eliot is a Python logging system that models your program as a tree of actions, where an action can spawn other actions and eventually succeeds or fails, so a log line becomes part of a causal chain rather than an isolated fact. It generates logs only, and it leaves storing and searching them to Logstash, Elasticsearch or whatever you already run.
- Who is it for?
- Eliot fits a Python service whose failures are hard to explain because you cannot tell which of several concurrent operations caused the error, and it fits scientific and distributed code where the same causal model has to hold across threads, processes and frameworks. It does not fit a team that wants a logging product with storage and search included, because Eliot only generates logs and you still need a collector.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 168 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 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Standard logging gives you facts, and facts do not explain
Eliot's pitch starts with an observation about the standard library. Python's built-in logging and other similar systems output a stream of factoids: they're interesting, but you can't really tell what's going on.
The README then lists the three questions that factoids cannot answer. Why is your application slow? What caused this code path to be chosen? Why did this error happen?
That is an accurate description of the gap. A log line saying that a request took four seconds tells you nothing about which of the three things it did took those four seconds. A log line saying that a fallback path was taken tells you the fallback happened, not why the primary path was not available. These are the questions that make an incident expensive, and the standard model has no place to put the answer, because a line has no parent.
Eliot's answer is a different model rather than more fields. It is a Python logging system that outputs causal chains of actions, where actions can spawn other actions, and eventually they either succeed or fail. The resulting logs tell the story of what your software did: what happened, and what caused it.
The audience is therefore a developer with a specific complaint, most likely a service where a user reports an error and the logs contain a hundred lines about that request with no indication of which of them is the actual failure. The project is maintained by Itamar Turner-Trauring and released under the Apache 2.0 licence, and the documentation is at eliot.readthedocs.io.
The action tree is the whole idea
An action is the unit of work, and the system's semantics are the standard transactional ones: an action starts, it may spawn child actions, and it ends in one of two states, success or failure.
That vocabulary is what makes the log causal rather than chronological. When an action fails, the chain of actions above it is the explanation. When a request is slow, the chain of actions inside it shows which child took the time. The difference from a flat log is not that more information is written, it is that each entry knows what it is inside.
Two details from the repository show what the model looks like in practice. The examples directory contains files named cross_thread.py, cross_process_client.py and cross_process_server.py, which are the three cases where causality is hardest to preserve, because the parent action lives in one thread or one process and the child lives in another. And the testimonial in the README describes a team whose errors are logged to Sentry with the Eliot task UUID, which lets them go from a Sentry notification to a high-level trace of operations with important metadata at each operation in a few seconds, and know which user did what in which part of the system.
That last point is the practical payoff. A task UUID in an error report is a key into the causal tree, so the trace of what led to the failure is one lookup away from the alert rather than something you assemble by hand from timestamps.
The same model has to survive threads, processes and frameworks
Eliot is not only for request handlers in a web service, and the range of use cases in the README is the clearest sign of that.
It supports logging within a single process, causal tracing across a distributed system, and scientific computing with built-in support for NumPy and Dask. It also supports Asyncio and Trio coroutines and the Twisted networking framework, each with its own documentation page.
Each of those is a place where the parent-child relationship of an action can be lost, and each gets a dedicated example in the repository. There is a dask_eliot.py for the Dask case, a trio_say.py for Trio, an asyncio_linkcheck.py for asyncio, a rometrip_actions.py that shows the model applied to a domain rather than to a toy, and a linkcheck.py, which is a small program that checks links and is the natural place to see the log of a real, if simple, run.
There is also a stdlib.py example, and that one matters for anyone arriving from the standard library, because it shows how Eliot coexists with ordinary logging rather than replacing it.
The threading and multi-process examples are the ones to study if you are adopting this. Those are the cases where a naive implementation loses the parent, and where a lost parent means a log that has a start time and an end time but no explanation.
Eliot writes logs, it does not keep them
There is a limitation in the README that is stated in a single sentence and deserves more attention than it usually gets.
Eliot is only used to generate your logs; you will might need tools like Logstash and ElasticSearch to aggregate and store logs if you are using multiple processes across multiple machines.
So Eliot is a logging library in the strict sense, not a logging system in the operational sense. It produces structured records describing actions, and then something else has to receive them, index them and let you search them. If you are running one process on one machine and reading the output in a terminal, that something else is a file. If you are running a fleet, it is a pipeline you already have, or one you have to build.
The dependency list tells you which path is likely. The project depends on orjson, but only on CPython, which is a fast JSON serialiser, and on pyrsistent for persistent objects, boltons for better decorators with type annotations, and zope.interface for internal code documentation. The journald extra adds cffi, because that is how the project talks to the journald API, and the examples include a journald.py to match.
A machine that is already writing to journald, or already shipping JSON to a collector, has somewhere for these records to go. A team that has no log infrastructure at all will find that Eliot gives them excellent causality in a file nobody queries, which is a worse outcome than plain logging would have been.
Installing and pointing it at a destination
The install itself is unremarkable, and the README gives you the two channels: the package is on PyPI and on conda-forge under the name eliot.
The journald integration is an extra rather than part of the default install, so if you are writing to journald you need to ask for it, and the setup metadata names the extra and the reason for the version floor on cffi, because there are significant API changes in older releases.
Two practical notes from the packaging metadata. The build is setuptools with setuptools-scm and versioneer, and the version is read from the repository rather than written into the file, which is why the versioneer.py sits at the top level of the tree. And the supported Python range is stated twice, in the README and in the classifiers: Python 3.10 through 3.14, plus PyPy 3.11. The python_requires is >=3.10.0.
For a first run, the cheapest thing to do is to take one of the examples and run it rather than write your own instrumentation from scratch. The linkcheck example is a complete program with a real task, and the stdout.py example is the shortest path to seeing the shape of the output. The documentation has a page per framework, and the recipe for the standard library is in the stdlib example, which is where you want to start if the goal is to introduce Eliot into an existing codebase one call site at a time rather than rewriting the logging.
Apache-2.0, one maintainer, and a support business attached
Eliot is released under the Apache 2.0 licence, and the licence is spelled out in the setup metadata classifiers as License:: OSI Approved:: Apache Software License. There is no licence complication and no copyleft obligation on a proprietary service that uses it.
The project is maintained by Itamar Turner-Trauring, and the README links to commercial support available from Python⇒Speed. That combination is worth naming plainly: this is a single-maintainer library with a paid support option attached, which is a different risk profile from a foundation-hosted project with many committers, and also a different one from an abandoned personal repository. The maintainer has an incentive to keep the library working, and there is a way to pay for help when the problem is urgent.
The release history is sparse. Versions 1.17.4 and 1.17.5 were both published on 2025-02-28, and 1.18.0 on 2026-05-07, and the last push to the repository was on that same day. So the newest tagged release dates from the same day as the last push to the repository. For a library whose compatibility range already reaches Python 3.14, pinning the tagged release is the safe choice.
The repository itself is a conventional Python project with a tox.ini, a.coveragerc, a.dir-locals.el, a benchmarks directory, a docs directory, a presentations directory and the examples already mentioned. The presence of a presentations directory is a small detail that says something about the project's nature: this is a library whose author has had to explain the causal model to other people more than once, which is what you would expect from a design that replaces the idea of a log line with the idea of a tree.
Editorial conclusion
Eliot fits a Python service whose failures are hard to explain because you cannot tell which of several concurrent operations caused the error, and it fits scientific and distributed code where the same causal model has to hold across threads, processes and frameworks. It does not fit a team that wants a logging product with storage and search included, because Eliot only generates logs and you still need a collector. Before you adopt it, check that your Python version is between 3.10 and 3.14, decide which destination your action logs go to, and read the standard library integration example, since migrating existing log calls is the bulk of the work.
Frequently asked questions
What is Eliot and how is it different from standard logging?
Eliot is a Python logging system that outputs causal chains of actions, where actions can spawn other actions and eventually succeed or fail. Unlike the standard library's stream of factoids, the resulting logs tell what happened and what caused it, so you can answer why an application is slow or why an error happened.
Does Eliot store the logs it produces?
No. Eliot is only used to generate your logs, and the README says you will might need tools like Logstash and ElasticSearch to aggregate and store logs if you are using multiple processes across multiple machines. The journald integration is an extra that needs cffi.
Which Python versions does Eliot support?
The README states that Eliot supports Python 3.10 to 3.14, as well as PyPy 3.11, and the packaging metadata sets python_requires to >=3.10.0 and lists the same CPython versions in its classifiers.
Does Eliot work with asyncio, Trio, Twisted, Dask and NumPy?
Yes. The README lists support for Asyncio and Trio coroutines, the Twisted networking framework, and scientific computing with built-in support for NumPy and Dask, each with its own documentation page and an example in the repository's examples directory.
Who maintains Eliot and what licence is it under?
It is maintained by Itamar Turner-Trauring and released under the Apache 2.0 licence, with commercial support available from Python⇒Speed. The recent releases are 1.17.5 from 2025-02-28 and 1.18.0 from 2026-05-07.
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/itamarst-eliot)Was this useful?