C/C++编程:log4cplus使用实例

  • 使用感受:不推荐,因为库的写者没有把依赖管理好,编译起来比较困难。而且新版使用了C++17以上的特性
  • 如果真的要用的话,log4cplus-1.0.4.1这个版本比较稳定

主要类说明

  • Filter:过滤器,过滤输出消息
  • Layout : 布局器,控制输出消息的格式
  • Appender :挂接器,与布局器和过滤器紧密配合,将特定格式的消息过滤后输出到所挂接的设备终端如屏幕,文件等等)
  • Logger :记录器,保存并跟踪对象日志信息变更的实体,当你需要对一个对象进行记录时,就需要生成一个logger
  • Hierarchy:分类器,层次化的树型结构,用于对被记录信息的分类,层次中每一个节点维护一个logger的所有信息
  • LogLevel:优先权,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL

输出实例

示例一:输出到控制台

#include 
#include 
#include 

using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{

    /* step 1: Instantiate an appender object */
    SharedObjectPtr<Appender> _appnend(new ConsoleAppender);
    _appnend->setName("append for test");

    /* step 2: Instantiate a layout object */
    std::string pattern = "%d{%m/%d/%y %H:%M:%S}  - %m [%l]%n";
    std::auto_ptr<Layout> _layout(new PatternLayout(pattern));

    /* step 3: Attach the layout object to the appender */
    _appnend->setLayout(_layout);


    /* step 4: Instantiate a logger object */
    Logger _logger = Logger::getInstance("test");

    /* step 5: Attach the appender object to the logger  */
    _logger.addAppender(_appnend);

    /* step 6: Set a priority for the logger  */
    _logger.setLogLevel(ALL_LOG_LEVEL);

    /* log activity */
    LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...");
    LOG4CPLUS_WARN(_logger, "This is the SECOND log message...");
    return 0;
}

在这里插入图片描述

#include 
#include 
#include 

using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{
    /* step 1: Instantiate an appender object */
    SharedObjectPtr<Appender> _appnend(new ConsoleAppender);
    _appnend->setName("append for test");
    
    /* step 4: Instantiate a logger object */
    Logger _logger = Logger::getInstance("test");

    /* step 5: Attach the appender object to the logger  */
    _logger.addAppender(_appnend);

    /* step 6: Set a priority for the logger  */
    _logger.setLogLevel(ALL_LOG_LEVEL);

    /* log activity */
    LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...");
    LOG4CPLUS_WARN(_logger, "This is the SECOND log message...");
    return 0;
}

在这里插入图片描述

输出时还可以使用iostream模式

#include 
#include 
#include 
#include 
using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{
    /* step 1: Instantiate an appender object */
    SharedObjectPtr<Appender> _appnend(new ConsoleAppender);
    _appnend->setName("append for test");

    /* step 4: Instantiate a logger object */
    Logger _logger = Logger::getInstance("test");

    /* step 5: Attach the appender object to the logger  */
    _logger.addAppender(_appnend);

    /* step 6: Set a priority for the logger  */
    _logger.setLogLevel(ALL_LOG_LEVEL);

    /* log activity  ------------ */
    LOG4CPLUS_TRACE(_logger, "This is"  << " just a t" << "est." << std::endl);
    LOG4CPLUS_DEBUG(_logger, "This is a bool: " << true);
    LOG4CPLUS_INFO(_logger, "This is a char: " << 'x');
    LOG4CPLUS_WARN(_logger, "This is a int: " << 1000);
    LOG4CPLUS_ERROR(_logger, "This is a long(hex): " << std::hex << 100000000);
    LOG4CPLUS_FATAL(_logger, "This is a double: "  << std::setprecision(15)  << 1.2345234234);
    return 0;
}

C/C++编程:log4cplus使用实例_第1张图片

示例二:输出到文件

#include 
#include 
using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{
    SharedAppenderPtr  _append(new FileAppender("Test.log"));
    _append->setName("file log test");

    Logger _logger = Logger::getInstance("test.subtestof_filelog");
    _logger.addAppender(_append);

    int i;
    for( i = 0; i < 5; ++i )
    {
        LOG4CPLUS_DEBUG(_logger, "Entering loop #" << i << "End line #");
    }

    return 0;
}

C/C++编程:log4cplus使用实例_第2张图片

示例三:使用loglog输出日志

LogLog类实现了debug、warn、error函数用于logcplus运行时显示log4cplus自身的调试、警告和错误信息,是对输出标准的简单封装,它也可以用来进行简单的日志输出。LogLog同时提供了两个方法来进一步控制所输出的信息,其中setInternalDebugging()方法用来控制是否屏蔽输出信息中的调试信息,当输入参数为false则屏蔽,缺省设置为false。 setQuietMode()方法用来控制是否屏蔽所有输出信息,当输入参数为true则屏蔽,缺省设置为false。

#include 
#include 

using namespace log4cplus::helpers;

void printMsgs(void)
{
    std::cout << "Entering printMsgs()..." << std::endl;
    LogLog::getLogLog()->debug("This is a Debug statement...");
    LogLog::getLogLog()->warn("This is a Warning...");
    LogLog::getLogLog()->error("This is a Error...");
    std::cout << "Exiting printMsgs()..." << std::endl << std::endl;
}

int main()
{
    printMsgs();
    return 0;
}

C/C++编程:log4cplus使用实例_第3张图片

#include 
#include 

using namespace log4cplus::helpers;

void printMsgs(void)
{
    std::cout << "Entering printMsgs()..." << std::endl;
    LogLog::getLogLog()->debug("This is a Debug statement...");
    LogLog::getLogLog()->warn("This is a Warning...");
    LogLog::getLogLog()->error("This is a Error...");
    std::cout << "Exiting printMsgs()..." << std::endl << std::endl;
}

int main()
{
    LogLog::getLogLog()->setInternalDebugging(true);
    printMsgs();
    return 0;
}

C/C++编程:log4cplus使用实例_第4张图片

#include 
#include 

using namespace log4cplus::helpers;

void printMsgs(void)
{
    std::cout << "Entering printMsgs()..." << std::endl;
    LogLog::getLogLog()->debug("This is a Debug statement...");
    LogLog::getLogLog()->warn("This is a Warning...");
    LogLog::getLogLog()->error("This is a Error...");
    std::cout << "Exiting printMsgs()..." << std::endl << std::endl;
}

int main()
{
    LogLog::getLogLog()->setQuietMode(true);
    printMsgs();
    return 0;
}

在这里插入图片描述

日志输出宏

log4cplus在头文件loggingmacros.h中提供了以下的日志输出宏:

LOG4CPLUS_TRACE_METHOD(logger, logEvent)  

LOG4CPLUS_TRACE(logger, logEvent)
LOG4CPLUS_TRACE_STR(logger, logEvent)

LOG4CPLUS_DEBUG(logger, logEvent)
LOG4CPLUS_DEBUG_STR(logger, logEvent)

LOG4CPLUS_INFO(logger, logEvent)
LOG4CPLUS_INFO_STR(logger, logEvent)

LOG4CPLUS_WARN(logger, logEvent)
LOG4CPLUS_WARN_STR(logger, logEvent)

LOG4CPLUS_ERROR(logger, logEvent)
LOG4CPLUS_ERROR_STR(logger, logEvent)

LOG4CPLUS_FATAL(logger, logEvent)
LOG4CPLUS_FATAL_STR(logger, logEvent)

其中logger 为Logger实例名称,logEvent为日志内容。由于log4cplus选用C++的流机制进行日志输出,因此为了区分包含<<运算符和不包含<<运算符的日志内容,分别提供了LOG4CPLUS_XXXX和LOG4CPLUS_XXXX_STR两种日志输出宏。 另外,日志输出宏LOG4CPLUS_TRACE_METHOD主要用来跟踪方法的调用轨迹。

输出格式控制

log4cplus通过布局器(Layouts)来控制输出的格式,log4cplus提供了三种类型的Layouts,分别是SimpleLayout、PatternLayout、和TTCCLayout。

SimpleLayout

一种简单格式的布局器,在输出的原始信息之前加上LogLevel和一个"-",如果初始化时没有将布局器附加到挂接器,则默认使用SimpleLayout。

#include 
#include 
#include 

using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{
    SharedObjectPtr<Appender> _append(new ConsoleAppender);
    _append->setName("append for test");

    std::auto_ptr<Layout> _layout(new log4cplus::SimpleLayout());
    _append->setLayout( _layout );


    Logger _logger = Logger::getInstance("test");
    _logger.addAppender(_append);


    _logger.setLogLevel(ALL_LOG_LEVEL);

    LOG4CPLUS_DEBUG(_logger, "This is the simple formatted log message...");
    return 0;
}

在这里插入图片描述

PatternLayout

一种有词法分析功能的模式布局器,类似于C语言的printf()函数,能够对预定义的转换标识符(conversion specifiers)进行解析,转换成特定格式输出。

PatterLayout支持的转换标识符主要包括:

  • “%%”,转义为%, 即,std::string pattern = “%%” 时输出"%"

  • “%c”,输出logger名称,比如std::string pattern ="%c" 时输出: “test_logger.subtest”, 也可以控制logger名称的显示层次,比如"%c{1}“时输出"test_logger”,其中数字表示层次。

  • “%D”,显示本地时间,当std::string pattern ="%D" 时输出:“2004-10-16 18:55:45”,%d显示标准时间,所以当std::string pattern ="%d" 时输出 “2004-10-16 10:55:45” (因为北京时间位于东8区,差8个小时)。 可以通过%d{…}定义更详细的显示格式,比如%d{%H:%M:%s}表示要显示小时:分钟:秒。大括号中可显示的预定义标识符如下:

    • %a – 表示礼拜几,英文缩写形式,比如"Fri"
      %A – 表示礼拜几,比如"Friday"
      %b – 表示几月份,英文缩写形式,比如"Oct"
      %B – 表示几月份,“October”
      %c – 标准的日期+时间格式,如 “Sat Oct 16 18:56:19 2004”
      %d – 表示今天是这个月的几号(1-31)“16”
      %H – 表示当前时刻是几时(0-23),如 “18”
      %I – 表示当前时刻是几时(1-12),如 “6”
      %j – 表示今天是哪一天(1-366),如 “290”
      %m – 表示本月是哪一月(1-12),如 “10”
      %M – 表示当前时刻是哪一分钟(0-59),如 “59”
      %p – 表示现在是上午还是下午, AM or PM
      %q – 表示当前时刻中毫秒部分(0-999),如 “237”
      %Q – 表示当前时刻中带小数的毫秒部分(0-999.999),如 “430.732”
      %S – 表示当前时刻的多少秒(0-59),如 “32”
      %U – 表示本周是今年的第几个礼拜,以周日为第一天开始计算(0-53),如 “41”
      %w – 表示礼拜几,(0-6, 礼拜天为0),如 “6”
      %W – 表示本周是今年的第几个礼拜,以周一为第一天开始计算(0-53),如 “41”
      %x – 标准的日期格式,如 “10/16/04”
      %X – 标准的时间格式,如 “19:02:34”
      %y – 两位数的年份(0-99),如 “04”
      %Y – 四位数的年份,如 “2004”
      %Z – 时区名,比如 “GMT”
  • “%F”,输出当前记录器所在的文件名称,比如std::string pattern ="%F" 时输出: “main.cpp”

  • “%L”,输出当前记录器所在的文件行号,比如std::string pattern ="%L" 时输出: “51”

  • “%l”,输出当前记录器所在的文件名称和行号,比如std::string pattern ="%L" 时输出"main.cpp:51"

  • “%m”,输出原始信息,比如std::string pattern ="%m" 时输出: “teststr”,即上述代码中LOG4CPLUS_DEBUG的第二个参数,这种实现机制可以确保原始信息被嵌入到带格式的信息中

  • “%n”,换行符,没什么好解释的

  • “%p”,输出LogLevel,比如std::string pattern ="%p" 时输出: “DEBUG”

  • “%t”,输出记录器所在的线程ID,比如std::string pattern ="%t" 时输出: “1075298944”

  • “%x”,嵌套诊断上下文NDC (nested diagnostic context) 输出,从堆栈中弹出上下文信息,NDC可以用对不同源的log信息(同时地)交叉输出进行区分

  • 格式对齐,比如std::string pattern ="%-10m"时表示左对齐,宽度是10,此时会输出"teststr “,当然其它的控制字符也可以相同的方式来使用,比如”%-12d","%-5p"等等

#include 
#include 
#include 

using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{
    SharedObjectPtr<Appender> _append(new ConsoleAppender);
    _append->setName("append for test");

    std::string pattern = "%d{%m/%d/%y %H:%M:%S}  - %m [%l]%n";
    std::auto_ptr<Layout> _layout(new PatternLayout(pattern));
    _append->setLayout( _layout );


    Logger _logger = Logger::getInstance("test");
    _logger.addAppender(_append);


    _logger.setLogLevel(ALL_LOG_LEVEL);

    LOG4CPLUS_DEBUG(_logger, "teststr");
    return 0;
}

在这里插入图片描述

TTCCLayout

是在PatternLayout基础上发展的一种缺省的带格式输出的布局器, 其格式由时间,线程ID,Logger和NDC 组成(consists of time, thread, Logger and nested diagnostic context information, hence the name),因而得名

#include 
#include 
#include 

using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{
    SharedObjectPtr<Appender> _append(new ConsoleAppender);
    _append->setName("append for test");


 //   std::auto_ptr _layout(new TTCCLayout(false));
    std::auto_ptr<Layout> _layout(new TTCCLayout(true));  // 参数好像没有起作用,可能是版本问题
    _append->setLayout( _layout );


    Logger _logger = Logger::getInstance("test");
    _logger.addAppender(_append);



    LOG4CPLUS_DEBUG(_logger, "teststr");
    return 0;
}

在这里插入图片描述

输出重定向

重定向到控制台

log4cplus默认将输出到控制台,提供ConsoleAppender用于操作。

重定向到文件

log4cplus提供了三个类用于文件操作,它们是FileAppender类、RollingFileAppender类、DailyRollingFileAppender类

FileAppender

实现了基本的文件操作功能,构造函数如下:

FileAppender ::FileAppender(
                         const log4cplus::tstring& filename,
                         LOG4CPLUS_OPEN_MODE_TYPE mode = LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc,
                         bool immediateFlush = true);

参数:

  • filename : 文件名
  • mode : 文件类型,可选择的文件类型包括app、ate、binary、in、out、trunc,实际上只是对stl的一个简单包装。缺省是trunc,表示将先前文件删除。
  • immediateFlush : 缓冲刷新标志,如果为true表示每向文件写一条记录就刷新一次缓存,否则直到FileAppender被关闭或文件缓存已满才更新文件,一般是要设置true的,比如你往文件写的过程中出现了错误(如程序非正常退出),即使文件没有正常关闭也可以保证程序终止时刻之前的所有 记录都会被正常保存。

RollingFileAppender

实现可以滚动转储的文件操作功能,构造函数如下:

RollingFileAppender::RollingFileAppender(const log4cplus::tstring& filename,
                                                    long maxFileSize,
                                                    int maxBackupIndex,
                                                    bool immediateFlush)

参数:

  • filename : 文件名
  • maxFileSize : 文件的最大尺寸
  • maxBackupIndex : 最大记录文件数
  • immediateFlush : 缓冲刷新标志

RollingFileAppender类可以根据你预先设定的大小来决定是否转储,当超过该大小,后续log信息会另存到新文件中,除了定义每个记录文件的大小之外,你还要确定在RollingFileAppender类对象构造时最多需要多少个这样的记录文件(maxBackupIndex+1),当存储的文件数目超过maxBackupIndex+1时,会删除最早生成的文件,保证整个文件数目等于maxBackupIndex+1。然后继续记录:

#include 
#include 
#include 

using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{
    // log4cplus:WARN RollingFileAppender: MaxFileSize property value is too small. Resetting to 204800.
    SharedAppenderPtr _append(new RollingFileAppender("Test.log",  5*1024, 5));
    _append->setName("file test");
    _append->setLayout( std::auto_ptr<Layout>(new TTCCLayout()) );
    Logger::getRoot().addAppender(_append);

    Logger root = Logger::getRoot();
    Logger test = Logger::getInstance("test");
    Logger subTest = Logger::getInstance("test.subtest");

    for(int i=0; i  < 100; i++  ){
            NDCContextCreator _context("loop");
            LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i);
    }
    return 0;
}

运行后会产生6个输出文件,Test.log、Test.log.1、Test.log.2、Test.log.3、Test.log.4、Test.log.5其中Test.log存放着最新写入的信息,而最后一个文件中并不包含第一个写入信息,说明已经被不断更新了。

需要指出的是,这里除了Test.log之外,每个文件的大小都是200K,而不是我们想像中的5K,这是因为log4cplus中隐含定义了文件的最小尺寸是200K,只有大于200K的设置才生效,<= 200k的设置都会被认为是200K。

DailyRollingFileAppender

实现根据频度来决定是否转储的文件转储功能,构造函数如下:

DailyRollingFileAppender::DailyRollingFileAppender(const log4cplus::tstring& filename,
                                             DailyRollingFileSchedule schedule,
                                             bool immediateFlush,
                                             int maxBackupIndex)

参数:

  • filename : 文件名
  • schedule : 存储频度
  • immediateFlush : 缓冲刷新标志
  • maxBackupIndex : 最大记录文件数

DailyRollingFileAppender类可以根据你预先设定的频度来决定是否转储,当超过该频度,后续log信息会另存到新文件中,这里的频度包括:MONTHLY(每月)、WEEKLY(每周)、DAILY(每日)、TWICE_DAILY(每两天)、HOURLY(每时)、MINUTELY(每分)。maxBackupIndex的含义同上所述

#include 
#include 
#include 

using namespace log4cplus;
using namespace log4cplus::helpers;

int main()
{

    SharedAppenderPtr   _append(new DailyRollingFileAppender("Test.log", MINUTELY, true, 5));
    _append->setName("file test");
    _append->setLayout( std::auto_ptr<Layout>(new TTCCLayout()) );
    Logger::getRoot().addAppender(_append);

    Logger root = Logger::getRoot();
    Logger test = Logger::getInstance("test");
    Logger subTest = Logger::getInstance("test.subtest");

    for(int i=0; i  < 100; i++  ){
            NDCContextCreator _context("loop");
            LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i);
    }
    return 0;
}

输出结果:

  • 运行后会以分钟为单位,分别生成名为Test.log.2004-10-17-03-03、Test.log.2004-10-17-03-04和Test.log.2004-10-17-03-05这样的文件。
  • 需要指出的是这里的"频度"并不是你写入文件的速度,其实是否转储的标准并不依赖你写入文件的速度,而是依赖于写入的那一时刻是否满足了频度条件,即是否超过了以分钟、小时、周、月为单位的时间刻度,如果超过了就另存。

重定向到远程服务器

log4cplus提供了SocketAppender,实现了C/S方式的日志记录,用于支持重定向到远程服务器。

客户端程序需要做的工作

(1) 定义一个SocketAppender类型的挂接器

SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));

(2) 把该挂接器加入到logger中

Logger::getRoot().addAppender(_append);

(3) SocketAppender类型不需要Layout, 直接调用宏就可以将信息发往loggerServer了

LOG4CPLUS_INFO(Logger::getRoot(), "This is a test: ")

注意这里对宏的调用其实是调用了SocketAppender::append(),里面有一个数据传输约定,即先发送一个后续数据的总长度,然后再发送实际的数据:

    SocketBuffer buffer = convertToBuffer(event, serverName);
    SocketBuffer msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE);

    msgBuffer.appendSize_t(buffer.getSize());
    msgBuffer.appendBuffer(buffer);

服务器端程序需要做的工作

(1) 定义一个ServerSocket

   ServerSocket serverSocket(port);

(2) 调用accept函数创建一个新的socket与客户端连接

   Socket sock = serverSocket.accept();

(3) 此后即可用该sock进行数据read/write了,形如

	SocketBuffer msgSizeBuffer(sizeof(unsigned int));
  
  if(!clientsock.read(msgSizeBuffer))
  {
      return;
  }   
  
  unsigned int msgSize = msgSizeBuffer.readInt();
  
  SocketBuffer buffer(msgSize);
  
  if(!clientsock.read(buffer))
  {
      return;
	}

(4) 为了将读到的数据正常显示出来,需要将SocketBuffer存放的内容转换成InternalLoggingEvent格式:

log4cplus::spi::InternalLoggingEvent event = readFromBuffer(buffer);

然后输出:

Logger logger = Logger::getInstance(event.getLoggerName());
logger.callAppenders(event);

注意read/write是按照阻塞方式实现的,意味着对其调用直到满足了所接收或发送的个数才返回。

完整代码

以下是服务器端代码。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

using namespace std;
using namespace log4cplus;
using namespace log4cplus::helpers;
using namespace log4cplus::thread;

namespace loggingserver{
    class ClientThread : public AbstractThread{
    public:
        ClientThread(Socket clientsock)
                : clientsock(clientsock)
        {
            cout << "Received a client connection!!!!" << endl;
        }

        ~ClientThread()
        {
            cout << "Client connection closed." << endl;
        }


        virtual void run();
    private:
        Socket clientsock;
    };
}

void
loggingserver::ClientThread::run(){
    while (1){
        if(!clientsock.isOpen()){
            return;
        }

        SocketBuffer msgSizeBuffer(sizeof(unsigned int));
        if(!clientsock.read(msgSizeBuffer)){
            return;
        }

        unsigned int msgSize = msgSizeBuffer.readInt();

        SocketBuffer buffer(msgSize);
        if(!clientsock.read(buffer)){
            return;
        }

        spi::InternalLoggingEvent event = readFromBuffer(buffer);
        Logger logger = Logger::getInstance(event.getLoggerName());
        logger.callAppenders(event);
    }
}

int main(int argc, char** argv)
{
    if(argc < 3) {
        cout << "Usage: port config_file" << endl;
        return 1;
    }
    int port = atoi(argv[1]);
    tstring configFile = LOG4CPLUS_C_STR_TO_TSTRING(argv[2]);
    PropertyConfigurator config(configFile);
    config.configure();

    ServerSocket serverSocket(port);
    while(1) {
        loggingserver::ClientThread *thr =
                new loggingserver::ClientThread(serverSocket.accept());
        thr->start();
    }

    return 0;
}

以下是客户端代码。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace log4cplus;

int main(int argc, char** argv)
{
    
    tstring serverName = (argc > 1 ? LOG4CPLUS_C_STR_TO_TSTRING(argv[1]) : tstring());
    tstring host = LOG4CPLUS_TEXT("127.0.0.1");
    SharedAppenderPtr append_1(new SocketAppender(host, 9998, serverName));
    append_1->setName( LOG4CPLUS_TEXT("First") );
    Logger::getRoot().addAppender(append_1);

    Logger root = Logger::getRoot();
    Logger test = Logger::getInstance( LOG4CPLUS_TEXT("socket.test") );

    LOG4CPLUS_DEBUG(root,    "This is"
            << " a reall"
            << "y long message." << endl
            << "Just testing it out" << endl
            << "What do you think?");
    test.setLogLevel(NOT_SET_LOG_LEVEL);
    LOG4CPLUS_DEBUG(test, "This is a bool: " << true);
    LOG4CPLUS_INFO(test, "This is a char: " << 'x');
    LOG4CPLUS_INFO(test, "This is a short: " << (short)-100);
    LOG4CPLUS_INFO(test, "This is a unsigned short: " << (unsigned short)100);
    LOG4CPLUS_INFO(test, "This is a int: " << (int)1000);
    LOG4CPLUS_INFO(test, "This is a unsigned int: " << (unsigned int)1000);
    LOG4CPLUS_INFO(test, "This is a long(hex): " << hex << (long)100000000);
    LOG4CPLUS_INFO(test, "This is a unsigned long: " << (unsigned long)100000000);
    LOG4CPLUS_WARN(test, "This is a float: " << (float)1.2345);
    LOG4CPLUS_ERROR(test, "This is a double: "
            << setprecision(15)
            << (double)1.2345234234);
    LOG4CPLUS_FATAL(test, "This is a long double: "
            << setprecision(15)
            << (long double)123452342342.342);

    return 0;
}

嵌入诊断上下文NDC

log4cplus中的嵌入诊断上下文(Nested Diagnostic Context),即NDC。对log系统而言,当输入源可能不止一个,而只有一个输出时,往往需要分辩所要输出消息的来源,比如服务器处理来自不同客户端的消息时就需要作此判断,NDC可以为交错显示的信息打上一个标记(stamp),使得辨认工作看起来比较容易些。这个标记是线程特有的,利用了线程局部存储机制,称为线程私有数据(Thread-Specific Data,或TSD)。相关定义如下,包括定义、初始化、获取、设置和清除操作:

linux pthread
#define LOG4CPLUS_THREAD_LOCAL_TYPE pthread_key_t*
#define LOG4CPLUS_THREAD_LOCAL_INIT ::log4cplus::thread::createPthreadKey()
#define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) pthread_getspecific(*key)
#defineLOG4CPLUS_SET_THREAD_LOCAL_VALUE(key,value)  \
  pthread_setspecific(*key, value)
#define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) pthread_key_delete(*key)

win32
#define LOG4CPLUS_THREAD_LOCAL_TYPE DWORD
#define LOG4CPLUS_THREAD_LOCAL_INIT TlsAlloc()
#define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) TlsGetValue(key)
#define LOG4CPLUS_SET_THREAD_LOCAL_VALUE( key, value ) \
       TlsSetValue(key, static_cast(value))
#define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) TlsFree(key)

使用起来比较简单,在某个线程中:

NDC& ndc = log4cplus::getNDC();
ndc.push("ur ndc string");
LOG4CPLUS_DEBUG(logger, "this is a NDC test");

    ... ...
   
ndc.pop();
   
    ... ...
   
LOG4CPLUS_DEBUG(logger, "There should be no NDC...");
ndc.remove();

输出结果(当设定输出格式为TTCCLayout时):

10-21-04 21:32:58, [3392] DEBUG test  - this is a NDC test
10-21-04 21:32:58, [3392] DEBUG test <> - There should be no NDC...

也可以在自定义的输出格式中使用NDC(用%x) ,比如:

 ... ...
   
    std::string pattern = "NDC:[%x]  - %m %n";
    std::auto_ptr _layout(new PatternLayout(pattern));

    ... ...
   
    LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...")
    NDC& ndc = log4cplus::getNDC();
    ndc.push("ur ndc string");
    LOG4CPLUS_WARN(_logger, "This is the SECOND log message...")
    ndc.pop();
    ndc.remove();
   
    ... ...

输出结果:

NDC:[]  - This is the FIRST log message...
NDC:[ur ndc string]  - This is the SECOND log message...

另外一种更简单的使用方法是在线程中直接用NDCContextCreator:

NDCContextCreator _first_ndc("ur ndc string");
LOG4CPLUS_DEBUG(logger, "this is a NDC test")

不必显式地调用push/pop了,而且当出现异常时,能够确保push与pop的调用是匹配的。

输出过滤

利用日志级别进行输出过滤

日志级别管理

log4cplus将输出的log信息按照LogLevel(从低到高)分为:

NOT_SET_LOG_LEVEL ( -1)      接受缺省的LogLevel,如果有父logger则继承它的LogLevel
ALL_LOG_LEVEL (0)             开放所有log信息输出
TRACE_LOG_LEVEL (0)           开放trace信息输出(即ALL_LOG_LEVEL)
DEBUG_LOG_LEVEL(10000)       开放debug信息输出
INFO_LOG_LEVEL (20000)         开放info信息输出
WARN_LOG_LEVEL (30000)       开放warning信息输出
ERROR_LOG_LEVEL(40000)       开放error信息输出
FATAL_LOG_LEVEL (50000)       开放fatal信息输出
OFF_LOG_LEVEL (60000)          关闭所有log信息输出

在log4cplus中,所有logger都通过一个层次化的结构(其实内部是hash表)来组织的,有一个Root级别的logger,可以通过以下方法获取:

Logger root = Logger::getRoot();

用户定义的logger都有一个名字与之对应,比如:

 Logger test = Logger::getInstance("test");

可以定义该logger的子logger:

 Logger subTest = Logger::getInstance("test.subtest");   

注意Root级别的logger只有通过getRoot方法获取,Logger::getInstance(“root”)获得的是它的子对象而已。有了这些具有父子关系的logger之后可分别设置其LogLevel,比如:

root.setLogLevel( ... );
Test.setLogLevel( ... );
subTest.setLogLevel( ... );

各个logger可以通过setLogLevel设置自己的优先级,当某个logger的LogLevel设置成NOT_SET_LOG_LEVEL时,该logger会继承父logger的优先级,另外,如果定义了重名的多个logger, 对其中任何一个的修改都会同时改变其它logger。

利用日志级别进行输出过滤

log4cplus支持编译时候和运行时刻利用日志级别进行输出过滤。编译时刻通过如下的预定义变量进行过滤:

	 #define LOG4CPLUS_DISABLE_FATAL
   #define LOG4CPLUS_DISABLE_WARN
   #define LOG4CPLUS_DISABLE_ERROR
   #define LOG4CPLUS_DISABLE_INFO
   #define LOG4CPLUS_DISABLE_DEBUG
   #define LOG4CPLUS_DISABLE_TRACE

运行时刻的过滤则通过使用Logger的setLogLevel设置日志级别进行过滤。

实例如下:

#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include 

using namespace std;
using namespace log4cplus;

int main()
{
    SharedAppenderPtr _append(new ConsoleAppender());
    _append->setName("test");
    Logger::getRoot().addAppender(_append);
    Logger root = Logger::getRoot();

    Logger test = Logger::getInstance("test");
    Logger subTest = Logger::getInstance("test.subtest");
    LogLevelManager& llm = getLogLevelManager();

    cout << endl << "Before Setting, Default LogLevel" << endl;
    LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root,"test.subtest:" << llm.toString(subTest.getChainedLogLevel()));

    cout << endl << "Setting test.subtest to WARN" << endl;
    subTest.setLogLevel(WARN_LOG_LEVEL);
    LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));

    cout << endl << "Setting test to TRACE" << endl;
    test.setLogLevel(TRACE_LOG_LEVEL);
    LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()));

    cout << endl << "Setting test.subtest to NO_LEVEL" << endl;
    subTest.setLogLevel(NOT_SET_LOG_LEVEL);;
    LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()) << '\n');

    cout << "create a logger test_bak, named \"test_\", too. " << endl;
    Logger test_bak = Logger::getInstance("test");
    cout << "Setting test to INFO, so test_bak also be set to INFO" << endl;
    test.setLogLevel(INFO_LOG_LEVEL);
    LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel()));
    LOG4CPLUS_FATAL(root, "test_bak: " << llm.toString(test_bak.getChainedLogLevel()));

    return 0;
}

C/C++编程:log4cplus使用实例_第5张图片
还可以运行时利用日志级别进行输出过滤

#include "log4cplus/logger.h"
#include "log4cplus/consoleappender.h"
#include "log4cplus/loglevel.h"
#include 

using namespace std;
using namespace log4cplus;

void ShowMsg(void)
{
    LOG4CPLUS_TRACE(Logger::getRoot(),"info");
    LOG4CPLUS_DEBUG(Logger::getRoot(),"info");
    LOG4CPLUS_INFO(Logger::getRoot(),"info");
    LOG4CPLUS_WARN(Logger::getRoot(),"info");
    LOG4CPLUS_ERROR(Logger::getRoot(),"info");
    LOG4CPLUS_FATAL(Logger::getRoot(),"info");
}

int main()
{
    SharedAppenderPtr _append(new ConsoleAppender());
    _append->setName("test");
    _append->setLayout(std::auto_ptr<Layout>(new TTCCLayout()));
    Logger root = Logger::getRoot();
    root.addAppender(_append);

    cout << endl << "all-log allowed" << endl;
    root.setLogLevel(ALL_LOG_LEVEL);
    ShowMsg();

    cout << endl << "trace-log and above allowed" << endl;
    root.setLogLevel(TRACE_LOG_LEVEL);
    ShowMsg();

    cout << endl << "debug-log and above allowed" << endl;
    root.setLogLevel(DEBUG_LOG_LEVEL);
    ShowMsg();

    cout << endl << "info-log and above allowed" << endl;
    root.setLogLevel(INFO_LOG_LEVEL);
    ShowMsg();

    cout << endl << "warn-log and above allowed" << endl;
    root.setLogLevel(WARN_LOG_LEVEL);
    ShowMsg();

    cout << endl << "error-log and above allowed" << endl;
    root.setLogLevel(ERROR_LOG_LEVEL);
    ShowMsg();

    cout << endl << "fatal-log and above allowed" << endl;
    root.setLogLevel(FATAL_LOG_LEVEL);
    ShowMsg();

    cout << endl << "log disabled" << endl;
    root.setLogLevel(OFF_LOG_LEVEL);
    ShowMsg();

    return 0;
}

C/C++编程:log4cplus使用实例_第6张图片

利用脚本配置进行输出过滤

由于log4cplus脚本配置中可以设置日志的级别、过滤器Filter,因此它也是进行输出过滤的一种很好的选择

LogLog的输出过滤

Loglog可以使用setInternalDebugging()方法用来控制是否屏蔽输出信息中的调试信息,当输入参数为false则屏蔽,缺省设置为false。 另外方法setQuietMode()方法用来控制是否屏蔽所有输出信息,当输入参数为true则屏蔽,缺省设置为false

脚本配置

除了通过程序实现对log环境的配置之外,log4cplus通过PropertyConfigurator类实现了基于脚本配置的功能。通过脚本可以完成对logger、appender和layout的配置,因此可以解决怎样输出,输出到哪里的问题。

基本配置

基本配置语法主要针对包括rootLogger和non-root logger。

根Logger的配置

语法:

   log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...

非根Logger的配置

语法:

log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...

说明:INHERITED表示继承父Logger的日志级别。

高级配置

Appender配置

语法:

log4cplus.appender.appenderName=fully.qualified.name.of.appender.class

举例:

log4cplus.appender.append_1=log4cplus::ConsoleAppender
log4cplus.appender.append_2=log4cplus::FileAppender
log4cplus.appender.append_3=log4cplus::RollingFileAppender
log4cplus.appender.append_4=log4cplus::DailyRollingFileAppender
log4cplus.appender.append_4=log4cplus::SocketAppender

Filter配置

Appender可以附加Filter组成的链表,如果Filter链中存在过滤器Filter, log4cplus在输出日志之前将调用链表中Filter的过滤方法decide(),根据该方法的返回值决定是否过滤该输出日志。

语法:

log4cplus.appender.appenderName.Filter.FilterNumber=fully.qualified.name.of.Filter.class
log4cplus.appender.appenderName.Filter.FilterNumber.FilterCondition=value.of.FilterCondition

举例:

log4cplus.appender.append_1.filters.1=log4cplus::spi::LogLevelMatchFilter
log4cplus.appender.append_1.filters.1.LogLevelToMatch=TRACE
log4cplus.appender.append_1.filters.1.AcceptOnMatch=true

目前log4plus提供的过滤器包括DenyAllFilter 、LogLevelMatchFilter、LogLevelRangeFilter、和StringMatchFilter。

LogLevelMatchFilter根据特定的日志级别进行过滤。

  • 过滤条件包括LogLevelToMatch和AcceptOnMatch(true|false)
  • 只有当日志的LogLevel值与LogLevelToMatch相同,且AcceptOnMatch为true时才会匹配。

LogLevelRangeFilter根据根据日志级别的范围进行过滤。

  • 过滤条件包括LogLevelMin、LogLevelMax和AcceptOnMatch
  • 只有当日志的LogLevel在LogLevelMin、LogLevelMax之间同时AcceptOnMatch为true时才会匹配。

StringMatchFilter根据日志内容是否包含特定字符串进行过滤。

  • 过滤条件包括StringToMatch和AcceptOnMatch,只有当日志包含StringToMatch字符串 且AcceptOnMatch为true时会匹配。
    DenyAllFilter则过滤掉所有消息。
  • 过滤条件处理机制类似于Linux中IPTABLE的Responsibility chain机制,(即先deny、再allow)不过执行顺序刚好相反,后写的条件会被先执行,比如:
log4cplus.appender.append_1.filters.1=log4cplus::spi::LogLevelMatchFilter
log4cplus.appender.append_1.filters.1.LogLevelToMatch=TRACE
log4cplus.appender.append_1.filters.1.AcceptOnMatch=true
#log4cplus.appender.append_1.filters.2=log4cplus::spi::DenyAllFilter

会首先执行filters.2的过滤条件,关闭所有过滤器,然后执行filters.1,仅匹配TRACE信息。

Layout配置

可以选择不设置、TTCCLayout、或PatternLayout,如果不设置,会输出SimpleLayout格式的日志。

设置TTCCLayout的语法:

log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout

设置PatternLayout的语法:

log4cplus.appender.append_1.layout=log4cplus::PatternLayout

举例:

log4cplus.appender.append_1.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p - %m%n

实例

脚本方式使用起来非常简单,只要首先加载配置即可(urconfig.properties是自行定义的配置文件):

PropertyConfigurator::doConfigure("urconfig.properties");

看个例子。以下是urconfig.properties示例脚本配置内容。

/*
 *    urconfig.properties
 */
log4cplus.rootLogger=TRACE, ALL_MSGS, TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS

log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.ALL_MSGS.File=all_msgs.log
log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout

log4cplus.appender.TRACE_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.TRACE_MSGS.File=trace_msgs.log
log4cplus.appender.TRACE_MSGS.layout=log4cplus::TTCCLayout
log4cplus.appender.TRACE_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter
log4cplus.appender.TRACE_MSGS.filters.1.LogLevelToMatch=TRACE
log4cplus.appender.TRACE_MSGS.filters.1.AcceptOnMatch=true
log4cplus.appender.TRACE_MSGS.filters.2=log4cplus::spi::DenyAllFilter

log4cplus.appender.DEBUG_INFO_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.DEBUG_INFO_MSGS.File=debug_info_msgs.log
log4cplus.appender.DEBUG_INFO_MSGS.layout=log4cplus::TTCCLayout
log4cplus.appender.DEBUG_INFO_MSGS.filters.1=log4cplus::spi::LogLevelRangeFilter
log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMin=DEBUG
log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMax=INFO
log4cplus.appender.DEBUG_INFO_MSGS.filters.1.AcceptOnMatch=true
log4cplus.appender.DEBUG_INFO_MSGS.filters.2=log4cplus::spi::DenyAllFilter

log4cplus.appender.FATAL_MSGS=log4cplus::RollingFileAppender
log4cplus.appender.FATAL_MSGS.File=fatal_msgs.log
log4cplus.appender.FATAL_MSGS.layout=log4cplus::TTCCLayout
log4cplus.appender.FATAL_MSGS.filters.1=log4cplus::spi::StringMatchFilter
log4cplus.appender.FATAL_MSGS.filters.1.StringToMatch=FATAL
log4cplus.appender.FATAL_MSGS.filters.1.AcceptOnMatch=true
log4cplus.appender.FATAL_MSGS.filters.2=log4cplus::spi::DenyAllFilter

以下是示例代码。

/*
 *    main.cpp
 */
#include 
#include 
#include 

using namespace log4cplus;

static Logger logger = Logger::getInstance("log");

void printDebug()
{
    LOG4CPLUS_TRACE_METHOD(logger, "::printDebug()");
    LOG4CPLUS_DEBUG(logger, "This is a DEBUG message");
    LOG4CPLUS_INFO(logger, "This is a INFO message");
    LOG4CPLUS_WARN(logger, "This is a WARN message");
    LOG4CPLUS_ERROR(logger, "This is a ERROR message");
    LOG4CPLUS_FATAL(logger, "This is a FATAL message");
}
int main()
{
    Logger root = Logger::getRoot();
    PropertyConfigurator::doConfigure("urconfig.properties");
    printDebug();

    return 0;
} 

你可能感兴趣的:(C++,leetcode,C++)