log4cplus在windows下的编译,以及配置文件的用法

上一篇文章介绍了log4cpp库在Windows系统下的编译和使用,在学习过程中发现在log4系列的库中,log4cpp相比之下比较旧,更新也少,最近一次更新也要追溯到2017年了。而同样适用于c++的log4cplus库更新则频繁得多,功能看上去也更强大,于是乎就有了改用log4cplus的想法。

  • 具体的操作步骤和相对的重点以此无序列表的形式标记出来,方便阅读

本以为log4cplus就是log4cpp的兼容升级版,但实际试下来感觉区别不小。其强大的功能(如确保线程安全,动态监控配置文件设置,将日志传到远程服务器等)使得它的用法和需要注意的问题也在一定程度上复杂化了。
本文在使用功能的范围及封装后的使用方法基本保持与log4cpp的编译及配置文件的使用方法一致,虽然log4cplus明显功能更多,代码也更完善(丰富的注释和文档),但最终选择哪种库还是以需求为准。强大的工具能解决更多的问题,但有时候也可能显得笨重且不好操控。

关于环境和软件版本

log4cplus 2.0.5
CMake 3.17.1
Visual Studio 2017
git ,git bash

使用CMake和VS2017编译log4cplus

官方的README中就直接提到了支持Windows7和MS Visual Studio 2015,源码目录内有个msvc文件夹,里面就是VS的工程项目,使用vs2015应该也可以顺利的编译,不过我电脑中没有vs2015,使用其它版本可能需要改动,最后没有尝试这个方法。

相比log4cpp,log4cplus提供了更加完善的cmake支持,编译后的文件会包含一个cmake文件夹,可以直接使用cmake的findpackage命令将其添加到你的工程项目中。log4cplus的团队已经在诸多平台上进行了测试,构建文件比较完善,相应的,编译过程通常比较顺利。

  • 在顶层CMakeLists.txt中添加CMAKE_INSTALL_PREFIX,否则安装时会因没有权限而失败,在cmake_minimum_required (VERSION 3.1)下方添加一行即可
set(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR}/installed)
  • 在此目录下打开git bash,输入
mkdir build
cd build
cmake -A x64 ..
  • 其中-A x64参数用来设置编译64位的库。之后在build目录会生成一个VS工程项目,打开log4cplus.sln,在解决方案一栏中右击install选择生成,即可编译成功。库文件会存放在./build/installed/bin中

注意如果选择Release模式,则生成的库为log4cplusU.dll
Debug模式生成的库为log4cplusUD.dll

编写配置文件

使用配置文件来加载配置的方法在使用上比较灵活,通常无需重新编译程序即可修改日志设置

Mylog.conf
log4cplus.rootLogger=DEBUG, console

log4cplus.logger.rollingFile=DEBUG,rollingFile

# 定义console类型和layout属性
log4cplus.appender.console=log4cplus::ConsoleAppender
log4cplus.appender.console.layout=log4cplus::PatternLayout
log4cplus.appender.console.layout.ConversionPattern=%D{%m/%d/%y  %H:%M:%S.%q} [%t] %-5p %c{2} - %m %n 

#定义rollingFile的属性
log4cplus.appender.rollingFile=log4cplus::RollingFileAppender
log4cplus.appender.rollingFile.File=./log/Test.log

#下方这一条用来设置日志路径不存在时自动创建,不必再像log4cpp中需要保证路径存在
log4cplus.appender.rollingFile.CreateDirs=true 
log4cplus.appender.rollingFile.MaxFileSize=1MB
log4cplus.appender.rollingFile.MaxBackupIndex=3
log4cplus.appender.rollingFile.layout=log4cplus::PatternLayout
log4cplus.appender.rollingFile.layout.ConversionPattern=%D{%m/%d/%y  %H:%M:%S.%q} [%t] %-5p %c{2} - %m %n

使用单例模式封装log4cplus

其实log4cplus本身也是单例的,这里封装的主要意义在于简化代码以及统一用法,在需要记录日志的文件中包含封装好的头文件即可按规则使用。注意我这里的单例没有线程相关代码,有需要可以按照经典单例模式中的思路增添加锁操作。

Mylog.h
#ifndef __MYLOG_H__
#define __MYLOG_H__

#include <log4cplus/initializer.h>
#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <log4cplus/helpers/stringhelper.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/log4cplus.h>

#include <iostream>
#include <string>

using std::string;

class Mylog
{
public:
	static Mylog*  getInstance();
	static Mylog* myLogger;

	void error(string& msg);
	void warn(string& msg);
	void debug(string& msg);
	void info(string& msg);

private:
	Mylog();
	void init();
	static Mylog* _mLogger;
	static log4cplus::Logger _mCate;
	
};

#define postfix(msg)  (std::string(msg).append(" [ ")\
	.append(__FILE__).append(":").append(__func__)\
	.append(":").append(std::to_string(__LINE__)).append(" ] "))


#define LOG_ERROR(msg) Mylog::myLogger->error(postfix(msg)) 
#define LOG_WARN(msg)  Mylog::myLogger->warn(postfix(msg))
#define LOG_INFO(msg)  Mylog::myLogger->info(postfix(msg))
#define LOG_DEBUG(msg) Mylog::myLogger->debug(postfix(msg))


#endif
Mylog.cpp

注意log4cplus使用tstring和输出流,所以想输出std::string对象或字符串常量中的内容时需要使用宏命令进行转换。

  • log4cplus中已有相应的转换宏,在以下代码中都有使用
#include "Mylog.h"
#include <log4cplus/configurator.h>
#include <log4cplus/helpers/stringhelper.h>
#include <log4cplus/loggingmacros.h>

Mylog* Mylog::_mLogger = Mylog::getInstance();
Mylog* Mylog::myLogger = Mylog::getInstance();
log4cplus::Logger Mylog::_mCate = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("rollingFile"));


Mylog* Mylog::getInstance()
{
	if (_mLogger == nullptr)
	{
		_mLogger = new Mylog();
	}
	return _mLogger;
}


Mylog::Mylog()
{
	this->init();
}

void Mylog::init()
{
	try
	{
		//这里对字符串常量进行了转换
		log4cplus::PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("Mylog.conf"));
	}
	catch (...)
	{
		std::cerr << "configure problem "  << std::endl;
	}
}

void Mylog::error(string& msg)
{
	//这里对std::string类型进行了转换
	LOG4CPLUS_ERROR(_mCate,LOG4CPLUS_STRING_TO_TSTRING(msg));
}

void Mylog::warn(string& msg)
{
	LOG4CPLUS_WARN(_mCate, LOG4CPLUS_STRING_TO_TSTRING(msg));
}

void Mylog::debug(string& msg)
{
	LOG4CPLUS_DEBUG(_mCate, LOG4CPLUS_STRING_TO_TSTRING(msg));
}

void Mylog::info(string& msg)
{
	LOG4CPLUS_INFO(_mCate, LOG4CPLUS_STRING_TO_TSTRING(msg));
}

使用示例

使用上与log4cpp有一些不同,在官方文档中关于Windows和Visual Studio有如下说明


log4cplus uses C++11 thread and synchronization facilities. The synchronization facilities are implemented in Visual Studio C++ standard library in a way that utilizes global variables. Therefore it is impossible (due to “static initialization order fiasco”) to use them outside main(). This issue manifests as a deadlock on exit during destruction of log4cplus’ thread pool.

To overcome this limitation,

  • always use log4cplus::Initializer initializer; as the first thing in main();

  • never try to log from static/global objects constructors;

  • never try to log from static/global object destructors.

Defining the log4cplus::Initializer instance as the first thing in main() ensures that log4cplus is initialized. More importantly, it ensures that log4cplus shuts down before the execution leaves the main() function. log4cplus will try to automatically initialize at process startup and/or on DLL load, and will not tear down until all log4cplus:Initializer instances are destroyed.


  • 简单说就是在main函数中需要添加log4cplus::Initializer initializer;这一行以确保log4cplus实例的初始化和销毁,初始化时会开启线程池,如果没有这一句可能会使线程池无法关闭而导致程序不能退出。
main.cpp
#include <iostream>
#include <stdio.h>
#include <string>
#include "Mylog.h"

int func()
{
	LOG_WARN("这里是中文");
	
	LOG_DEBUG("这里也是中文");
	return 1;
}

int main()
{
	log4cplus::Initializer initializer;

	func();
	
	std::cout<<"press any key to continue..."<<std::endl;
	getchar();
	
	return 0;
}

用法小结

  • 将log4cplus头文件路径和库依赖添加至工程项目中,Mylog.h和Mylog.cpp添加至代码目录中
  • 在需要记录日志的文件添加头文件#include "Mylog.h"
  • 在代码中使用LOG_ERROR() LOG_WARN() LOG_INFO() LOG_DEBUG()添加想要输出到日志的内容
  • 将Mylog.conf添加至可执行文件目录中即可

使用CMake+VS2017编译示例程序

依旧以此种方式举例,CMakeLists.txt中链接log4cplus库的方法

  • 按照前述方法编译出的文件应该存放在./build/installed/目录下
    在./build/installed/lib/cmake/log4cplus中可以找到如下.cmake文件

log4cplusConfig.cmake
log4cplusConfigVersion.cmake
log4cplusTargets.cmake
log4cplusTargets-release.cmake

CMake的findpackage命令可以通过这些.cmake来较为自动地将库添加至工程项目中

set(log4cplus_DIR /编译出的.cmake文件所在路径/)
find_package(log4cplus REQUIRED)
target_link_libraries(${PROJECT_NAME} log4cplus::log4cplusU)
代码根目录下编写CMakeLists.txt
cmake_minimum_required(VERSION 3.12.0)

#set PROJECT_NAME and version
project(testLog)
set(VERSION_MAJOR 0)
set(VERSION_MINOR 0)
set(VERSION_PATCH 1)


file(GLOB SOURCE_FILE *.cpp)
file(GLOB HEAD_FILE *.h)

set(log4cplus_DIR ${PROJECT_SOURCE_DIR}/lib/log4cplus/lib/cmake/log4cplus)
find_package(log4cplus REQUIRED)
if(log4cplus_FOUND)
	message("found log4cplus ")
endif()


set(project_headers
	${HEAD_FILE})

set(project_sources
	${SOURCE_FILE})

add_executable(${PROJECT_NAME} ${project_headers} ${project_sources})

target_link_libraries(${PROJECT_NAME} log4cplus::log4cplusU)

set(CMAKE_INSTALL_PREFIX ${PROJECT_BINARY_DIR}/installed)

install(TARGETS ${PROJECT_NAME} DESTINATION /)

install(FILES ${PROJECT_SOURCE_DIR}/Mylog.conf DESTINATION /)
install(FILES ${PROJECT_SOURCE_DIR}/lib/log4cplus/bin/log4cplusU.dll DESTINATION .)

编译

  • 代码根目录内打开git bash
mkdir build
cd build
cmake -A x64 ..
  • 打开生成的VS项目testLog.sln,解决方案中找到install,右击选择->生成,之后就可以在./build/installed目录中找到可执行文件testLog.exe了。

github示例项目使用说明

本文代码以上传至github
新建一个用来存放代码仓库的目录

git clone https://github.com/tinyprogramer/log4cplusTutorial.git

打开git bash,执行./build.bat即可

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值