C++开源跨平台OJ系统判题核心FreeJudger(二)——logger设计

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

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
BNUEP Offline Judge 北京师范大学珠海分校离线评测系统是在具备题目测试数据的情况下,能无联网自动评测ACM/ICPC模式的源代码评测系统(即本地测试工具、评测机)。它主要有以下功能(所有的功能都无需联网,在本机即可实现): *评测核心功能: 基本具备Online Judge的判题核心功能,如编译代码、内存限定,时间限定,获取代码长度等; *支持多种语言: 1.0 Beta2版本支持C/C++、Pascal、C#、JAVA; *出题模式 可以在有标准输入数据和标准程序的情况下,由系统产生标准输出数据,并可批量保存,同时自动命名标准输出数据的后缀; *文本高亮对比 在判题后,可以直接在本系统中将自己的程序输出和标准输出进行高亮的文本差异对比,操作类似于一些文本对比软件,在一定程度上可以较方便地发现WA代码的出错细节; *支持不限时执行代码 这个功能可以在一定程度上检测TLE代码的算法是否正确的,当然,不能是跑一天都没跑出来的程序; *打包与加密测试数据 使用加密后的数据可以正常判题,但不显示标准输出。这个功能是为了弥补放出去给别人评测的测试数据是明文的缺陷。加密之后评测方就看不到测试数据。这样就既可以实现离线评测,又可以实现Online Judge上的对测试数据屏蔽; ACM-ICPC简介: ACM国际大学生程序设计竞赛(简称ACM-ICPC)是由国际计算机界具有悠久历史的权威性组织ACM学会(Association for Computing Machinery)主办,是世界上公认的规模最大、水平最高、参与人数最多的大学生程序设计竞赛,其宗旨是使大学生能通过计算机充分展示自己分析问题和解决问题的能力。 ACM-ICPC的每一道题,都具备题目、需求描述、输入格式描述、输出格式描述、样例输入和样例输出共六大信息,有些题目还有一定的提示。此外,裁判还额外存储了关于该题的一组或多组对选手屏蔽的标准输入和标准输出数据,这些测试数据已经经过验证符合题意要求。当用户提交一道题目的源码之后,裁判会将该源码放入评测系统中编译运行,并使用标准输入作为用户程序的输入,然后获取用户程序的输出,接着,将用户程序输出和标准输出比较,最后返回给用户一个评判结果。评判结果包括:Accepted(测试通过)、Compile Error(编译失败)、Memory Limit Exceed(内存超出限制)、Presentation Error(格式错误)、Runtime Error(运行时错误,可能是数组越界,改写只读的内存,除零,栈或堆溢出等错误)、Time Limit Exceed(时间超出限制)、Wrong Answer(答案错误)等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值