G3log
Asynchronous and crash-safe C++ logging library. Design emphasizing high performance and system stability. Characterized by functionality preventing log loss during crashes and minimizing impact on applications through asynchronous processing.
Library
G3log
Overview
G3log is an asynchronous, crash-safe logger designed for C++ applications. Implemented in pure C++14 (with C++11 support up to release 1.3.2), it has no external dependencies and runs on OSX, Windows, and multiple Linux distributions. In addition to providing default logging sinks, G3log allows easy addition of custom sinks and ensures log safety even when programs crash, making it ideal for enterprise-level applications requiring robust logging capabilities.
Details
G3log is a performance-focused and safety-oriented C++ logging library that continues active development as of 2025. Its key features include true asynchronous processing for high performance and crash-safe functionality that ensures logs are written even during application crashes. It provides comprehensive modern C++ development features including LogWorker-based dedicated thread log processing, flexible output destination control via custom sinks, support for both streaming and printf syntax, CHECK functionality for design-by-contract, and automatic stack dumps during fatal signals.
Key Features
- Asynchronous Log Processing: High-performance log processing via LogWorker dedicated threads
- Crash-Safe: Ensures log data is reliably written even during abnormal application termination
- Custom Sink Support: Flexible output control to files, console, network, and other destinations
- Multiple Syntax Support: Support for both streaming (LOG) and printf-style (LOGF) syntax
- Conditional Logging: Efficient conditional logging with LOG_IF, LOG_EVERY_N, and other macros
- Design by Contract: Assertion functionality with CHECK, CHECK_F, and related macros
Advantages and Disadvantages
Advantages
- True asynchronous processing minimizes performance impact on main threads
- Crash-safe functionality reliably preserves critical debugging information
- Easy integration with no external dependencies and standard CMake build system
- Flexible output destination control and extensibility through custom sinks
- Automatic stack dumps during fatal errors improve debugging efficiency
- Rich conditional logging features enable efficient log control
Disadvantages
- Requires C++14 or later, incompatible with older compiler environments
- Asynchronous processing may cause log output timing to differ from execution timing
- Learning curve exists, especially when implementing custom sinks
- Integration with other language ecosystems (Java, Python) requires additional work
- Log format customization is relatively limited
- Immediate log verification may require attention due to asynchronous processing
Reference Pages
Code Examples
Basic Setup
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <memory>
int main(int argc, char* argv[]) {
// Create LogWorker (manages asynchronous log processing)
auto worker = g3::LogWorker::createLogWorker();
// Add default file logger
const std::string directory = "./logs/";
const std::string name = "app_log";
auto handle = worker->addDefaultLogger(name, directory);
// Initialize logging system
g3::initializeLogging(worker.get());
LOG(INFO) << "G3log initialized successfully";
// Log flushing is automatically handled by worker destructor
// which calls g3::internal::shutDownLogging()
return 0;
}
Basic Log Output
#include <g3log/g3log.hpp>
void basicLogging() {
// Streaming syntax for log output
LOG(INFO) << "Application started successfully";
LOG(WARNING) << "Warning message: config value = " << config_value;
LOG(FATAL) << "Fatal error occurred"; // Process termination
// Printf-style log output
LOGF(INFO, "User %s logged in (ID: %d)", username.c_str(), user_id);
LOGF(WARNING, "Memory usage: %.2f MB", memory_usage_mb);
LOGF(DEBUG, "Debug info: file=%s, line=%d", __FILE__, __LINE__);
// Conditional logging
LOG_IF(INFO, user_count > 100) << "Many users connected: " << user_count;
LOGF_IF(WARNING, memory_usage > 0.8, "High memory usage: %.1f%%", memory_usage * 100);
// Periodic logging (every N times)
LOG_EVERY_N(INFO, 10) << "Every 10th log message: " << google::COUNTER << " times";
// First N times only
LOG_FIRST_N(INFO, 5) << "First 5 times only: " << google::COUNTER << " times";
}
Advanced Configuration (Custom Sinks and Formatting)
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <iostream>
#include <fstream>
// Custom sink implementation example (console output)
struct ColorConsoleSink {
enum Color { RED = 31, GREEN = 32, YELLOW = 33, WHITE = 97 };
Color getColor(const LEVELS& level) const {
if (level.value == WARNING.value) return YELLOW;
if (level.value == DEBUG.value) return GREEN;
if (g3::internal::wasFatal(level)) return RED;
return WHITE;
}
void ReceiveLogMessage(g3::LogMessageMover logEntry) {
auto level = logEntry.get()._level;
auto color = getColor(level);
std::cout << "\033[" << color << "m"
<< logEntry.get().toString()
<< "\033[0m" << std::endl;
}
};
// Custom file output sink
class CustomFileSink {
private:
std::ofstream file_;
public:
CustomFileSink(const std::string& filename) : file_(filename, std::ios::app) {}
void ReceiveLogMessage(g3::LogMessageMover logEntry) {
if (file_.is_open()) {
// Use custom formatting function
file_ << logEntry.get().toString(&CustomFormatting) << std::endl;
file_.flush(); // Immediate write
}
}
// Custom formatting function
static std::string CustomFormatting(const g3::LogMessage& msg) {
std::ostringstream oss;
oss << "[" << msg.timestamp("%Y-%m-%d %H:%M:%S") << "] "
<< "[" << msg.level() << "] "
<< "[Thread:" << msg.threadID() << "] "
<< msg.file() << ":" << msg.line() << " - "
<< msg.message();
return oss.str();
}
};
int main() {
auto worker = g3::LogWorker::createLogWorker();
// Default file logger
auto fileHandle = worker->addDefaultLogger("app", "./logs/");
// Add custom console sink
auto consoleHandle = worker->addSink(
std::make_unique<ColorConsoleSink>(),
&ColorConsoleSink::ReceiveLogMessage
);
// Add custom file sink
auto customFileHandle = worker->addSink(
std::make_unique<CustomFileSink>("./logs/custom.log"),
&CustomFileSink::ReceiveLogMessage
);
g3::initializeLogging(worker.get());
// Test output by log level
LOG(INFO) << "Information message";
LOG(WARNING) << "Warning message";
LOG(DEBUG) << "Debug message";
// Asynchronously call sink methods
std::future<void> future = customFileHandle->call(
&CustomFileSink::SomeCustomMethod, param1, param2
);
return 0;
}
Error Handling and Design by Contract
#include <g3log/g3log.hpp>
void errorHandlingExamples() {
// CHECK macros - terminate program if condition is false
int* ptr = getPointer();
CHECK(ptr != nullptr) << "Pointer is null";
CHECK_NOTNULL(ptr); // Returns ptr (if not null)
// Comparison CHECKs
int expected = 42;
int actual = calculateValue();
CHECK_EQ(expected, actual) << "Expected and actual values differ";
CHECK_NE(status, ERROR_STATE) << "System is in error state";
CHECK_LT(memory_usage, MAX_MEMORY) << "Memory usage exceeds limit";
// String comparison CHECK
const char* config_path = getConfigPath();
CHECK_STREQ(config_path, "/etc/app.conf") << "Invalid config file path";
// Non-fatal CHECK (log only, continue program)
CHECK(validate_data(data)); // Program continues even if false
// Fatal CHECK (terminate program)
CHECK_F(initialize_system(), "System initialization failed");
}
// More practical error handling example
class DatabaseConnection {
private:
void* connection_;
public:
DatabaseConnection(const std::string& conn_string) {
connection_ = connect(conn_string);
CHECK_NOTNULL(connection_) << "Database connection failed: " << conn_string;
LOG(INFO) << "Connected to database: " << conn_string;
}
bool executeQuery(const std::string& query) {
CHECK(!query.empty()) << "Query is empty";
bool result = internal_execute(query);
LOG_IF(WARNING, !result) << "Query execution failed: " << query;
return result;
}
~DatabaseConnection() {
if (connection_) {
disconnect(connection_);
LOG(INFO) << "Database connection closed";
}
}
};
Practical Example (Application Integration)
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <g3sinks/LogRotate.h> // External sink (log rotation)
class ApplicationLogger {
private:
std::unique_ptr<g3::LogWorker> worker_;
std::unique_ptr<g3::SinkHandle<g3::FileSink>> default_handle_;
std::unique_ptr<g3::SinkHandle<LogRotate>> rotate_handle_;
public:
void initialize(const std::string& log_dir, const std::string& app_name) {
worker_ = g3::LogWorker::createLogWorker();
// Default log file
default_handle_ = worker_->addDefaultLogger(app_name, log_dir);
// Log rotation sink
rotate_handle_ = worker_->addSink(
std::make_unique<LogRotate>(app_name, log_dir),
&LogRotate::save
);
// Configure log rotation (10MB per file)
const int k10MB = 10 * 1024 * 1024;
rotate_handle_->call(&LogRotate::setMaxLogSize, k10MB);
// Custom header configuration
std::string header = "=== " + app_name + " Log Started ===\n";
default_handle_->call(&g3::FileSink::overrideLogHeader, header);
// Apply custom formatting
default_handle_->call(&g3::FileSink::overrideLogDetails,
&g3::LogMessage::FullLogDetailsToString);
g3::initializeLogging(worker_.get());
LOG(INFO) << "Logging system initialized";
}
void shutdown() {
if (worker_) {
LOG(INFO) << "Shutting down logging system";
// Automatic shutdown via worker destructor
worker_.reset();
}
}
// Application-specific log methods
void logUserAction(const std::string& user, const std::string& action) {
LOG(INFO) << "USER_ACTION: " << user << " -> " << action;
}
void logPerformanceMetric(const std::string& metric, double value) {
LOGF(INFO, "METRIC: %s = %.3f", metric.c_str(), value);
}
void logError(const std::string& component, const std::string& error) {
LOG(FATAL) << "ERROR in " << component << ": " << error;
}
};
// Usage example
int main(int argc, char* argv[]) {
ApplicationLogger logger;
try {
logger.initialize("./logs", "MyApplication");
// Application processing
logger.logUserAction("user123", "login");
logger.logPerformanceMetric("response_time", 0.125);
// Some processing...
} catch (const std::exception& e) {
LOG(FATAL) << "Unhandled exception: " << e.what();
}
logger.shutdown();
return 0;
}