C++開源跨平臺OJ系統判題核心FreeJudger(二)——logger設計

凝霜發表於2013-02-08

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,其介面如下所示:

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);
            }
        }
    };
};
使用時,首先要將ILogger例項通過registerLogger註冊到LoggerFactory中,程式碼如下:

        IMUST::LoggerFactory::registerLogger(
            new IMUST::Log4CxxLoggerImpl(GetOJString("log.cfg"), GetOJString("logger0")), 
            IMUST::LoggerId::AppInitLoggerId);
上述程式碼中的IMUST::LoggerId::AppInitLoggerId是用於程式主執行緒日誌例項的id,對於其他執行緒,其定義如下:

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:

#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
接下來是Logger_log4cxx.cpp:

#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

專案主頁  https://github.com/NsLib/FreeJudger

歡迎加入

群117975329,驗證資訊CSDN

主要維護人:

  • 周寶      you_lan_hai@foxmail.com
  • 馬冬亮  mdl2009@vip.qq.com

相關文章