Logfault
One C++ log library to rule them all! MacOS, IOS, Android, Linux, Windows, QT, std::clog
Install / Use
/learn @jgaa/LogfaultREADME
logfault
Simple to use, header only C++ library for application-logging on all major platforms.
No dependencies, except the standard-library for C++11 and platform dependent logging libraries, like log for Android.
Why another C++ logging library?
Simply because I am tired of using different log methods on different platforms. Most of the C++ code I write is highly portable, and it makes sense to add logging in a convenient manner. For me that is to send the log to a std::ostream like device. Logging should be as easy as writing to std::cout!
Logfault can write log-events to traditional log-files, but it's also capable of using the native logging facility for the target platform for the target application. That means that you can write your C++ library, and then let the consumer of the library configure logging for the platform they build it for.
Logfault is not meant as a replacement of a sophisticated logger for a large application. It's more like a hack to get logging right, in libraries, smaller applications and mobile apps written in C++. It was written in a few hours when I desperately needed to get log-output from the C++ library for Android and IOS mentioned above. In the following days I have spent a few extra hours to make it a little more mature, and hopefully useful for other developers as well.
What are the benefits of logfault?
- Header only library.
- Very, very easy to use:
LFLOG_DEBUG << "We are entering foo foo: " << 1 << 2 << 3; - Designed to make a tiny binary footprint; ideal for mobile and IoT.
- Ideal for X-platform apps and libraries; logs to files, syslog, systemd, IOS/macOS native log, Android's
__android_log_write(), QT log macros and the Windows EventLog. - Lazy evaluation. Log statements are not evaluated unless they will be logged (filtered by log-level)
- Compile time filter for lowest enabled log-level. Can be used to totally remove TRACE level evaluation from release builds.
- Flexible time-stamps, easy to use local-time or UTC.
- Can log to several log-targets at different log-levels.
- Written by someone who has worked extensively with logging for decades (from tiny libraries and applications, to owning the log/event libraries in a two digits multi million line C++ application from one of the largest software vendors in the world).
- Flushes the output buffer after write so the latest log message is always visible. (Can be disabled to improve performance).
When should you not use logfault?
- In applications and servers that normally logs lots of information. Logfault is optimized for moderate log-volumes and occasional debugging session with extensive logging. The reason is that it use std::stream's which are relatively slow compared to raw buffer-based IO.
Built-in handlers
-
StreamHandler Logs messages to any C++ output stream (
std::ostream), such asstd::coutor a file stream. -
StreamBufferHandler (C++20+) Writes log messages directly to a stream buffer (low-level efficiency, useful with file descriptors).
-
FileIOHandler (requires
LOGFAULT_ENABLE_POSIX_WRITE) Logs directly to a POSIX file descriptor usingwrite(). Very efficient for Unix-like systems. -
JsonHandler Outputs structured logs in JSON format. Useful for machine parsing and log aggregation tools.
-
ProxyHandler Allows plugging in a custom callback function to handle log messages however you like.
-
SystemdHandler Sends logs to the
systemdjournal (Linux). -
SyslogHandler Sends logs to the system
syslogservice (Unix/Linux). -
AndroidHandler Logs messages to the Android system log (
logcat). -
QtHandler Routes log messages through Qt’s logging system (
qDebug(), etc.). -
OsLogHandler Logs messages to Apple's newer
os_loglogger (macOS/iOS). -
CocoaHandler Sends logs to Apple’s legacy Cocoa logging system (macOS/iOS).
-
WindowsEventLogHandler Logs to the Windows Event Log.
Note that it's very simple to write your own handler and plug it in.
Logging
When you log messages, you stream data into a temporary std::ostream object. So everything that goes into a std::ostream instance can be logged. I often find myself writing custom std::ostream operators to log things like enum names and internal data structures or object identifiers.
Logfault has two types of log macros. You have normal log macros, that are used like this:
LFLOG_ERROR << "Some error occurred: " << errno;
LFLOG_DEBUG << "We are entering foo foo";
These macros expand to something like:
if (log_event_log_level is within current_log_level_range
and we_are_indeed_logging) {
logstream << args ...;
}
In other words, the streaming arguments will be ignored (and function arguments not called) unless we will actually log the line. If the log-level is set to NOTICE, all DEBUG and TRACE messages will be totally ignored and not consume any CPU. The only CPU consumed for such log statements is the check to see if the log statements are relevant. Even these can be removed if you use compile time filtering.
The full set of log-macros
LFLOG_ERRORErrorsLFLOG_WARNWarningsLFLOG_NOTICENotable eventsLFLOG_INFOInformation about what's going onLFLOG_DEBUGDebug messagesLFLOG_TRACETrace messages. These may give very detailed information about what's going on
Configure log targets and log levels
A log target is somewhere to deliver the log events, like a file on disk, or a log application like syslog.
Logfault use instances of log handlers to configure the targets.
Log targets can be configured in C++ code, as part of the initialization of your library, or application, or the log setup can be wrapped to the target platform's language(s).
It's simple to create initialization functions for other languages.
Log to stdout or file
If you just want to log to standard output:
#include "logfault/logfault.h"
using namespace std;
int main() {
// Set up a log-handler to stdout
logfault::LogManager::Instance().AddHandler(make_unique<logfault::StreamHandler>(clog, logfault::LogLevel::TRACE));
LFLOG_DEBUG << "Logging to std::clog is enabled at DEBUG level";
}
You can of course replace clog with any std::ostream, for example an open file to write to.
Linux, Unix, syslog
If you want to log to the syslog under Linux or Unix, just set up logging like this:
// Enable syslog
#define LOGFAULT_USE_SYSLOG
#include "logfault/logfault.h"
using namespace std;
int main() {
// Set up a log-handler to syslog
logfault::LogManager::Instance().AddHandler(make_unique<SyslogHandler>(logfault::LogLevel::DEBUGGING));
LFLOG_DEBUG << "Logging to syslog is enabled at DEBUG level";
}
Linux, systemd
If you want to log directly to the systemd log.
Note that the SystemdHandler requires at least C++17.
#define LOGFAULT_WITH_SYSTEMD
#include "logfault/logfault.h"
int main() {
// Set up a log-handler to systemd
logfault::SystemdHandler::Options opt;
opt.ident = "my-app-name";
logfault::LogManager::Instance().AddHandler(make_unique<logfault::SystemdHandler>(logfault::LogLevel::INFO));
LFLOG_INFO << "Hello to journalctl";
}
Log via Apple’s unified logging (os_log)
On macOS, iOS, tvOS and watchOS you can use Apple’s modern unified logging system via the OsLogHandler.
This writes to the system log visible in Console.app or with the log command.
#define LOGFAULT_USE_OS_LOG
#include "logfault/logfault.h"
int main() {
// Set up a log-handler to os_log
logfault::LogManager::Instance().AddHandler(std::make_unique<logfault::OsLogHandler>(
"oslog",
logfault::LogLevel::DEBUGGING,
logfault::OsLogHandler::Options{"com.example.app", "network"}));
LFLOG_INFO << "Hello from Apple's os_log unified logging system";
}
Viewing logs
-
Live stream:
log stream --style syslog --info --debug \ --predicate 'subsystem == "com.example.app" && category == "network"' -
Console.app: Filter by subsystem =
com.example.appand category =network.
Privacy
By default, logfault marks the whole message as public (%{public}s) in debug builds and private in release builds.
If you want it differently, set Options.public_output to your match your preference.
Windows EventLog
The library can send log-events to the Windows EventLog under Windows.
If you want to do it properly, you need to create a message template file, compile it, include it in the Visual Studio project, and then add it in the registry on the computers that will run the application. For obvious reasons, most applications don't do that, and the events are polluted by the message:
The description for Event ID 0 from source general_tests cannot be found. Either the component that raises this event is not installed on your local computer or the installation is corrupted. You can install or repair the component on the local computer.
If the event originated on another computer, the display information had to be saved with the event.
The following information was included with the event:
This is fine. Even large Windows application vendors ignore this inconvenience in their logging. Blame Microsoft for making it very hard to support the EventLog in 3rd party applications.
Example of application logging to the Windows EventLog:
#define LOGFAULT_USE_WINDOWS_EVENTLOG
#include "logfault/logfault.h"
int main( int argc, char *argv[]) {
std::unique_ptr<logfault::Handler> eventhandler{new logfault::WindowsEventLogHandler("example", logfault::LogLevel::DEBUGGING)};
logfault::LogManager:
