https://github.com/pug523/femtolog
A fast C++ logger
https://github.com/pug523/femtolog
async asynchronous cpp cpp20 cross-platform femtolog fmtlib high-performance log log-library logger logging low-latency low-level
Last synced: 2 months ago
JSON representation
A fast C++ logger
- Host: GitHub
- URL: https://github.com/pug523/femtolog
- Owner: pug523
- License: apache-2.0
- Created: 2025-06-25T16:15:39.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2026-03-27T09:25:41.000Z (3 months ago)
- Last Synced: 2026-03-27T19:33:23.963Z (3 months ago)
- Topics: async, asynchronous, cpp, cpp20, cross-platform, femtolog, fmtlib, high-performance, log, log-library, logger, logging, low-latency, low-level
- Language: C++
- Homepage:
- Size: 2.66 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
femtolog
[](https://github.com/pugur523/femtolog/actions/workflows/ci.yml)
[](https://github.com/pugur523/femtolog/issues)
[](LICENSE)
[](https://isocpp.org/)
[](https://xmake.io)
> Ultra Light High Performance Asynchronous Logger
## Overview
**femtolog** is a blazing-fast, minimal-overhead asynchronous logging library built for performance-critical applications. It leverages zero-cost abstractions, cache-aligned SPSC queues, and compile-time format string serialization.
Designed for modern C++ projects where every nanosecond counts.
---
## Table of Contents
- [Overview](#-overview)
- [Table of Contents](#-table-of-contents)
- [Features](#-features)
- [Usage](#-usage)
- [Workflow](#-workflow)
- [Benchmarks](#-benchmarks)
- [System Environment](#system-environment)
- [Literal without format](#literal-without-format)
- [Format integer](#format-integer)
- [Format multi integers](#format-multi-integers)
- [Format string](#format-string)
- [Format string view](#format-string-view)
- [Format mixed](#format-mixed)
- [Format large string](#format-large-string)
- [Installation](#-installation)
- [Using CMake](#using-cmake)
- [Custom Sinks](#-custom-sinks)
- [Implement Your Own Sink](#-implement-your-own-sink)
- [License](#-license)
- [Credits](#️-credits)
## Features
- Compile-time format string registration
- True asynchronous logging pipeline
- Zero dynamic memory allocations on the frontend
- Dedicated backend worker thread for formatting and output
- Faster than `spdlog`, and `quill` in benchmark
---
## Usage
`femtolog` supports formatting messages using [fmtlib](https://github.com/fmtlib/fmt).
### Basic Example
```cpp
#include "femtolog.h"
using namespace femtolog;
int main() {
// Initialize the logger
Logger& logger = Logger::logger();
logger.init();
logger.register_sink>();
logger.start_worker();
// Output "Hello World" to console
logger.info<"Hello World\n">();
logger.stop_worker();
logger.clear_sinks();
return 0;
}
```
### Advanced Example
```cpp
#include "femtolog.h"
int main() {
// Get thread local logger instance.
femtolog::Logger& logger = femtolog::Logger::logger();
// Initialize logger and register log sink.
femtolog::FemtologOptions options = {
.spsc_queue_size = 1024 * 1024 * 4,
.backend_format_buffer_size = 1024 * 64,
.backend_dequeue_buffer_size = 1024 * 64,
.backend_worker_cpu_affinity = 5,
.color_mode = femtolog::ColorMode::kAuto,
};
logger.init(options);
logger.register_sink>();
logger.register_sink();
logger.register_sink>();
logger.level("trace");
// Start the backend worker that dequeues logged entries.
logger.start_worker();
std::string username = "pugur";
float cpu_usage = 42.57f;
bool result = true;
int error_code = -1;
// Log messages with compile-time interpreted format strings.
logger.trace<"Hello {}\n">("World");
logger.debug<"Hello World wo formatting\n">();
logger.info<"User \"{}\" logged in.\n">(username);
logger.warn<"CPU usage is high: {}%\n">(cpu_usage);
logger.error<"Return value is: {}\n">(result);
logger.fatal<"Fatal error occured; error code: {}\n">(error_code);
logger.stop_worker();
logger.clear_sinks();
return 0;
}
```
## Workflow
The logging pipeline consists of a frontend (thread-local logger) and a backend (worker thread):
Format string literals (`"..."`) are hashed at compile time into a `format_id`. Arguments are serialized as a raw byte stream and passed asynchronously from the frontend to the backend through an SPSC queue.
```mermaid
graph TD
A[Logger - frontend] -->|embed format id by hash at compile time, serialize format args at runtime| B[SPSC Queue]
B --> C[Backend Worker - async]
C --> D[Deserialize and Format]
D --> E[Sink: stdout / files / custom]
```
This architecture separates formatting from the hot path of logging, minimizing latency.
## Benchmarks
### System Environment
- **OS**: Ubuntu 22.04 x86_64
- **CPU**: Intel Core i3 12100 @ 4.3GHz
- **RAM**: DDR4 3600MHz 64GB
The following benchmark results were measured using [Google Benchmark](https://github.com/google/benchmark) with Clang-20 -O3 Release build on the above environment. For comparison, results of similar logging benchmarks using the following libraries are also shown:
- [**quill**](https://github.com/odygrd/quill)
- [**spdlog**](https://github.com/gabime/spdlog)
The benchmark codes are available in [`//src/bench/`](src/bench/) directory and the detail results of benchmark are archived in [`//src/bench/results/archive`](src/bench/results/archive/) directory.
Benchmarks are generated in the `//out/build///bin` directory by setting `FEMTOLOG_BUILD_BENCHMARK` to `true` when building with CMake.
After building, you can run the built benchmarks and collect results by executing `//src/build/scripts/run_bench.py` with the `--format` option. The results will be saved in the `//src/bench/results/` directory in both json and png formats.
#### Literal without format
| Library | 50%[ns] | 75%[ns] | 90%[ns] | 95%[ns] | 99%[ns] | 99.9%[ns] |
| :----------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-------: |
| **femtolog** | 3.512 | 3.516 | 3.624 | 3.660 | 3.689 | 3.695 |
| quill | 22.873 | 23.911 | 25.149 | 25.562 | 25.892 | 25.967 |
| spdlog | 29.930 | 29.981 | 31.014 | 31.358 | 31.634 | 31.696 |
#### Format integer
| Library | 50%[ns] | 75%[ns] | 90%[ns] | 95%[ns] | 99%[ns] | 99.9%[ns] |
| :----------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-------: |
| **femtolog** | 10.806 | 11.309 | 11.344 | 11.356 | 11.365 | 11.367 |
| quill | 23.872 | 23.934 | 24.552 | 24.758 | 24.923 | 24.960 |
| spdlog | 46.295 | 46.610 | 48.806 | 49.538 | 50.124 | 50.256 |
#### Format multi integers
| Library | 50%[ns] | 75%[ns] | 90%[ns] | 95%[ns] | 99%[ns] | 99.9%[ns] |
| :----------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-------: |
| **femtolog** | 11.297 | 13.048 | 13.092 | 13.107 | 13.119 | 13.122 |
| quill | 24.175 | 24.310 | 24.848 | 25.027 | 25.171 | 25.203 |
| spdlog | 65.505 | 67.241 | 69.786 | 70.634 | 71.313 | 71.466 |
#### Format string
| Library | 50%[ns] | 75%[ns] | 90%[ns] | 95%[ns] | 99%[ns] | 99.9%[ns] |
| :----------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-------: |
| **femtolog** | 13.591 | 14.059 | 14.387 | 14.496 | 14.583 | 14.603 |
| quill | 23.571 | 24.868 | 24.938 | 24.961 | 24.980 | 24.984 |
| spdlog | 49.125 | 50.731 | 50.755 | 50.763 | 50.769 | 50.771 |
#### Format string view
| Library | 50%[ns] | 75%[ns] | 90%[ns] | 95%[ns] | 99%[ns] | 99.9%[ns] |
| :----------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-------: |
| **femtolog** | 11.867 | 11.928 | 12.045 | 12.085 | 12.116 | 12.123 |
| quill | 24.805 | 24.840 | 24.951 | 24.988 | 25.018 | 25.024 |
| spdlog | 46.054 | 49.631 | 54.423 | 56.020 | 57.297 | 57.585 |
#### Format mixed
| Library | 50%[ns] | 75%[ns] | 90%[ns] | 95%[ns] | 99%[ns] | 99.9%[ns] |
| :----------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-------: |
| **femtolog** | 11.791 | 12.116 | 12.299 | 12.360 | 12.409 | 12.420 |
| quill | 23.330 | 26.236 | 26.258 | 26.266 | 26.272 | 26.273 |
| spdlog | 111.116 | 115.427 | 117.489 | 118.176 | 118.726 | 118.850 |
#### Format large string
| Library | 50%[ns] | 75%[ns] | 90%[ns] | 95%[ns] | 99%[ns] | 99.9%[ns] |
| :----------: | :-----: | :-----: | :-----: | :-----: | :-----: | :-------: |
| **femtolog** | 12.107 | 12.393 | 12.845 | 12.996 | 13.117 | 13.144 |
| quill | 22.043 | 22.193 | 22.893 | 23.126 | 23.313 | 23.355 |
| spdlog | 51.425 | 53.556 | 55.591 | 56.269 | 56.812 | 56.934 |
## Installation
### Using CMake
Add this repository as a git submodule:
```bash
git submodule add https://github.com/pugur523/femtolog.git ./femtolog --recursive
```
Add `femtolog` as a subdirectory:
```cmake
add_subdirectory(femtolog)
target_link_libraries(your_target PRIVATE femtolog)
```
To install the compiled library:
```cmake
set(INSTALL_FEMTOLOG TRUE)
set(FEMTOLOG_INSTALL_HEADERS TRUE)
add_subdirectory(femtolog)
target_link_libraries(your_target PRIVATE femtolog)
```
## Custom Sinks
Need to log to a database, a network socket, or a ring buffer?
`femtolog` supports plug-and-play custom sinks via a simple interface:
### Implement Your Own Sink
To define a custom sink, just inherit from `SinkBase` and implement `on_log()`:
```cpp
#include "sinks/sink_base.h"
class MySink : public femtolog::SinkBase {
public:
void on_log(const LogEntry& entry, const char* content, size_t len) override {
// Write to file, send over network, etc.
std::fwrite(content, 1, len, stderr);
}
};
```
Then register your sink with the logger:
```cpp
logger.register_sink();
```
That's it — your sink will now receive fully formatted log entries, asynchronously, from the backend.
## License
`femtolog` is licensed under the [Apache 2.0 License](LICENSE).
## Credits
- **[zlib](https://github.com/madler/zlib)**
Used in `FileSink` and `JsonLinesSink` for compressing log files efficiently.
- **[GoogleTest (gtest)](https://github.com/google/googletest)**
Serves as the primary unit testing framework for the entire project.
- **[Google Benchmark](https://github.com/google/benchmark)**
Used to benchmark `femtolog` against other logging libraries to ensure high performance.
- **[fmtlib](https://github.com/fmtlib/fmt)**
Powers the formatting engine behind all log message rendering.