log4cplus学习笔记(二 )

log4cplus在很多方面做的都很出色,但是使用过程有些地方感觉不爽。在继续吹捧之前我先把不爽之处
稍微提一提,然后继续介绍关于线程和套接字的知识。

### 一些可以改进之处 ###
1. 用户自定义LogLevel的实现机制不够开放
在第五篇中曾经介绍过如何实现用户自行定义LogLevel,为了实现比较理想的效果,甚至还需要改log4cplus的源代码。:(
2. 生成Logger对象的机制可以改进
我在使用时候,经常需要在不同的文件、函数中操作同一个logger,虽然log4cplus实现了树状存储以及根据名称生成Logger,却没有充分利用这样的特点确保同一个名称对应的logger对象的唯一性,比如以下代码:
    ... ...        Logger logger1 = Logger::getInstance("test");    Logger logger2 = Logger::getInstance("test");
    Logger * plogger1 = &logger1;    Logger * plogger2 = &logger2;
    std::cout << "plogger1: " << plogger1 << std::endl << "plogger2: " << plogger2 << std::endl;        ... ...        运行结果:
plogger1: 0xbfffe5a0plogger2: 0xbfffe580
从结果可以看出,明明是同一个Logger,但每次调用都会产生一个Logger副本,虽然结果是正确的(因为将存储和操作分开了),但是资源有些浪费,我看了一下log4cplus的代码,其实可以按照如下方式实现(示意性的):
#include <iostream></iostream>#include <string></string>#include 
/* forward declaration */class Logger;
class LoggerContainer{public:
    ~LoggerContainer();
    Logger * getinstance(const std::string & strLogger);
private:
    typedef std::map<:string,> LoggerMap;    LoggerMap loggerPtrs;};
class Logger{public:     Logger() {std::cout << "ctor of Logger " << std::endl; }    ~Logger() {std::cout << "dtor of Logger " << std::endl; }
    static Logger * getInstance( const std::string & strLogger)    {        static LoggerContainer defaultLoggerContainer;        return defaultLoggerContainer.getinstance(strLogger);    }};
LoggerContainer::~LoggerContainer(){    /* release all ptr in LoggerMap */    LoggerMap::iterator itr = loggerPtrs.begin();
    for( ; itr != loggerPtrs.end(); ++itr ) {     delete (*itr).second; }
}
Logger * LoggerContainer::getinstance(const std::string & strLogger){   LoggerMap::iterator itr = loggerPtrs.find(strLogger);
   if(itr != loggerPtrs.end())   {       /* logger exist, just return it */       return (*itr).second;   }   else   {       /* return a new logger */       Logger * plogger = new Logger();       loggerPtrs.insert(std::make_pair(strLogger, plogger));
       return plogger;   }}
int main(){    Logger * plogger1 = Logger::getInstance("test");    Logger * plogger2 = Logger::getInstance("test");
    std::cout << "plogger1: " << plogger1 << std::endl << "plogger2: " << plogger2 << std::endl;
    return 0;}
运行结果:
ctor of Loggerplogger1: 0x804fc30plogger2: 0x804fc30dtor of Logger
这里的LoggerContainer相当于log4cplus中的Hierarchy类,结果可以看出,通过同一个名称可以获取相同的Logger实例。
还有一些小毛病比如RollingFileAppender和DailyRollingFileAppender的参数输入顺序可以调整成统一方式等等,就不细说了。
本部分提到了使用log4cplus时候感觉不爽的地方,最后一部分将介绍一下log4cplus中线程和套接字实现情况

(七)

经过短暂的熟悉过程,log4cplus已经被成功应用到了我的项目中去了,效果还不错,:)除了上文提及的
功能之外,下面将介绍log4cplus提供的线程和套接字的使用情况。

### 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)#   define LOG4CPLUS_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<lpvoid></lpvoid>(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();    当设定输出格式(Layout)为TTCCLayout时,输出如下:
10-21-04 21:32:58, [3392] DEBUG test <ur string="" ndc=""></ur> - this is a NDC test10-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></layout> _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中的副产品, 而且仅作了最基本的实现,使用起来也异常简单,只要且必须要在派生类中重载run函数即可:
class TestThread : public AbstractThread{public:
    virtual void run();
};				void TestThread::run(){    /* do sth. */    ... ...
}
log4cplus的线程没有考虑同步、死锁,有互斥,实现线程切换的小函数挺别致的:
void log4cplus::thread::yield(){#if defined(LOG4CPLUS_USE_PTHREADS)    ::sched_yield();#elif defined(LOG4CPLUS_USE_WIN32_THREADS)    ::Sleep(0);#endif}
### 套接字 ###
套接字也是log4cplus中的副产品,在namespace log4cplus::helpers中,实现了C/S方式的日志记录。
1. 客户端程序需要做的工作:
/* 定义一个SocketAppender类型的挂接器 */SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));
/* 把_append加入到logger中 */Logger::getRoot().addAppender(_append);
/*  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);       ... ...
2. 服务器端程序需要做的工作:
/* 定义一个ServerSocket */ServerSocket serverSocket(port); 
/* 调用accept函数创建一个新的socket与客户端连接 */Socket sock = serverSocket.accept();
此后即可用该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;}
为了将读到的数据正常显示出来,需要将SocketBuffer存放的内容转换成InternalLoggingEvent格式:
spi::InternalLoggingEvent event = readFromBuffer(buffer);
然后输出:Logger logger = Logger::getInstance(event.getLoggerName());logger.callAppenders(event);				【注】 read/write是按照阻塞方式实现的,意味着对其调用直到满足了所接收或发送的个数才返回。

log4cplus的三个例程

http://log4cplus.sourceforge.net/codeexamples.html 
里面自带的三个例程

Hello World Example

#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <iomanip>

using namespace log4cplus;

int
main()
{
    BasicConfigurator config;
    config.configure();

Logger logger = Logger::getInstance("main");
    LOG4CPLUS_WARN(logger, "Hello, World!");
    return 0;
}

ostream Example (Show how to write logging messages.)

#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <iomanip>

using namespace std;
using namespace log4cplus;

int
main()
{
    BasicConfigurator config;
    config.configure();
    Logger logger = Logger::getInstance("logger");

    LOG4CPLUS_WARN(logger,   "This is"
                           << " a reall"
                           << "y long message." << endl
                           << "Just testing it out" << endl
                           << "What do you think?")
    LOG4CPLUS_WARN(logger, "This is a bool: " << true)
    LOG4CPLUS_WARN(logger, "This is a char: " << ‘x‘)
    LOG4CPLUS_WARN(logger, "This is a short: " << (short)-100)
    LOG4CPLUS_WARN(logger, "This is a unsigned short: " << (unsigned short)100)
    LOG4CPLUS_WARN(logger, "This is a int: " << (int)1000)
    LOG4CPLUS_WARN(logger, "This is a unsigned int: " << (unsigned int)1000)
    LOG4CPLUS_WARN(logger, "This is a long(hex): " << hex << (long)100000000)
    LOG4CPLUS_WARN(logger, "This is a unsigned long: " 
                   << (unsigned long)100000000)
    LOG4CPLUS_WARN(logger, "This is a float: " << (float)1.2345)
    LOG4CPLUS_WARN(logger, "This is a double: " 
                          << setprecision(15) 
                          << (double)1.2345234234)
    LOG4CPLUS_WARN(logger, "This is a long double: " 
                          << setprecision(15) 
                          << (long double)123452342342.342)

    return 0;
}

LogLevel Example (Shows how log messages can be filtered at runtime by adjusting the LogLevel.)

#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <iostream>

using namespace std;
using namespace log4cplus;

Logger logger = Logger::getInstance("main");

void printMessages()
{
    LOG4CPLUS_TRACE(logger, "printMessages()");
    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()
{
    BasicConfigurator config;
    config.configure();

    logger.setLogLevel(TRACE_LOG_LEVEL);
    cout << "*** calling printMessages() with TRACE set: ***" << endl;
    printMessages();

    logger.setLogLevel(DEBUG_LOG_LEVEL);
    cout << "/n*** calling printMessages() with DEBUG set: ***" << endl;
    printMessages();

    logger.setLogLevel(INFO_LOG_LEVEL);
    cout << "/n*** calling printMessages() with INFO set: ***" << endl;
    printMessages();

    logger.setLogLevel(WARN_LOG_LEVEL);
    cout << "/n*** calling printMessages() with WARN set: ***" << endl;
    printMessages();

    logger.setLogLevel(ERROR_LOG_LEVEL);
    cout << "/n*** calling printMessages() with ERROR set: ***" << endl;
    printMessages();

    logger.setLogLevel(FATAL_LOG_LEVEL);
    cout << "/n*** calling printMessages() with FATAL set: ***" << endl;
    printMessages();

    return 0;
}

时间: 2024-07-29 14:25:02

log4cplus学习笔记(二 )的相关文章

Caliburn.Micro学习笔记(二)----Actions

Caliburn.Micro学习笔记(二)----Actions 上一篇已经简单说了一下引导类和简单的控件绑定 我的上一个例子里的button自动匹配到ViewModel事件你一定感觉很好玩吧 今天说一下它的Actions,看一下Caliburn.Micro给我们提供了多强大的支持 我们还是从做例子开始 demo的源码下载在文章的最后 例子1.无参数方法调用 点击button把textBox输入的文本弹出来 如果textbox里没有文本button不可点,看一下效果图 看一下前台代码 <Stac

2. 蛤蟆Python脚本学习笔记二基本命令畅玩

2. 蛤蟆Python脚本学习笔记二基本命令畅玩 本篇名言:"成功源于发现细节,没有细节就没有机遇,留心细节意味着创造机遇.一件司空见惯的小事或许就可能是打开机遇宝库的钥匙!" 下班回家,咱先来看下一些常用的基本命令. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/48092873 1.  数字和表达式 看下图1一就能说明很多问题: 加法,整除,浮点除,取模,幂乘方等.是不是很直接也很粗暴. 关于上限,蛤蟆不太清楚

小猪的数据结构学习笔记(二)

小猪的数据结构学习笔记(二) 线性表中的顺序表 本节引言: 在上个章节中,我们对数据结构与算法的相关概念进行了了解,知道数据结构的 逻辑结构与物理结构的区别,算法的特性以及设计要求;还学了如何去衡量一个算法 的好坏,以及时间复杂度的计算!在本节中我们将接触第一个数据结构--线性表; 而线性表有两种表现形式,分别是顺序表和链表;学好这一章很重要,是学习后面的基石; 这一节我们会重点学习下顺序表,在这里给大家一个忠告,学编程切忌眼高手低,看懂不代表自己 写得出来,给出的实现代码,自己要理解思路,自己

JavaScript--基于对象的脚本语言学习笔记(二)

第二部分:DOM编程 1.文档象模型(DOM)提供了访问结构化文档的一种方式,很多语言自己的DOM解析器. DOM解析器就是完成结构化文档和DOM树之间的转换关系. DOM解析器解析结构化文档:将磁盘上的结构化文档转换成内存中的DOM树 从DOM树输出结构化文档:将内存中的DOM树转换成磁盘上的结构化文档 2.DOM模型扩展了HTML元素,为几乎所有的HTML元素都新增了innerHTML属性,该属性代表该元素的"内容",即返回的某个元素的开始标签.结束标签之间的字符串内容(不包含其它

马哥学习笔记二十四——分布式复制快设备drbd

DRBD: 主从 primary: 可执行读.写操作 secondary: 文件系统不能挂载 DRBD: dual primay, 双主(基于集群文件系统的高可用集群) 磁盘调度器:合并读请求,合并写请求: Procotol:drbd数据同步协议 A: Async, 异步  数据发送到本机tcp/ip协议栈 B:semi sync, 半同步  数据发送到对方tcp/ip协议 C:sync, 同步  数据到达对方存储设备 DRBD Source: DRBD资源 资源名称:可以是除了空白字符外的任意

【Unity 3D】学习笔记二十八:unity工具类

unity为开发者提供了很多方便开发的工具,他们都是由系统封装的一些功能和方法.比如说:实现时间的time类,获取随机数的Random.Range( )方法等等. 时间类 time类,主要用来获取当前的系统时间. using UnityEngine; using System.Collections; public class Script_04_13 : MonoBehaviour { void OnGUI() { GUILayout.Label("当前游戏时间:" + Time.t

Spring Batch学习笔记二

此系列博客皆为学习Spring Batch时的一些笔记: Spring Batch的架构 一个Batch Job是指一系列有序的Step的集合,它们作为预定义流程的一部分而被执行: Step代表一个自定义的工作单元,它是Job的主要构件块:每一个Step由三部分组成:ItemReader.ItemProcessor.ItemWriter:这三个部分将执行在每一条被处理的记录上,ItemReader读取每一条记录,然后传递给ItemProcessor处理,最后交给ItemWriter做持久化:It

angular学习笔记(二十八)-$http(6)-使用ngResource模块构建RESTful架构

ngResource模块是angular专门为RESTful架构而设计的一个模块,它提供了'$resource'模块,$resource模块是基于$http的一个封装.下面来看看它的详细用法 1.引入angular-resource.min.js文件 2.在模块中依赖ngResourece,在服务中注入$resource var HttpREST = angular.module('HttpREST',['ngResource']); HttpREST.factory('cardResource

Swift学习笔记(二)参数类型

关于参数类型,在以前的编程过程中,很多时间都忽视了形参与实参的区别.通过这两天的学习,算是捡回了漏掉的知识. 在swift中,参数有形参和实参之分,形参即只能在函数内部调用的参数,默认是不能修改的,如果想要修改就需要在参数前添加var声明. 但这样的声明过后,仍旧不会改变实参的值,这样就要用到inout了,传递给inout的参数类型必须是var类型的,不能是let类型或者字面类型,(字面类型是在swift中常提的一个术语,个人认为就是赋值语句,也不能修改)而且在传递过程中,要用传值符号"&