log4cplus是C++编写的开源的日志系统很有名也很强大,但网上大多的资料都是讲如何配置使用。关于编译过程一笔带过,尤其是windows下的编译过程少之又少。得先有编译这个提才有后续的使用,里面的坑也少有提及。以下对详细的编译过程和使用做个完整总结。
一、log4cplus介绍
log4cplus是C++编写的开源的日志系统,前身是java编写的log4j系统,受Apache Software License保护,作者是Tad E. Smith。
log4cplus具有线程安全、灵活、以及多粒度控制的特点,通过将日志划分优先级使其可以面向程序调试、运行、测试、和维护等全生命周期。你可以选择将日志输出到屏幕、文件、NT event log、甚至是远程服务器;通过指定策略对日志进行定期备份等等。
二、log4cplus下载
下载链接:https://sourceforge.net/projects/log4cplus/files/log4cplus-stable/2.0.7/
最新稳定版2.0.8(2022.8.15)
官方文档:https://sourceforge.net/p/log4cplus/wiki/Home/
github地址:https://github.com/log4cplus/log4cplus
国内镜像地址:mirrors / log4cplus / log4cplus · GitCode
最新版可能需要C++2017(不一定哦,实测vs2015编译2.0.8通过。官方介绍是3.0以后的才需要c++2017),这里也给个低版本的,支持用C++11的一个tag版本: GitHub - log4cplus/log4cplus at REL_2_0_4
三、编译生成库
Linux下编译
linux下的编译稍微简单些.
1- 解压: gzip -cd log4cplus-x.x.x.tar.gz | tar -xf -
2- 进入log4cplus根目录: cd log4cplus-x.x.x
3- 产生Makefile: ./configure --prefix=/where/to/install -enable-threads=no
如果需要指定安装路径可使用--prefix参数, 否则将缺省安装到/usr/local目录下。另外,如果需要单线程版本可通过参数-enable-threads=no指定, 否则默认将安装多线程版本。
另外需要注意的是,--enable-static这个参数是编译静态库的选项,默认是没打开的。需要静态库的话需要开启此选项。
windows下编译
windows下的编译稍麻烦些。
可以选择的编译方式有:Visual Studio,cmake,vcpkg等. 其中使用vcpkg是最简单省事的。
方式一、Visual Studio环境编译
On Windows, the primary build system is Visual Studio 2015 solution and projects (msvc14/log4cplus.sln).
有windows下的Visual Studio环境的,可以打开Visual Studio,进入 log4cplus-2.x\msvc14目录下,运行log4cplus.sln解决方案。
1.选择log4cplus项目,属性里面的字符集和目标程序一致。右键——>属性——>配置属性——>常规——>字符集,选择Unicode字符集。
2.解决方案的平台与目标程序一致,这里选择的是x64。
3.版本也要与目标程序一致,这里选择的是release版本。
编译完成后,会在log4cplus-2.x\msvc14\x64\bin.Release文件夹下生成我们需要的log4cplus.lib和log4cplus.dll两个文件。
将log4cplus-2.0.x目录下的include文件夹拷贝到我们的目标程序文件夹中,这里面是我们需要的头文件。
简单的测试:
#include <log4cplus/log4cplus.h> //#pragma comment(lib, "log4cplus.lib") int main() { //初始化 log4cplus::Initializer initializer; log4cplus::BasicConfigurator config; config.configure(); log4cplus::Logger logger = log4cplus::Logger::getInstance( LOG4CPLUS_TEXT("main")); LOG4CPLUS_WARN(logger, LOG4CPLUS_TEXT("Hello, World!")); system("pause"); return 0; }
异步模式需要开启,不然多线程同时写日志会有问题。
以下是旧版本的才需要的。
旧版本的需要到 https://github.com/log4cplus/ThreadPool
把.h和.cpp文件下载下来,放到\log4cplus\log4cplus-REL_2_0_4\threadpool 目录里。
Catch-master
下载:Catch-master.zip 解压后,把文件夹中的内容复制到 log4cplus-REL_2_0_4\catch中。
新版本的不用这些操作,新版源码里已经包含了。
方式二、vcpkg环境
依赖包安装
1.使用神器vcpkg
2.vcpkg install log4cplus[core, unicode]:x86-windows
, 安装匹配项目的包(64位的是:x64-windows)
方式三、cmake环境编译
在下载的源码根目录下建个build_msvc文件夹,进入该文件夹,执行:
log4cplus-2.0.8\build_msvc> cmake ../
vc的工程
以上如果不指定生成的makefile类型,则默认生成的是vc的工程。
gcc工具链
若不需要vs的工程,则需额外指定生成的makefile文件类型如:
log4cplus-2.0.8\build_msvc>cmake ../ -G"Unix Makefiles"
但以上生成的默认是使用的gcc工具链。
MinGW工具链
若使用MinGW
编译器 ,则需指定
cmake -G "MinGW Makefiles"
生成的是MinGW Makefiles
,而不是生成 MSVC
的工程文件。
若想使用msvc的工具链和vc的nmake,怎么办呢?
msvc的工具链(nmake)
cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RELEASE -DTARGET_PROCESSOR=x64 ../
这个操作的前提是需要工具链和nmake在环境变量里能找到。
手工编译
手工编译的话,最简单的办法是直接进入vs自带的dos环境窗口:
以上是手工编译的详细过程。
自动化ps脚本编译
若要写成windows下的powershell脚本,则复用性更强些,可以写为:
$VcpkgPath = "E:/vcpkg/scripts/buildsystems/vcpkg.cmake" # Write-Host "`nVcpkgPath: $VcpkgPath" -ForegroundColor Yellow $QtPath = "D:\Qt5.12.11\Qt5.12.11\5.12.11\msvc2015_64\bin" $QtLibPATH = "D:\Qt5.12.11\Qt5.12.11\5.12.11\msvc2015_64\lib\cmake" # Write-Host "`nQtPath: $QtPath" -ForegroundColor Yellow function RemoveBuildDirectory { Set-Location .. if (Test-Path build_for_log4cplus) { Remove-Item build_for_log4cplus -Recurse } Set-Location script_build } function MakeEnvForBuildProject { Push-Location 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64' cmd /c "vcvars64.bat&set" | ForEach-Object { if ($_ -match "=") { $v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])" } } Pop-Location # write-host "`nVisual Studio 2015 Command Prompt variables set." -ForegroundColor Yellow Push-Location $QtPath cmd /c "qtenv2.bat&set" | ForEach-Object { if ($_ -match "=") { $v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])" } } Pop-Location } function ConfigCMakeProject { Set-Location ../ cmake . -B Release_build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_PREFIX_PATH="$QtLibPATH" -Wno-dev -G "NMake Makefiles" if ($?) { Write-Host "Config CMake Project Success." -ForegroundColor Green Set-Location script_build } else { Write-Host "Config CMake Project Failed." -ForegroundColor Red Remove-Item Release_build -Recurse Set-Location script_build Exit 2 } } function BuildCMakeProject { Write-Host "compiling for project. Debug" -ForegroundColor Green Set-Location ../Release_build $Env:NINJA_STATUS = "[%f/%t %e/sec]" nmake if ($?) { Write-Host "compiled for service project success." -ForegroundColor Green Set-Location .. Remove-Item Release_build -Recurse Set-Location script_build } else { Write-Host "compiled for service project failed." -ForegroundColor Red Set-Location .. Remove-Item Release_build -Recurse Set-Location script_build Exit 3 } } RemoveBuildDirectory MakeEnvForBuildProject ConfigCMakeProject BuildCMakeProject
经过以上操作,就输出了我们想要的log4cplusU.dll了,不过静态库竟然没生成。(其实是有生成的,在src文件夹里,没和动态库所在的bin文件夹一块儿)。使用时别忘把头文件也拷贝进自己的项目工程。
四、log4cplus库使用
加载库到工程
把需要的头文件放在工程目录下。把需要的log4cplusU.dll和log4cplusU.lib库也添加到工程项目里。
注意,虽然使用的是静态库,但是那个动态库也得放进去。否则虽然编译通过,但是跑不起来,会报找不到og4cplusU.dll的错误。
这里以cmake的工程为例,介绍下静态库的使用。CMakeLists.txt文件片段如下:
#find_library(LibConfig libconfig++) #message(STATUS ${LibConfig}) #find_package(unofficial-breakpad CONFIG REQUIRED) set(LOGGING_LIB ${LIB_DIR}/lib/log4cplusU${LIB_FIX}.lib) set(THIRD_LIBS ${LOGGING_LIB} #unofficial::breakpad::libbreakpad #unofficial::breakpad::libbreakpad_client ) #target_link_options(${PROJECT_NAME} PRIVATE -mwindows) target_link_libraries(${PROJECT_NAME} PRIVATE ${THIRD_LIBS})
基本使用步骤
使用log4cplus有六个基本步骤:
1、实例化一个封装了输出介质的appender对象。
2、实例化一个封装了输出格式的layout对象。
3、将layout对象绑定(attach)到appender对象,如省略此步骤,简单布局器SimpleLayout(参见5.1小节)对象会绑定到logger。
4、实例化一个封装了日志输出logger对象,并调用其静态函数getInstance()获得实例,log4cplus::Logger::getInstance(“logger_name”)。
5、将appender对象绑定(attach)到logger对象。
6、设置logger的优先级,如省略此步骤,各种有限级的日志都将被输出。
代码示例
#include <log4cplus/log4cplus.h> int main() { //用Initializer类进行初始化 log4cplus::Initializer initializer; //第1步:创建ConsoleAppender(实例化一个appender对象) log4cplus::SharedAppenderPtr appender(new log4cplus::ConsoleAppender()); //第2步:设置Appender的名称和输出格式(SimpleLayout) appender->setName(LOG4CPLUS_TEXT("console")); //第3步:实例化一个layout对象,将layout对象绑定到appender对象 appender->setLayout(std::unique_ptr<log4cplus::Layout>(new log4cplus::SimpleLayout)); //第4步:实例化一个封装了日志输出的Logger对象,并设置其日志输出等级阈值 log4cplus::Logger logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("test")); //第5步:将appender对象绑定到logger对象 logger.addAppender(appender); //第6步:设置日志log的优先级 logger.setLogLevel(log4cplus::INFO_LOG_LEVEL); //使用宏将日志输出 LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("Hello world")); return 0; }
以上示例,使用了简单的Simplelayout布局器。输出格式可能不是我们想要的输出格式。一般常使用PatternLayout格式,示例如下:
#include <log4cplus/log4cplus.h> int main() { //用Initializer类进行初始化 log4cplus::Initializer initializer; //第1步:建立ConsoleAppender log4cplus::SharedAppenderPtr appender(new log4cplus::ConsoleAppender()); //第2步:设置Appender的名称和输出格式(SimpleLayout) appender->setName(LOG4CPLUS_TEXT("console")); log4cplus::tstring pattern = LOG4CPLUS_TEXT("%D{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p %c - %m [%l]%n"); appender->setLayout(std::unique_ptr<log4cplus::Layout>(new log4cplus::PatternLayout(pattern))); //第3步:得到一个Logger实例,并设置其日志输出等级阈值 log4cplus::Logger logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("test")); logger.setLogLevel(log4cplus::INFO_LOG_LEVEL); //第4步:为Logger实例添加ConsoleAppender logger.addAppender(appender); //第5步:使用宏将日志输出 LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("Hello world")); return 0; }