Apache log4cxx

C++ port of Java log4j by Apache Foundation. Provides API design and configuration methods familiar to Java developers. Traditional logging framework applying concepts like appenders, layouts, and filters to C++.

LoggingC++AppendersLayoutsFilters

Library

Apache log4cxx

Overview

Apache log4cxx is a C++ port of Java log4j developed by the Apache Foundation. It provides API design and configuration methods familiar to Java developers, applying traditional logging framework concepts such as appenders, layouts, and filters to the C++ environment. With enterprise-grade features including thread safety, flexible configuration, and hierarchical logger management, it enables comprehensive log management in C++ applications.

Details

Apache log4cxx 2025 edition has over 15 years of development experience as a mature logging solution that faithfully ports the design philosophy of Java log4j to C++. It supports advanced requirements needed in large-scale C++ systems, including configuration management through XML and properties files, simultaneous log output to multiple destinations, message filtering, and log rotation functionality. With complete support for CMake build system and cross-platform development support, it is designed to minimize learning costs during C++ project migration for teams with Java development experience.

Key Features

  • Java log4j API Compatibility: Adherence to familiar Logger, Appender, and Layout concepts
  • XML Configuration Support: Flexible configuration management through external files
  • Hierarchical Loggers: Logger management system corresponding to package hierarchies
  • Multiplatform Support: Windows, Linux, macOS compatibility
  • Rich Appenders: Support for file, console, and network output
  • Thread Safety: Guaranteed safe operation in multithreaded environments

Pros and Cons

Pros

  • Intuitive and understandable API design for those experienced with Java log4j
  • Easy runtime log level and output destination changes through XML configuration
  • Flexible output customization through rich appenders and layouts
  • Unified log control in large-scale projects through hierarchical logger management
  • Long-term maintenance and community support by Apache Foundation
  • Unified log management approach in Java-C++ mixed environments

Cons

  • Other lightweight libraries tend to be prioritized in modern C++ projects
  • Configuration complexity may result in excessive features for small-scale projects
  • Performance inferior to dedicated C++ libraries like spdlog and Boost.Log
  • Limited maintenance activity with slow adaptation to latest C++ standards
  • Introduction costs due to dependency and build configuration complexity
  • Discomfort when Java-specific concepts don't match C++ conventions

Reference Pages

Code Examples

Installation and Basic Setup

# Install dependencies on Ubuntu/Debian
sudo apt-get update
sudo apt-get install build-essential cmake libapr1-dev libaprutil1-dev

# Build from source
git clone https://github.com/apache/logging-log4cxx.git
cd logging-log4cxx
mkdir build && cd build

# CMake build configuration
cmake .. -DCMAKE_BUILD_TYPE=Release \
         -DCMAKE_INSTALL_PREFIX=/usr/local \
         -DLOG4CXX_ENABLE_TESTS=OFF

# Build and install
make -j$(nproc)
sudo make install

# Check pkg-config
pkg-config --modversion liblog4cxx
pkg-config --cflags --libs liblog4cxx

# Usage example in CMakeLists.txt
find_package(log4cxx REQUIRED)
target_link_libraries(your_target ${LOG4CXX_LIBRARIES})
target_include_directories(your_target PRIVATE ${LOG4CXX_INCLUDE_DIRS})

Basic Logging (Hello World)

#include <log4cxx/logger.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/configurator.h>
#include <log4cxx/initializer.h>

int main()
{
    // Initialize log4cxx
    log4cxx::Initializer initializer;

    // Apply basic configuration
    log4cxx::BasicConfigurator config;
    config.configure();

    // Get logger instance
    log4cxx::Logger logger = log4cxx::Logger::getInstance(
        LOG4CPLUS_TEXT("MyApp"));
    
    // Basic log output
    LOG4CXX_TRACE(logger, "Starting application");
    LOG4CXX_DEBUG(logger, "Debug info: System initialization complete");
    LOG4CXX_INFO(logger, "Info: User login successful");
    LOG4CXX_WARN(logger, "Warning: Memory usage is high");
    LOG4CXX_ERROR(logger, "Error: Database connection failed");
    LOG4CXX_FATAL(logger, "Fatal error: Shutting down system");

    return 0;
}

Log Level Control and Filtering

#include <log4cxx/logger.h>
#include <log4cxx/loglevel.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/configurator.h>
#include <log4cxx/initializer.h>

void printAllLogLevels(log4cxx::Logger const & logger)
{
    LOG4CXX_TRACE(logger, "TRACE message - Most detailed debug info");
    LOG4CXX_DEBUG(logger, "DEBUG message - Debug information");
    LOG4CXX_INFO(logger, "INFO message - General information");
    LOG4CXX_WARN(logger, "WARN message - Warning");
    LOG4CXX_ERROR(logger, "ERROR message - Error");
    LOG4CXX_FATAL(logger, "FATAL message - Fatal error");
}

void testLogLevel(log4cxx::LogLevel level, const std::string& levelName)
{
    log4cxx::Logger logger = log4cxx::Logger::getInstance(
        LOG4CPLUS_TEXT("TestLogger"));

    // Set log level
    logger.setLogLevel(level);

    std::cout << "=== Test with " << levelName << " level ===" << std::endl;
    printAllLogLevels(logger);
    std::cout << std::endl;
}

int main()
{
    log4cxx::Initializer initializer;
    log4cxx::BasicConfigurator config;
    config.configure();

    // Test with each log level
    testLogLevel(log4cxx::TRACE_LOG_LEVEL, "TRACE");
    testLogLevel(log4cxx::DEBUG_LOG_LEVEL, "DEBUG");
    testLogLevel(log4cxx::INFO_LOG_LEVEL, "INFO");
    testLogLevel(log4cxx::WARN_LOG_LEVEL, "WARN");
    testLogLevel(log4cxx::ERROR_LOG_LEVEL, "ERROR");
    testLogLevel(log4cxx::FATAL_LOG_LEVEL, "FATAL");

    // Example of conditional log execution
    log4cxx::Logger mainLogger = log4cxx::Logger::getInstance(
        LOG4CPLUS_TEXT("MainApp"));
    
    if (mainLogger.isDebugEnabled()) {
        // Heavy processing executed only when DEBUG level is enabled
        std::string expensiveDebugInfo = generateExpensiveDebugInfo();
        LOG4CXX_DEBUG(mainLogger, "Detailed debug info: " << expensiveDebugInfo);
    }

    return 0;
}

std::string generateExpensiveDebugInfo() {
    // Simulate time-consuming debug info generation
    std::stringstream ss;
    for (int i = 0; i < 1000; ++i) {
        ss << "Item " << i << " ";
    }
    return ss.str();
}

Using XML Configuration Files

// main.cpp
#include <log4cxx/logger.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/initializer.h>

int main()
{
    log4cxx::Initializer initializer;

    // Load XML configuration file
    log4cxx::xml::DOMConfigurator::configure("log4cxx.xml");

    // Use multiple loggers
    log4cxx::Logger rootLogger = log4cxx::Logger::getRootLogger();
    log4cxx::Logger appLogger = log4cxx::Logger::getLogger("app");
    log4cxx::Logger dbLogger = log4cxx::Logger::getLogger("app.database");
    log4cxx::Logger netLogger = log4cxx::Logger::getLogger("app.network");

    // Application logs
    LOG4CXX_INFO(appLogger, "Application started");
    LOG4CXX_DEBUG(appLogger, "Configuration file loaded");

    // Database logs
    LOG4CXX_INFO(dbLogger, "Database connection established");
    LOG4CXX_WARN(dbLogger, "Connection pool usage: 85%");
    LOG4CXX_ERROR(dbLogger, "SQL query execution failed: timeout");

    // Network logs
    LOG4CXX_INFO(netLogger, "API server connection successful");
    LOG4CXX_DEBUG(netLogger, "HTTP request sent: GET /api/users");
    LOG4CXX_WARN(netLogger, "Response time delay: 2.5 seconds");

    LOG4CXX_INFO(appLogger, "Application terminated");

    return 0;
}
<!-- log4cxx.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<log4cxx:configuration xmlns:log4cxx="http://logging.apache.org/log4cxx">

  <!-- Console appender -->
  <appender name="console" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n"/>
    </layout>
  </appender>

  <!-- Application file appender -->
  <appender name="appFile" class="org.apache.log4j.RollingFileAppender">
    <param name="File" value="logs/application.log"/>
    <param name="MaxFileSize" value="10MB"/>
    <param name="MaxBackupIndex" value="5"/>
    <param name="Append" value="true"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} [%t] %-5p %c - %m%n"/>
    </layout>
  </appender>

  <!-- Database dedicated file appender -->
  <appender name="dbFile" class="org.apache.log4j.DailyRollingFileAppender">
    <param name="File" value="logs/database.log"/>
    <param name="DatePattern" value="'.'yyyy-MM-dd"/>
    <param name="Append" value="true"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{HH:mm:ss.SSS} %-5p [%c{2}] %m%n"/>
    </layout>
  </appender>

  <!-- Logger configuration -->
  <logger name="app">
    <level value="INFO"/>
    <appender-ref ref="appFile"/>
    <appender-ref ref="console"/>
  </logger>

  <logger name="app.database">
    <level value="DEBUG"/>
    <appender-ref ref="dbFile"/>
  </logger>

  <logger name="app.network">
    <level value="WARN"/>
  </logger>

  <!-- Root logger -->
  <root>
    <level value="ERROR"/>
    <appender-ref ref="console"/>
  </root>

</log4cxx:configuration>

Advanced Configuration and Customization

#include <log4cxx/logger.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/appender.h>
#include <log4cxx/fileappender.h>
#include <log4cxx/consoleappender.h>
#include <log4cxx/layout.h>
#include <log4cxx/patternlayout.h>
#include <log4cxx/level.h>
#include <log4cxx/helpers/pool.h>
#include <log4cxx/initializer.h>

// Custom log filter
class PerformanceFilter : public log4cxx::spi::Filter
{
public:
    FilterDecision decide(const log4cxx::spi::LoggingEventPtr& event) const override
    {
        // Allow only performance-related messages
        std::string message = event->getMessage();
        if (message.find("performance") != std::string::npos ||
            message.find("timing") != std::string::npos) {
            return FilterDecision::ACCEPT;
        }
        return FilterDecision::DENY;
    }
};

int main()
{
    log4cxx::Initializer initializer;

    // Programmatic configuration
    log4cxx::helpers::Pool pool;

    // Create pattern layout
    log4cxx::PatternLayoutPtr layout(new log4cxx::PatternLayout());
    layout->setConversionPattern(
        LOG4CXX_STR("%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{2} (%F:%L) - %m%n"));
    layout->activateOptions(pool);

    // Create file appender
    log4cxx::FileAppenderPtr fileAppender(new log4cxx::FileAppender());
    fileAppender->setName(LOG4CXX_STR("FileAppender"));
    fileAppender->setFile(LOG4CXX_STR("logs/custom.log"));
    fileAppender->setAppend(true);
    fileAppender->setLayout(layout);
    fileAppender->activateOptions(pool);

    // Add custom filter
    fileAppender->addFilter(log4cxx::spi::FilterPtr(new PerformanceFilter()));

    // Create console appender
    log4cxx::ConsoleAppenderPtr consoleAppender(new log4cxx::ConsoleAppender());
    consoleAppender->setName(LOG4CXX_STR("ConsoleAppender"));
    consoleAppender->setTarget(LOG4CXX_STR("System.out"));
    
    // Color layout (simplified version)
    log4cxx::PatternLayoutPtr colorLayout(new log4cxx::PatternLayout());
    colorLayout->setConversionPattern(
        LOG4CXX_STR("\\033[36m%d{HH:mm:ss}\\033[0m [\\033[35m%t\\033[0m] \\033[1m%-5p\\033[0m %c - %m%n"));
    colorLayout->activateOptions(pool);
    consoleAppender->setLayout(colorLayout);
    consoleAppender->activateOptions(pool);

    // Configure logger
    log4cxx::Logger rootLogger = log4cxx::Logger::getRootLogger();
    rootLogger.addAppender(fileAppender);
    rootLogger.addAppender(consoleAppender);
    rootLogger.setLevel(log4cxx::Level::getDebug());

    // Create custom logger
    log4cxx::Logger perfLogger = log4cxx::Logger::getLogger("performance");
    perfLogger.setLevel(log4cxx::Level::getTrace());

    // Log output test
    LOG4CXX_INFO(rootLogger, "Initialization complete with custom configuration");
    LOG4CXX_DEBUG(rootLogger, "Debug mode is enabled");
    
    // Performance logs (will pass filter)
    LOG4CXX_INFO(perfLogger, "performance: processing time 123ms");
    LOG4CXX_WARN(perfLogger, "timing: response delay detected");
    
    // Regular logs (will be filtered out)
    LOG4CXX_INFO(perfLogger, "Regular message - filtered out");

    return 0;
}

Error Handling and Troubleshooting

#include <log4cxx/logger.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/initializer.h>
#include <iostream>
#include <exception>

// Custom exception class
class DatabaseException : public std::exception
{
public:
    DatabaseException(const std::string& message) : message_(message) {}
    const char* what() const noexcept override { return message_.c_str(); }
private:
    std::string message_;
};

// Safe logger initialization
bool initializeLogging(const std::string& configFile)
{
    try {
        log4cxx::xml::DOMConfigurator::configure(configFile);
        return true;
    }
    catch (const log4cxx::helpers::Exception& e) {
        std::cerr << "log4cxx configuration error: " << e.what() << std::endl;
        return false;
    }
    catch (const std::exception& e) {
        std::cerr << "Configuration file loading error: " << e.what() << std::endl;
        return false;
    }
}

// Exception handling and logging
void processDatabase()
{
    log4cxx::Logger logger = log4cxx::Logger::getLogger("app.database");
    
    try {
        LOG4CXX_INFO(logger, "Database processing started");
        
        // Simulate database operation
        bool connectionFailed = true;  // Replace with actual condition
        if (connectionFailed) {
            throw DatabaseException("Connection timeout: server not responding");
        }
        
        LOG4CXX_INFO(logger, "Database processing completed successfully");
    }
    catch (const DatabaseException& e) {
        LOG4CXX_ERROR(logger, "Database error: " << e.what());
        
        // Add stack trace information
        LOG4CXX_DEBUG(logger, "Error location: " << __FILE__ << ":" << __LINE__);
        
        // Retry logic
        LOG4CXX_WARN(logger, "Retrying in 5 seconds...");
        throw;  // Re-throw exception to upper layer
    }
    catch (const std::exception& e) {
        LOG4CXX_FATAL(logger, "Unexpected error: " << e.what());
        throw;
    }
}

// Log rotation and cleanup
class LogManager
{
private:
    log4cxx::Logger logger_;
    
public:
    LogManager() : logger_(log4cxx::Logger::getLogger("LogManager")) 
    {
        LOG4CXX_INFO(logger_, "Log manager initialized");
    }
    
    ~LogManager()
    {
        LOG4CXX_INFO(logger_, "Log manager shutdown");
        // log4cxx shutdown is automatic
    }
    
    void forceLogRotation()
    {
        LOG4CXX_INFO(logger_, "Manual log rotation executed");
        // Implementation depends on appender type
    }
    
    void checkLogFileSize()
    {
        // Log file size check example
        LOG4CXX_DEBUG(logger_, "Checking log file size...");
        
        // Actual file size check logic
        long fileSize = getLogFileSize();
        if (fileSize > 100 * 1024 * 1024) {  // 100MB
            LOG4CXX_WARN(logger_, "Log file size is getting large: " 
                        << fileSize << " bytes");
        }
    }
    
private:
    long getLogFileSize() {
        // Actual implementation would use stat() etc.
        return 50 * 1024 * 1024;  // Simulate 50MB
    }
};

int main()
{
    log4cxx::Initializer initializer;
    
    // Step-by-step initialization and error handling
    if (!initializeLogging("log4cxx.xml")) {
        std::cerr << "Fallback: using basic configuration" << std::endl;
        log4cxx::BasicConfigurator::configure();
    }
    
    log4cxx::Logger mainLogger = log4cxx::Logger::getRootLogger();
    LOG4CXX_INFO(mainLogger, "Application started");
    
    // Use log manager
    LogManager logManager;
    
    try {
        processDatabase();
    }
    catch (const std::exception& e) {
        LOG4CXX_FATAL(mainLogger, "Application terminating due to fatal error: " << e.what());
        return 1;
    }
    
    // Regular maintenance
    logManager.checkLogFileSize();
    
    LOG4CXX_INFO(mainLogger, "Application terminated normally");
    return 0;
}

Production Environment Optimization

#include <log4cxx/logger.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/helpers/properties.h>
#include <log4cxx/mdc.h>
#include <log4cxx/ndc.h>
#include <log4cxx/initializer.h>
#include <thread>
#include <chrono>

// Performance measurement class
class PerformanceTimer
{
private:
    std::chrono::high_resolution_clock::time_point start_;
    log4cxx::Logger logger_;
    std::string operation_;

public:
    PerformanceTimer(const std::string& operation) 
        : start_(std::chrono::high_resolution_clock::now())
        , logger_(log4cxx::Logger::getLogger("performance"))
        , operation_(operation)
    {
        LOG4CXX_DEBUG(logger_, operation_ << " started");
    }
    
    ~PerformanceTimer()
    {
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start_);
        
        if (duration.count() > 1000) {  // Warning if over 1 second
            LOG4CXX_WARN(logger_, operation_ << " completed (delayed: " << duration.count() << "ms)");
        } else {
            LOG4CXX_INFO(logger_, operation_ << " completed (" << duration.count() << "ms)");
        }
    }
};

// Multi-threaded log processing
void workerThread(int threadId)
{
    // Thread-specific diagnostic context
    log4cxx::NDC::push("WorkerThread-" + std::to_string(threadId));
    log4cxx::MDC::put("threadId", std::to_string(threadId));
    
    log4cxx::Logger logger = log4cxx::Logger::getLogger("worker");
    
    LOG4CXX_INFO(logger, "Worker thread started");
    
    for (int i = 0; i < 5; ++i) {
        // Add context information via MDC
        log4cxx::MDC::put("iteration", std::to_string(i));
        
        {
            PerformanceTimer timer("DB Query " + std::to_string(i));
            
            // Simulate database query
            std::this_thread::sleep_for(std::chrono::milliseconds(100 + (i * 200)));
            
            LOG4CXX_DEBUG(logger, "Query executed: SELECT * FROM users WHERE id = " << i);
        }
        
        // Conditional log output (performance optimization)
        if (logger.isTraceEnabled()) {
            std::string debugInfo = "Detail info generation: Thread=" + std::to_string(threadId) 
                                  + ", Iteration=" + std::to_string(i);
            LOG4CXX_TRACE(logger, debugInfo);
        }
    }
    
    LOG4CXX_INFO(logger, "Worker thread terminated");
    
    // Cleanup
    log4cxx::MDC::remove("threadId");
    log4cxx::MDC::remove("iteration");
    log4cxx::NDC::pop();
}

// Resource monitoring and alerts
class SystemMonitor
{
private:
    log4cxx::Logger logger_;
    std::thread monitorThread_;
    bool running_;

public:
    SystemMonitor() 
        : logger_(log4cxx::Logger::getLogger("monitor"))
        , running_(true)
    {
        LOG4CXX_INFO(logger_, "System monitoring started");
        monitorThread_ = std::thread(&SystemMonitor::monitorLoop, this);
    }
    
    ~SystemMonitor()
    {
        running_ = false;
        if (monitorThread_.joinable()) {
            monitorThread_.join();
        }
        LOG4CXX_INFO(logger_, "System monitoring terminated");
    }

private:
    void monitorLoop()
    {
        log4cxx::NDC::push("SystemMonitor");
        
        while (running_) {
            checkMemoryUsage();
            checkCpuUsage();
            checkDiskSpace();
            
            std::this_thread::sleep_for(std::chrono::seconds(30));
        }
        
        log4cxx::NDC::pop();
    }
    
    void checkMemoryUsage()
    {
        // Simulate memory usage (actual implementation would use /proc/meminfo etc.)
        double memoryUsage = 67.5;
        
        log4cxx::MDC::put("memoryUsage", std::to_string(memoryUsage));
        
        if (memoryUsage > 90.0) {
            LOG4CXX_ERROR(logger_, "Memory usage at critical level: " << memoryUsage << "%");
        } else if (memoryUsage > 80.0) {
            LOG4CXX_WARN(logger_, "Memory usage is high: " << memoryUsage << "%");
        } else {
            LOG4CXX_DEBUG(logger_, "Memory usage normal: " << memoryUsage << "%");
        }
        
        log4cxx::MDC::remove("memoryUsage");
    }
    
    void checkCpuUsage()
    {
        double cpuUsage = 45.2;
        log4cxx::MDC::put("cpuUsage", std::to_string(cpuUsage));
        
        if (cpuUsage > 95.0) {
            LOG4CXX_FATAL(logger_, "CPU usage at limit: " << cpuUsage << "%");
        } else if (cpuUsage > 80.0) {
            LOG4CXX_WARN(logger_, "CPU usage is high: " << cpuUsage << "%");
        }
        
        log4cxx::MDC::remove("cpuUsage");
    }
    
    void checkDiskSpace()
    {
        double diskUsage = 72.1;
        log4cxx::MDC::put("diskUsage", std::to_string(diskUsage));
        
        if (diskUsage > 90.0) {
            LOG4CXX_ERROR(logger_, "Disk space low: " << diskUsage << "%");
        } else {
            LOG4CXX_TRACE(logger_, "Disk usage: " << diskUsage << "%");
        }
        
        log4cxx::MDC::remove("diskUsage");
    }
};

int main()
{
    log4cxx::Initializer initializer;
    
    // Load production configuration
    try {
        log4cxx::xml::DOMConfigurator::configureAndWatch("production-log4cxx.xml", 60000);  // Monitor configuration every 60 seconds
    } catch (...) {
        std::cerr << "Failed to load configuration file, using basic configuration" << std::endl;
        log4cxx::BasicConfigurator::configure();
    }
    
    log4cxx::Logger appLogger = log4cxx::Logger::getLogger("application");
    
    // Set application information in MDC
    log4cxx::MDC::put("version", "1.0.0");
    log4cxx::MDC::put("environment", "production");
    log4cxx::MDC::put("hostname", "web-server-01");
    
    LOG4CXX_INFO(appLogger, "Production application started");
    
    // Start system monitoring
    SystemMonitor monitor;
    
    // Multi-threaded processing
    std::vector<std::thread> workers;
    for (int i = 0; i < 3; ++i) {
        workers.emplace_back(workerThread, i);
    }
    
    // Simulate main loop
    for (int i = 0; i < 10; ++i) {
        PerformanceTimer timer("MainLoop-" + std::to_string(i));
        
        LOG4CXX_INFO(appLogger, "Main loop executing: " << i);
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    
    // Wait for thread termination
    for (auto& worker : workers) {
        worker.join();
    }
    
    LOG4CXX_INFO(appLogger, "All thread processing completed");
    
    // MDC cleanup
    log4cxx::MDC::clear();
    
    LOG4CXX_INFO(appLogger, "Application terminated normally");
    
    return 0;
}