https://github.com/cjappl/rtlog-cpp
Real-time safe logger for C++ using lock free queues!
https://github.com/cjappl/rtlog-cpp
Last synced: 4 months ago
JSON representation
Real-time safe logger for C++ using lock free queues!
- Host: GitHub
- URL: https://github.com/cjappl/rtlog-cpp
- Owner: cjappl
- License: other
- Created: 2023-04-15T18:49:37.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-01-13T10:03:05.000Z (6 months ago)
- Last Synced: 2025-03-18T21:09:10.577Z (4 months ago)
- Language: C++
- Size: 422 KB
- Stars: 25
- Watchers: 3
- Forks: 5
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# rtlog-cpp 🔊
`rtlog-cpp` is a logging library designed specifically for logging messages from the real-time thread. This is particularly useful in the audio and embedded industries, where hard real-time requirements must be met, and logging in traditional ways from your real-time threads is unacceptable.
If you're looking for a general use logger, this probably isn't the library for you!
The design behind this logger was presented at ADCx 2023.
[](https://www.youtube.com/watch?v=4KFFMGTQIFM)
Slides:
## Features
- Ability to log messages of any type and size from the real-time thread
- Statically allocated memory at compile time, no allocations in the real-time thread
- Support for printf-style format specifiers (using [a version of the printf family](https://github.com/nothings/stb/blob/master/stb_sprintf.h) that doesn't hit the `localeconv` lock) OR support for modern libfmt formatting.
- Efficient thread-safe logging using a [lock free queue](https://github.com/hogliux/farbot).## Requirements
- A C++17 compatible compiler
- The C++17 standard library
- farbot::fifo (will be downloaded via cmake if not provided)
- stb's vsnprintf (will be downloaded via cmake if not provided) OR libfmt if cmake is run with the `RTSAN_USE_FMTLIB` option## Installation via CMake
In CMakeLists.txt
```cmake
include(FetchContent)
FetchContent_Declare(rtlog-cpp
GIT_REPOSITORY https://github.com/cjappl/rtlog-cpp
)
FetchContent_MakeAvailable(rtlog-cpp)add_executable(audioapp ${SOURCES})
target_link_libraries(audioapp
PRIVATE
rtlog::rtlog
)
```To use formatlib, set the variable, either on the command line or in cmake:
```bash
cmake .. -DRTLOG_USE_FMTLIB=ON
```## Usage
For more fleshed out fully running examples check out `examples/` and `test/`
After including via cmake:
1. Include the `rtlog/rtlog.h` header file in your source code
2. Create a `rtlog::Logger` object with the desired template parameters:
3. Process the log messages on your own thread, or via the provided `rtlog::LogProcessingThread````c++
#includestruct ExampleLogData
{
ExampleLogLevel level;
ExampleLogRegion region;
};constexpr auto MAX_LOG_MESSAGE_LENGTH = 256;
constexpr auto MAX_NUM_LOG_MESSAGES = 100;std::atomic gSequenceNumber{0};
using RealtimeLogger = rtlog::Logger;
...
RealtimeLogger logger;
void SomeRealtimeCallback()
{
logger.Log({ExampleLogLevel::Debug, ExampleLogRegion::Audio}, "Hello, world! %i", 42);// using RTSAN_USE_LIBFMT
logger.Log({ExampleLogData::Debug, ExampleLogRegion::Audio, FMT_STRING("Hello, world! {}", 42);
}...
```
To process the logs in another thread, call `PrintAndClearLogQueue` with a function to call on the output data.
```c++
static auto PrintMessage = [](const ExampleLogData& data, size_t sequenceNumber, const char* fstring, ...) __attribute__ ((format (printf, 4, 5)))
{
std::array buffer;
va_list args;
va_start(args, fstring);
vsnprintf(buffer.data(), buffer.size(), fstring, args);
va_end(args);printf("{%lu} [%s] (%s): %s\n",
sequenceNumber,
rtlog::test::to_string(data.level),
rtlog::test::to_string(data.region),
buffer.data());
};...
void LogProcessorThreadMain()
{
while (running)
{
if (logger.PrintAndClearLogQueue(PrintMessage) == 0)
std::this_thread::sleep_for(std::chrono::milliseconds(10);
}
}```
Or alternatively spin up a `rtlog::LogProcessingThread`
```c++
rtlog::LogProcessingThread thread(logger, PrintMessage, std::chrono::milliseconds(10));
```## Customizing the queue type
rtlog provides two queue type variants: `rtlog::SingleRealtimeWriterQueueType` (SPSC - default) and `rtlog::MultiRealtimeWriterQueueType` (MPSC). It is always assummed that you have one log printing thread. These may be used by specifying them:
```cpp
using SingleWriterRtLoggerType = rtlog::Logger;SingleWriterRtLoggerType logger;
logger.Log({ExampleLogLevel::Debug, ExampleLogRegion::Audio}, "Hello, world! %i", 42);...
using MultiWriterRtLoggerType = rtlog::Logger;
MultiWriterRtLoggerType logger;
logger.Log({ExampleLogLevel::Debug, ExampleLogRegion::Audio}, "Hello, world! %i", 42);
```If you don't want to use either of these defaults, you may provide your own queue.
** IT IS UP TO YOU TO ENSURE THE QUEUE YOU PROVIDE IS LOCK-FREE AND REAL-TIME SAFE **
The queue must have the following:
```c++
template
class MyQueue
{
public:
using value_type = T;MyQueue(int capacity);
bool try_dequeue(T& item); // MUST return false if the queue is emptybool try_enqueue(T&& item);
// OR
bool try_enqueue(const T& item);
};
```Then, when creating the logger, provide the queue type as a template parameter:
```c++
using RealtimeLogger = rtlog::Logger;
```You can see an example of wrapping a known rt-safe queue in `examples/custom_queue_example`.