C++开源跨平台OJ系统判题核心FreeJudger(二)——logger设计
By 马冬亮(凝霜 Loki)
一个人的战争(http://blog.csdn.net/MDL13412)
前言
日志库在中等规模以上程序中的重要意义不必多说,特别在一些并行程序中,其起着“调试器”的作用。现在开源的日志库很多,下面列举其中一些著名的项目,并分析其特点:
![]()
由于以前在linux下封装过log4cxx日志库,所以在FreeJudger项目中logger模块采用log4cxx为核心。
接口设计
日志接口需要提供 Fatal、Error、Warn、Info、Debug、Trace几种日志级别,因此 ILogger接口设计如下:
class JUDGER_API ILogger { public: ILogger(); virtual ~ILogger(); virtual void logFatal(const OJString &msg) const = 0; virtual void logError(const OJString &msg) const = 0; virtual void logWarn(const OJString &msg) const = 0; virtual void logInfo(const OJString &msg) const = 0; virtual void logDebug(const OJString &msg) const = 0; virtual void logTrace(const OJString &msg) const = 0; };
由于 FreeJudger是多线程判题,因此每个线程需要独立的日志实例,并将日志输出到多个文件中,为了方便管理,需要设计 LoggerFactory,其接口如下所示:使用时,首先要将 ILogger实例通过 registerLogger注册到 LoggerFactory中,代码如下:class JUDGER_API LoggerFactory { public: typedef std::map<OJInt32_t, ILogger*> LoggerList; typedef std::shared_ptr<LoggerList> SharedLoggerList; public: static ILogger* getLogger(const OJInt32_t loggerId); static bool registerLogger(ILogger *logger, const OJInt32_t loggerId); private: LoggerFactory(); ~LoggerFactory(); private: static SharedLoggerList loggers_; private: struct deleter { void operator()(LoggerFactory::LoggerList *pLoggerFactory) { for (LoggerFactory::LoggerList::iterator iter = LoggerFactory::loggers_->begin(); LoggerFactory::loggers_->end() != iter; ++iter) { JUDGER_SAFE_DELETE_OBJ_AND_RESET((*iter).second); } } }; };
上述代码中的 IMUST::LoggerId::AppInitLoggerId是用于程序主线程日志实例的id,对于其他线程,其定义如下:IMUST::LoggerFactory::registerLogger( new IMUST::Log4CxxLoggerImpl(GetOJString("log.cfg"), GetOJString("logger0")), IMUST::LoggerId::AppInitLoggerId);
namespace LoggerId { const OJInt32_t AppInitLoggerId = 0; const OJInt32_t Thread1LoggerId = 1; const OJInt32_t Thread2LoggerId = 2; const OJInt32_t Thread3LoggerId = 3; const OJInt32_t Thread4LoggerId = 4; const OJInt32_t Thread5LoggerId = 5; const OJInt32_t Thread6LoggerId = 6; const OJInt32_t Thread7LoggerId = 7; const OJInt32_t Thread8LoggerId = 8; // 通用ID从100以后开始编号,1-100留给线程 }
由于具体实现继承自ILogger,因此日志实例可以同时使用不同的底层实现,其实例的获取及使用如下所示:
ILogger *logger = LoggerFactory::getLogger(LoggerId::AppInitLoggerId); logger->logTrace(GetOJString("[Daemon] - IMUST::InitApp"));
log4cxx封装
这里直接给出完整代码,首先是Logger_log4cxx.h:
接下来是 Logger_log4cxx.cpp:#ifndef IMUST_OJ_LOGGER_LOG4CXX_H #define IMUST_OJ_LOGGER_LOG4CXX_H #include "Logger.h" namespace IMUST { // log4cxx的LoggerPtr无法使用前置声明,因此要做包装 class LoggerPtrWrapper; class JUDGER_API Log4CxxLoggerImpl : public ILogger { public: Log4CxxLoggerImpl(const OJString &configFileName, const OJString &logTag); virtual ~Log4CxxLoggerImpl(); virtual void logFatal(const OJString &msg) const; virtual void logError(const OJString &msg) const; virtual void logWarn(const OJString &msg) const; virtual void logInfo(const OJString &msg) const; virtual void logDebug(const OJString &msg) const; virtual void logTrace(const OJString &msg) const; private: LoggerPtrWrapper *logger_; }; } #endif // IMUST_OJ_LOGGER_LOG4CXX_H
#include "Logger_log4cxx.h" #include "../../thirdpartylib/log4cxx/log4cxx.h" #include "../../thirdpartylib/log4cxx/propertyconfigurator.h" namespace IMUST { class LoggerPtrWrapper { public: log4cxx::LoggerPtr &operator ->() { return loggerPtr_; } log4cxx::LoggerPtr &operator *() { return loggerPtr_; } log4cxx::LoggerPtr &operator =(const log4cxx::LoggerPtr loggerPtr) { loggerPtr_ = loggerPtr; return loggerPtr_; } private: log4cxx::LoggerPtr loggerPtr_; }; Log4CxxLoggerImpl::Log4CxxLoggerImpl(const OJString &configFileName, const OJString &logTag) : logger_(new LoggerPtrWrapper) { assert(!configFileName.empty() && "Config filename can not be empty"); log4cxx::PropertyConfigurator::configure(configFileName); *logger_ = log4cxx::Logger::getLogger(logTag); } Log4CxxLoggerImpl::~Log4CxxLoggerImpl() { } void Log4CxxLoggerImpl::logFatal(const OJString &msg) const { (*logger_)->fatal(msg); } void Log4CxxLoggerImpl::logError(const OJString &msg) const { (*logger_)->error(msg); } void Log4CxxLoggerImpl::logWarn(const OJString &msg) const { (*logger_)->warn(msg); } void Log4CxxLoggerImpl::logInfo(const OJString &msg) const { (*logger_)->info(msg); } void Log4CxxLoggerImpl::logDebug(const OJString &msg) const { (*logger_)->debug(msg); } void Log4CxxLoggerImpl::logTrace(const OJString &msg) const { (*logger_)->trace(msg); } }
解决log4cxx中文乱码
log4cxx在Windows平台,即使使用了Unicode进行编译,在输出中文日志时,还是会产生乱码,其实解决方法非常简单,只需在main函数开始时,调用下述代码即可:
setlocale(LC_ALL, "");
完整代码
完整代码请参考FreeJudger项目中的judgerlib/logger
欢迎加入
群117975329,验证信息CSDN。
主要维护人:
- 周宝 you_lan_hai@foxmail.com
- 马冬亮 mdl2009@vip.qq.com