Quill (odygrd/quill) review: asynchronous C++17 logging and metrics for latency-sensitive threads
Ultra-low-latency asynchronous C++17 logging and metrics library for performance-critical applications
At a glance
- What is it?
- Quill moves formatting and I/O off the calling thread by encoding log arguments on the frontend and processing them on a dedicated backend worker. This review covers the mechanism, a first build, the caveats the README lists, and who should stay away.
- Who is it for?
- Adopt Quill when a latency-sensitive C++17 thread cannot afford formatting or file I/O inline, and you are willing to tune queue capacity and memory policy at compile time. Do not adopt it if you need a synchronous logger whose records are on disk by the time the call returns, or if you want a metrics system with its own scrape endpoint rather than a sink.
- Can I use it commercially?
- Yes. MIT 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 18 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Quill targets: formatting and I/O on the hot thread
A conventional logger does its work where the log statement is written. The call formats the message, takes a lock, and writes to a file or console. On a request-handling thread that is the wrong place to spend time, because the cost lands inside the interval you are trying to keep short. Quill's answer is to split the call in two. The frontend, running on the calling thread, encodes the log arguments into a queue. A dedicated backend worker thread formats them and hands the result to a sink. The README describes this as keeping formatting and I/O away from latency-sensitive application threads.
The audience is narrow and specific. This is a library for C++17 and later projects where a thread has a latency budget and logging sits inside it: trading systems, audio and media pipelines, game servers, and instrumentation inside a hot loop. If your program logs a few thousand lines a minute from a background job, the asynchronous machinery buys you little and costs you a thread plus configuration surface.
The same backend also carries metrics. Quill lets you publish pre-registered metrics through the asynchronous path, and ships a Prometheus sink for common metric types. That is a deliberate consolidation: one queue, one worker, both log lines and samples. The trade-off is that metrics inherit the logger's delivery characteristics, including whatever happens when the queue fills.
How the frontend and backend split actually works
The data flow is encode, queue, format, sink. On the calling thread Quill encodes the arguments rather than rendering them, so the expensive part of formatting is deferred. The backend worker drains the queue, performs the formatting, and passes the finished record to a sink. Sinks are the output stage, and loggers are composed from built-in or custom sinks with per-sink filters, so a single log call can be routed to more than one destination with different filtering rules.
The configuration surface is split along the same line. Frontend behaviour, meaning queue sizing and memory policy, is set at compile time. Backend behaviour, meaning idle handling, CPU affinity, buffering, timestamp handling, flushing, and callbacks, is configured at runtime. That split is the most consequential design decision in the library. Compile-time frontend options mean the queue and its memory strategy are baked into the binary, so changing them is a rebuild, not a config edit. Runtime backend options mean you can adjust flushing and buffering without recompiling.
Two details in the repository layout confirm the intended usage pattern. There is a whole directory of frontend examples, including bounded_dropping_queue_frontend.cpp and custom_frontend_options.cpp, which tells you the queue policy is expected to be a per-application choice rather than a default you accept. And there are separate examples for manual_backend_worker.cpp and backend_thread_notify.cpp, which suggests the backend worker's lifecycle and wake-up behaviour are things the library expects you to think about rather than hide.
Building Quill and writing a first log statement
The README's Quick Start section points at an Installation subsection, and the repository ships CMakeLists.txt plus cmake/, meson.build, BUILD.bazel and MODULE.bazel at the top level. That means CMake, Meson and Bazel are all first-class build paths, and the choice of which to use belongs to your existing build system rather than to Quill.
For a first real use, the examples directory is the intended entry point. console_logging.cpp is the smallest end-to-end program: it sets up a logger and writes to the console sink. The macro-free variant, console_logging_macro_free.cpp, is worth reading if your codebase bans logging macros. If your target is files rather than a terminal, file_logging.cpp is the closest match, and rotating_file_logging.cpp covers the rotation case. The example targets are declared in examples/CMakeLists.txt.
What you should see when console_logging runs is log output on stdout produced by the backend worker, not by the thread that issued the call. Once that runs, the natural second step is json_file_logging.cpp if you want structured output, or metric_publishing_prometheus.cpp if you came for the metrics half of the library.
One thing the README does not do in the section available is list the individual CMake option names. Before wiring Quill into a larger superbuild, read CMakeLists.txt and the files under cmake/ for the exact flags rather than guessing them.
What the Caveats section implies about queue behaviour
The README has a dedicated Caveats section in its table of contents. That is a signal in itself: the author considers the failure modes important enough to name rather than bury. The section is not reproduced in the portion of the README available, so the specific caveats cannot be quoted, and anyone evaluating Quill for a production system should read it directly before committing.
The structural limitation is clear from the design regardless. An asynchronous logger decouples the call from the write, which means a log statement returning successfully is not a guarantee that the record reached a file. If the process terminates abruptly, records still sitting in the frontend queue or the backend buffer are lost. Quill exposes flushing as a runtime backend option precisely because that window exists, and the backend_tsc_clock.cpp and backtrace_logging.cpp examples exist because timestamp sourcing and crash-time log recovery are the two places where that window bites hardest.
There is a second boundary. The frontend queue has a capacity, and the repository ships a bounded_dropping_queue_frontend.cpp example. A bounded queue that drops is the correct choice for a latency-critical thread, because blocking to preserve a log line defeats the purpose of the library. But it means that under sustained overload you lose records rather than slow down. If your requirement is that every log line survives, Quill's async model is working against you, and a synchronous logger with a blocking write is the honest answer.
Metrics inherit this. A dropped sample is a dropped sample, and a Prometheus counter that silently loses increments under load is worse than no counter. The README's own framing, that metrics are published through the same asynchronous backend, should be read as a shared-fate statement, not just a convenience.
Quill against spdlog and the synchronous default
The obvious alternative is spdlog, which is also a header-friendly C++ logging library with fmt-style formatting, and which supports both synchronous and asynchronous loggers. The difference is in where the default sits and how much is decided at compile time.
spdlog's async mode uses a thread pool and a queue, and its synchronous mode writes on the calling thread. You choose at logger construction. Quill does not offer a synchronous mode at all: every log call goes through the frontend encode and the backend worker. That is a narrower design, and it is the reason the frontend queue and memory policy are compile-time options rather than runtime knobs. The library has committed to one execution model and optimised the configuration around it.
The second difference is scope. spdlog is a logging library. Quill is a logging and metrics library with a bundled Prometheus sink and a documented path for custom sinks to StatsD or OpenTelemetry. If you need metrics and you are already writing C++, consolidating them onto one backend worker is a real reduction in moving parts. If you already run a Prometheus client library and a separate metrics pipeline, Quill's sink is redundant.
A third option is to keep a synchronous logger and move the call off the hot path yourself, into a lock-free ring buffer that a background thread drains. That is essentially what Quill does, except you own the ring buffer, the formatting, and the sink dispatch. Quill is worth taking when you would rather not own that code; it is not worth taking if you already have it and it works.
Maintenance, licensing and what a version bump costs you
The repository is not archived. The last push was on 2026-09-13, and releases v13.0.0, v12.2.2 and v12.2.1 landed between 2026-08-24 and 2026-08-30. That is a project with current activity, and the presence of a CHANGELOG.md at the top level means version-to-version changes are documented rather than left to git history.
The upgrade cost is concentrated in the compile-time frontend options. Because queue sizing and memory policy are compile-time settings, a change to those options is a rebuild and a redeploy of every binary that links Quill, not a config push. The v13.0.0 major bump is the version to read the changelog for before moving, since major versions are where compile-time interfaces tend to shift. The runtime backend options are the cheaper half: idle behaviour, CPU affinity, buffering, timestamp handling and flushing can be adjusted without recompiling.
The licence is MIT, per the repository's LICENSE file and the badge in the README. MIT is permissive: it allows use in closed-source products provided the copyright notice and permission notice are preserved. That is a summary of the licence text, not legal advice, and if Quill ships inside a commercial binary you should have someone check the notice requirements against how you distribute.
One practical note on the testing story. The README states the library is continuously tested across Linux, macOS, Windows and BSD, with sanitizers and fuzzing, and the workflow files for Fedora, Ubuntu, BSD, macOS, Windows and Intel LLVM are visible in .github/. That is a claim about the project's own CI, verifiable by reading those workflow files, and it is the right thing to check against your own target platform rather than assume.
Editorial conclusion
Adopt Quill when a latency-sensitive C++17 thread cannot afford formatting or file I/O inline, and you are willing to tune queue capacity and memory policy at compile time. Do not adopt it if you need a synchronous logger whose records are on disk by the time the call returns, or if you want a metrics system with its own scrape endpoint rather than a sink. Before committing, verify your toolchain against the supported compilers listed in the documentation, and read the Caveats section for the memory policy and queue behaviour that apply to your workload.
Frequently asked questions
What C++ standard does Quill require?
Quill is an asynchronous logging and metrics library for C++17 and later. The README labels it C++17 and the repository topics also list cpp20.
How do I install Quill in my project?
The repository ships CMakeLists.txt, cmake/, meson.build, BUILD.bazel and MODULE.bazel, so CMake, Meson and Bazel are all supported build paths. The README's Quick Start links to an Installation subsection for the details.
Does Quill support metrics as well as logging?
Yes. Metrics are published through the same asynchronous backend, and the bundled Prometheus sink handles common metric types. Custom sinks can route samples to StatsD, OpenTelemetry or other collectors.
What licence does Quill use?
MIT, per the LICENSE file and the licence badge in the README. That permits use in closed-source products as long as the copyright and permission notices are preserved.
Which platforms does Quill support?
The README states the library is continuously tested across Linux, macOS, Windows and BSD, with sanitizers and fuzzing. Workflow files for Fedora, Ubuntu, BSD, macOS, Windows and Intel LLVM are present in .github/.
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/odygrd-quill)