Serenity is a C++20 logging framework library that takes inspiration from spdlog for its user customizable aspect in logging and fmtlib for its highly flexible and efficient formatting of arguments. Serenity includes a native formatter in the form of ArgFormatter, which is a fully compliant but limited scope, drop-in replacement for the standard's <format> and Victor's fmtlib that is used for its relatively faster formatting times at the cost of some robustness that the other two provide. As a framework library, the backend formatter can be swapped between any three of these without any issues.
What sets Serenity apart is that this library offers very fast single threaded log calls alongside Unicode support via UTF-Utils, which allows the input paths for files and any string-type argument provided to a log call to be automatically decoded and encoded so that you don't have to worry so much about the headache of text encodings and can just log what you need to log. What this also means is that, by default, any derived extension to log targets that you may create will inherently achieve this as well. The internal log calls also use this functionality, so writing logs to char-type based containers from your derived class just got a whole lot easier on top of having stress-free log calls.
std::remove_cvref_t<>
<concepts>
header<format>
header if the build option "USE_STDFMT" is used
<source_location>
headerstd::jthread
std::stop_token
std::stop_source
ArgFormatter
is the default backend and uses the build option
"USE_NATIVEFMT"
fmtlib
which uses the build option "USE_FMTLIB" and defaults to version 9.1.0
<format>
which uses the build option "USE_STDFMT"
Utf-Utils
- this project is already included and built alongside
Serenity by default.
To start using any of the logging targets, simply `#include` any of
the targets found under `serenity/Targets`.
The available targets for out-of-the-box usage are:
There are three total constructors for `FileTarget`:
1.) explicit FileTarget(std::string_view fileName, bool replaceIfExists = false); 2.) explicit FileTarget(std::string_view name, std::string_view filePath, bool replaceIfExists = false); 3.) explicit FileTarget(std::string_view name, std::string_view formatPattern, std::string_view filePath, bool replaceIfExists = false);
There are three total constructors for `RotatingTarget`:
1.) explicit RotatingTarget(); 2.) explicit RotatingTarget(std::string_view name, std::string_view filePath, bool replaceIfExists = false); 3.) explicit RotatingTarget(std::string_view name, std::string_view formatPattern, std::string_view filePath, bool replaceIfExists = false);
There are three total constructors for `ConsoleTarget`:
1.) ConsoleTarget(); 2.) explicit ConsoleTarget(std::string_view name ); 3.) explicit ConsoleTarget(std::string_view name, std::string_view msgPattern );
Level | Color |
---|---|
Trace | Bright White |
Info | Bright Green |
Debug | Bright Cyan |
Warning | Bright Yellow |
Error | Basic Red |
Fatal | Bright Yellow On Red |
#include <serenity/Color/Color.h> namespace color = serenity::se_colors::bright_colors; namespace target = serenity::targets; // Create the console target with default format pattern and user-defined name target::ConsoleTarget example("Example_Logger"); // Set the message level to any ansi-supported code (using helper header here) example.SetMsgColor(LoggerLevel::trace, color::foreground::grey);
Flag | Flag Function | Flag Modifiers | Example |
---|---|---|---|
%l | Prints the message level as a short string representation | None | Trace = 'T', Info = 'I'
Debug = 'D', Warning = 'W' Error 'E', Fatal = 'F' |
%L | Prints the message level as a long string representation | None | Trace = 'Trace', Info = 'Info'
Debug = 'Debug', Warning = 'Warn' Error 'Error', Fatal = 'Fatal' |
%s | Formats the source location of the log site | ' : ' and any combination of 'a', 'c', 'f', 'l', 'F' |
%s:c =>
output the column number of the log site
%s:f => output the file of where the log site is %s:l => output the line number of the log site %s:F => output the function name of where the log site is called %s:a => format source location containing all fields %s => same as %s:a |
%t | Formats the current thread ID of the logger | ' : ' and any digit between 0-10 default length is 10 digits |
If thread ID was 123456789123456789:
%t:6 => 123456 |
%N | Prints the Logger's Name | None | If logger's name was 'Critical_Logger', prints 'Critical_Logger' |
%+ | Prints the actual log message itself | None |
If you called:
someLogger.Trace("This Is A Log Message With Value: {}", 42); Then this flag would output: "This Is A Log Message With Value: 42" |
%T | Prints the 24 hour time format as HH:MM:SS | ' : ' and any digits for sub-second precision of time |
If the time was 6:30 PM and the flag used was
'%T:3' then a possible output might be: 18:30:24.345 |
Once you decide what logger(s) you would like to use, all that's left to do to start using them is to call their respective log functions:
template <typename... Args> void Trace(MsgWithLoc formatString, Args&&... args); template <typename... Args> void Info(MsgWithLoc formatString, Args&&... args); template <typename... Args> void Debug(MsgWithLoc formatString, Args&&... args); template <typename... Args> void Warn(MsgWithLoc formatString, Args&&... args); template <typename... Args> void Error(MsgWithLoc formatString, Args&&... args); template <typename... Args> void Fatal(MsgWithLoc formatString, Args&&... args);
Where the format string is your message to log. The loggers use the active backend formatter to format any log message with the arguments provided so long as there are substitution brackets present in the form of "{}". If unfamiliar with what the message formatting grammar is, I would highly recommend checking out the grammar specification outlined HERE. "ArgFormatter" and "<format>" are modeled after "fmtlib" and thus use Victor's grammar spec.
#include <serenity/Targets/FileTarget.h> using namespace serenity::targets; FileTarget basicFileLogger("My_File_Logger", "[%L] %c [%N]: %+", "Sandbox/Logs/My_Log.txt", true); basicFileLogger.Trace("This is how {0} log message might look with an integer formatted to hex: [{1:X}]", "a simple", 42);
[Long_Logger_Level] ddd mmm dd HH:MM:SS YYYY [My_File_Logger]: Log_Message + Platform_Line_Ending
[Info] Mon Nov 28 20:35:32 2022 [My_File_Logger]: This is how a simple log message might look with an integer formatted to hex: [0X2A] \r\n
Logging Sink/Target | Logging Speed | Logging Throughput |
---|---|---|
Serenity Console Logger | 9.36 ns | 40738.34 MB/s |
Spdlog Console Sink | 12.47 ns | 30589.40 MB/s |
Serenity File Target | 350.15 ns | 1089.42 MB/s |
Spdlog Basic File Sink | 1146.80 ns | 332.64 MB/s |
Serenity Rotating Target | 1397.58 ns | 272.95 MB/s |
Spdlog Rotating Sink | 2852.14 ns | 133.75 MB/s |
Logging Sink/Target | Logging Speed | Logging Throughput |
---|---|---|
Serenity Console Logger | 9.47 ns | 40263.85 MB/s |
Spdlog Console Sink | 12.74 ns | 29931.17 MB/s |
Serenity File Target | 1617.41 ns | 235.85 MB/s |
Spdlog Basic File Sink | 1145.90 ns | 332.90 MB/s |
Serenity Rotating Target | 2205.76 ns | 172.94 MB/s |
Spdlog Rotating Sink | 2677.81 ns | 142.45 MB/s |
Logging Sink/Target | Logging Speed | Logging Throughput |
---|---|---|
Serenity Console Logger | 8.51 ns | 44806.05 MB/s |
Spdlog Console Sink | 12.74 ns | 29951.26 MB/ |
Serenity File Target | 2315.97 ns | 164.71 MB/ |
Spdlog Basic File Sink | 1027.15 ns | 371.39 MB/ |
Serenity Rotating Target | 2857.12 ns | 133.52 MB/s |
Spdlog Rotating Sink | 2654.13 n | 143.73 MB/s |