Apache log4cxx
Apache財団によるJava log4jのC++移植版。Java開発者に馴染みのあるAPI設計と設定方式を提供。アペンダー、レイアウト、フィルター等の概念をC++に適用した伝統的なロギングフレームワーク。
ライブラリ
Apache log4cxx
概要
Apache log4cxxは、Apache財団によって開発されたJava log4jのC++移植版です。Java開発者に馴染みのあるAPI設計と設定方式を提供し、アペンダー、レイアウト、フィルター等のlog4j由来の概念をC++環境に適用した伝統的なロギングフレームワークです。スレッドセーフ性、柔軟な設定、階層的なロガー管理といったエンタープライズ級の機能を備え、C++アプリケーションでの包括的なログ管理を実現します。
詳細
Apache log4cxx 2025年版は、Java log4jの設計思想を忠実にC++に移植した成熟したロギングソリューションとして15年以上の開発実績を持ちます。XMLやプロパティファイルによる設定管理、複数の出力先への同時ログ出力、メッセージフィルタリング、ログローテーション機能など、大規模C++システムで求められる高度な要件に対応しています。CMakeビルドシステムに完全対応し、クロスプラットフォーム開発をサポート。Java開発経験を持つチームでのC++プロジェクト移行において、学習コストを最小化できる設計が特徴です。
主な特徴
- Java log4j API互換: 馴染みのあるLogger、Appender、Layout概念の踏襲
- XML設定サポート: 外部ファイルによる柔軟な設定管理
- 階層的ロガー: パッケージ階層に対応したロガー管理システム
- マルチプラットフォーム: Windows、Linux、macOS対応
- 豊富なアペンダー: ファイル、コンソール、ネットワーク出力対応
- スレッドセーフ: マルチスレッド環境での安全な動作保証
メリット・デメリット
メリット
- Java log4j経験者にとって直感的で理解しやすいAPI設計
- XML設定による実行時のログレベル・出力先変更が容易
- 豊富なアペンダーとレイアウトによる柔軟な出力カスタマイズ
- 階層的ロガー管理による大規模プロジェクトでの統一的なログ制御
- Apache財団による長期的な保守とコミュニティサポート
- Java・C++混在環境での統一的なログ管理アプローチ
デメリット
- 現代的なC++プロジェクトでは他の軽量ライブラリが優先される傾向
- 設定の複雑さにより小規模プロジェクトでは過剰機能となる場合
- パフォーマンス面でspdlogやBoost.Logなど専用C++ライブラリに劣る
- メンテナンス活動が限定的で最新C++標準への対応が遅い
- 依存関係とビルド設定の複雑さによる導入コスト
- Java特有の概念がC++の慣例と合わない場面での違和感
参考ページ
書き方の例
インストールと基本セットアップ
# Ubuntu/Debianでの依存関係インストール
sudo apt-get update
sudo apt-get install build-essential cmake libapr1-dev libaprutil1-dev
# ソースからのビルド
git clone https://github.com/apache/logging-log4cxx.git
cd logging-log4cxx
mkdir build && cd build
# CMakeでのビルド設定
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DLOG4CXX_ENABLE_TESTS=OFF
# ビルドとインストール
make -j$(nproc)
sudo make install
# pkg-configの確認
pkg-config --modversion liblog4cxx
pkg-config --cflags --libs liblog4cxx
# CMakeLists.txtでの使用例
find_package(log4cxx REQUIRED)
target_link_libraries(your_target ${LOG4CXX_LIBRARIES})
target_include_directories(your_target PRIVATE ${LOG4CXX_INCLUDE_DIRS})
基本的なロギング(Hello World)
#include <log4cxx/logger.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/configurator.h>
#include <log4cxx/initializer.h>
int main()
{
// log4cxxの初期化
log4cxx::Initializer initializer;
// 基本設定の適用
log4cxx::BasicConfigurator config;
config.configure();
// ロガーインスタンスの取得
log4cxx::Logger logger = log4cxx::Logger::getInstance(
LOG4CPLUS_TEXT("MyApp"));
// 基本的なログ出力
LOG4CXX_TRACE(logger, "アプリケーションを開始します");
LOG4CXX_DEBUG(logger, "デバッグ情報: システム初期化完了");
LOG4CXX_INFO(logger, "情報: ユーザーログイン成功");
LOG4CXX_WARN(logger, "警告: メモリ使用量が高くなっています");
LOG4CXX_ERROR(logger, "エラー: データベース接続に失敗");
LOG4CXX_FATAL(logger, "致命的エラー: システムを停止します");
return 0;
}
ログレベル制御とフィルタリング
#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 メッセージ - 最詳細デバッグ情報");
LOG4CXX_DEBUG(logger, "DEBUG メッセージ - デバッグ情報");
LOG4CXX_INFO(logger, "INFO メッセージ - 一般情報");
LOG4CXX_WARN(logger, "WARN メッセージ - 警告");
LOG4CXX_ERROR(logger, "ERROR メッセージ - エラー");
LOG4CXX_FATAL(logger, "FATAL メッセージ - 致命的エラー");
}
void testLogLevel(log4cxx::LogLevel level, const std::string& levelName)
{
log4cxx::Logger logger = log4cxx::Logger::getInstance(
LOG4CPLUS_TEXT("TestLogger"));
// ログレベルの設定
logger.setLogLevel(level);
std::cout << "=== " << levelName << " レベルでのテスト ===" << std::endl;
printAllLogLevels(logger);
std::cout << std::endl;
}
int main()
{
log4cxx::Initializer initializer;
log4cxx::BasicConfigurator config;
config.configure();
// 各ログレベルでのテスト
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");
// 条件付きログ実行の例
log4cxx::Logger mainLogger = log4cxx::Logger::getInstance(
LOG4CPLUS_TEXT("MainApp"));
if (mainLogger.isDebugEnabled()) {
// DEBUGレベルが有効な場合のみ実行される重い処理
std::string expensiveDebugInfo = generateExpensiveDebugInfo();
LOG4CXX_DEBUG(mainLogger, "詳細デバッグ情報: " << expensiveDebugInfo);
}
return 0;
}
std::string generateExpensiveDebugInfo() {
// 時間のかかるデバッグ情報生成をシミュレート
std::stringstream ss;
for (int i = 0; i < 1000; ++i) {
ss << "Item " << i << " ";
}
return ss.str();
}
XML設定ファイルの使用
// main.cpp
#include <log4cxx/logger.h>
#include <log4cxx/loggingmacros.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/initializer.h>
int main()
{
log4cxx::Initializer initializer;
// XML設定ファイルの読み込み
log4cxx::xml::DOMConfigurator::configure("log4cxx.xml");
// 複数のロガーを使用
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");
// アプリケーションログ
LOG4CXX_INFO(appLogger, "アプリケーション開始");
LOG4CXX_DEBUG(appLogger, "設定ファイル読み込み完了");
// データベースログ
LOG4CXX_INFO(dbLogger, "データベース接続確立");
LOG4CXX_WARN(dbLogger, "接続プールの使用率: 85%");
LOG4CXX_ERROR(dbLogger, "SQLクエリ実行失敗: タイムアウト");
// ネットワークログ
LOG4CXX_INFO(netLogger, "APIサーバー接続成功");
LOG4CXX_DEBUG(netLogger, "HTTPリクエスト送信: GET /api/users");
LOG4CXX_WARN(netLogger, "レスポンス時間遅延: 2.5秒");
LOG4CXX_INFO(appLogger, "アプリケーション終了");
return 0;
}
<!-- log4cxx.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<log4cxx:configuration xmlns:log4cxx="http://logging.apache.org/log4cxx">
<!-- コンソールアペンダー -->
<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>
<!-- アプリケーション用ファイルアペンダー -->
<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>
<!-- データベース専用ファイルアペンダー -->
<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 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>
<level value="ERROR"/>
<appender-ref ref="console"/>
</root>
</log4cxx:configuration>
高度な設定とカスタマイズ
#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>
// カスタムログフィルター
class PerformanceFilter : public log4cxx::spi::Filter
{
public:
FilterDecision decide(const log4cxx::spi::LoggingEventPtr& event) const override
{
// パフォーマンス関連のメッセージのみ通す
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;
// プログラマティック設定
log4cxx::helpers::Pool pool;
// パターンレイアウトの作成
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);
// ファイルアペンダーの作成
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);
// カスタムフィルターの追加
fileAppender->addFilter(log4cxx::spi::FilterPtr(new PerformanceFilter()));
// コンソールアペンダーの作成
log4cxx::ConsoleAppenderPtr consoleAppender(new log4cxx::ConsoleAppender());
consoleAppender->setName(LOG4CXX_STR("ConsoleAppender"));
consoleAppender->setTarget(LOG4CXX_STR("System.out"));
// 色付きレイアウト(簡易版)
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);
// ロガーの設定
log4cxx::Logger rootLogger = log4cxx::Logger::getRootLogger();
rootLogger.addAppender(fileAppender);
rootLogger.addAppender(consoleAppender);
rootLogger.setLevel(log4cxx::Level::getDebug());
// カスタムロガーの作成
log4cxx::Logger perfLogger = log4cxx::Logger::getLogger("performance");
perfLogger.setLevel(log4cxx::Level::getTrace());
// ログ出力テスト
LOG4CXX_INFO(rootLogger, "カスタム設定による初期化完了");
LOG4CXX_DEBUG(rootLogger, "デバッグモードが有効です");
// パフォーマンスログ(フィルターを通過)
LOG4CXX_INFO(perfLogger, "performance: 処理時間 123ms");
LOG4CXX_WARN(perfLogger, "timing: レスポンス遅延検出");
// 通常ログ(フィルターで除外される)
LOG4CXX_INFO(perfLogger, "通常メッセージ - フィルターで除外");
return 0;
}
エラーハンドリングとトラブルシューティング
#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>
// カスタム例外クラス
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_;
};
// 安全なロガー初期化
bool initializeLogging(const std::string& configFile)
{
try {
log4cxx::xml::DOMConfigurator::configure(configFile);
return true;
}
catch (const log4cxx::helpers::Exception& e) {
std::cerr << "log4cxx設定エラー: " << e.what() << std::endl;
return false;
}
catch (const std::exception& e) {
std::cerr << "設定ファイル読み込みエラー: " << e.what() << std::endl;
return false;
}
}
// 例外処理とログ出力
void processDatabase()
{
log4cxx::Logger logger = log4cxx::Logger::getLogger("app.database");
try {
LOG4CXX_INFO(logger, "データベース処理開始");
// データベース操作をシミュレート
bool connectionFailed = true; // 実際の条件に置き換え
if (connectionFailed) {
throw DatabaseException("接続タイムアウト: サーバーが応答しません");
}
LOG4CXX_INFO(logger, "データベース処理正常完了");
}
catch (const DatabaseException& e) {
LOG4CXX_ERROR(logger, "データベースエラー: " << e.what());
// スタックトレース情報の追加
LOG4CXX_DEBUG(logger, "エラー発生箇所: " << __FILE__ << ":" << __LINE__);
// リトライロジック
LOG4CXX_WARN(logger, "5秒後に再試行します...");
throw; // 上位レイヤーに例外を再送出
}
catch (const std::exception& e) {
LOG4CXX_FATAL(logger, "予期しないエラー: " << e.what());
throw;
}
}
// ログローテーションとクリーンアップ
class LogManager
{
private:
log4cxx::Logger logger_;
public:
LogManager() : logger_(log4cxx::Logger::getLogger("LogManager"))
{
LOG4CXX_INFO(logger_, "ログマネージャー初期化");
}
~LogManager()
{
LOG4CXX_INFO(logger_, "ログマネージャー終了処理");
// log4cxxのシャットダウンは自動的に行われる
}
void forceLogRotation()
{
LOG4CXX_INFO(logger_, "手動ログローテーション実行");
// 実装はアペンダーの種類に依存
}
void checkLogFileSize()
{
// ログファイルサイズのチェック例
LOG4CXX_DEBUG(logger_, "ログファイルサイズをチェック中...");
// 実際のファイルサイズチェックロジック
long fileSize = getLogFileSize();
if (fileSize > 100 * 1024 * 1024) { // 100MB
LOG4CXX_WARN(logger_, "ログファイルサイズが大きくなっています: "
<< fileSize << " bytes");
}
}
private:
long getLogFileSize() {
// 実際の実装では stat() などを使用
return 50 * 1024 * 1024; // 50MB をシミュレート
}
};
int main()
{
log4cxx::Initializer initializer;
// 段階的な初期化とエラーハンドリング
if (!initializeLogging("log4cxx.xml")) {
std::cerr << "フォールバック: 基本設定を使用します" << std::endl;
log4cxx::BasicConfigurator::configure();
}
log4cxx::Logger mainLogger = log4cxx::Logger::getRootLogger();
LOG4CXX_INFO(mainLogger, "アプリケーション開始");
// ログマネージャーの使用
LogManager logManager;
try {
processDatabase();
}
catch (const std::exception& e) {
LOG4CXX_FATAL(mainLogger, "致命的エラーによりアプリケーションを終了: " << e.what());
return 1;
}
// 定期的なメンテナンス
logManager.checkLogFileSize();
LOG4CXX_INFO(mainLogger, "アプリケーション正常終了");
return 0;
}
プロダクション環境での最適化
#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>
// パフォーマンス測定用クラス
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_ << " 開始");
}
~PerformanceTimer()
{
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start_);
if (duration.count() > 1000) { // 1秒以上の場合は警告
LOG4CXX_WARN(logger_, operation_ << " 完了 (遅延: " << duration.count() << "ms)");
} else {
LOG4CXX_INFO(logger_, operation_ << " 完了 (" << duration.count() << "ms)");
}
}
};
// マルチスレッド対応ログ処理
void workerThread(int threadId)
{
// スレッド固有の診断コンテキスト
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, "ワーカースレッド開始");
for (int i = 0; i < 5; ++i) {
// MDCによるコンテキスト情報追加
log4cxx::MDC::put("iteration", std::to_string(i));
{
PerformanceTimer timer("DB Query " + std::to_string(i));
// データベースクエリをシミュレート
std::this_thread::sleep_for(std::chrono::milliseconds(100 + (i * 200)));
LOG4CXX_DEBUG(logger, "クエリ実行: SELECT * FROM users WHERE id = " << i);
}
// 条件付きログ出力(パフォーマンス最適化)
if (logger.isTraceEnabled()) {
std::string debugInfo = "詳細情報生成: Thread=" + std::to_string(threadId)
+ ", Iteration=" + std::to_string(i);
LOG4CXX_TRACE(logger, debugInfo);
}
}
LOG4CXX_INFO(logger, "ワーカースレッド終了");
// クリーンアップ
log4cxx::MDC::remove("threadId");
log4cxx::MDC::remove("iteration");
log4cxx::NDC::pop();
}
// リソース監視とアラート
class SystemMonitor
{
private:
log4cxx::Logger logger_;
std::thread monitorThread_;
bool running_;
public:
SystemMonitor()
: logger_(log4cxx::Logger::getLogger("monitor"))
, running_(true)
{
LOG4CXX_INFO(logger_, "システム監視開始");
monitorThread_ = std::thread(&SystemMonitor::monitorLoop, this);
}
~SystemMonitor()
{
running_ = false;
if (monitorThread_.joinable()) {
monitorThread_.join();
}
LOG4CXX_INFO(logger_, "システム監視終了");
}
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()
{
// メモリ使用量シミュレート (実際は /proc/meminfo 等を使用)
double memoryUsage = 67.5;
log4cxx::MDC::put("memoryUsage", std::to_string(memoryUsage));
if (memoryUsage > 90.0) {
LOG4CXX_ERROR(logger_, "メモリ使用量が危険水準: " << memoryUsage << "%");
} else if (memoryUsage > 80.0) {
LOG4CXX_WARN(logger_, "メモリ使用量が高くなっています: " << memoryUsage << "%");
} else {
LOG4CXX_DEBUG(logger_, "メモリ使用量正常: " << 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使用率が限界: " << cpuUsage << "%");
} else if (cpuUsage > 80.0) {
LOG4CXX_WARN(logger_, "CPU使用率が高い: " << 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_, "ディスク容量不足: " << diskUsage << "%");
} else {
LOG4CXX_TRACE(logger_, "ディスク使用量: " << diskUsage << "%");
}
log4cxx::MDC::remove("diskUsage");
}
};
int main()
{
log4cxx::Initializer initializer;
// プロダクション設定の読み込み
try {
log4cxx::xml::DOMConfigurator::configureAndWatch("production-log4cxx.xml", 60000); // 60秒間隔で設定監視
} catch (...) {
std::cerr << "設定ファイル読み込み失敗、基本設定を使用" << std::endl;
log4cxx::BasicConfigurator::configure();
}
log4cxx::Logger appLogger = log4cxx::Logger::getLogger("application");
// アプリケーション情報をMDCに設定
log4cxx::MDC::put("version", "1.0.0");
log4cxx::MDC::put("environment", "production");
log4cxx::MDC::put("hostname", "web-server-01");
LOG4CXX_INFO(appLogger, "プロダクションアプリケーション開始");
// システム監視開始
SystemMonitor monitor;
// マルチスレッド処理
std::vector<std::thread> workers;
for (int i = 0; i < 3; ++i) {
workers.emplace_back(workerThread, i);
}
// メインループシミュレート
for (int i = 0; i < 10; ++i) {
PerformanceTimer timer("MainLoop-" + std::to_string(i));
LOG4CXX_INFO(appLogger, "メインループ実行中: " << i);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// スレッド終了待ち
for (auto& worker : workers) {
worker.join();
}
LOG4CXX_INFO(appLogger, "全スレッド処理完了");
// MDCクリーンアップ
log4cxx::MDC::clear();
LOG4CXX_INFO(appLogger, "アプリケーション正常終了");
return 0;
}