`
honey_bee
  • 浏览: 90299 次
社区版块
存档分类
最新评论

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: 0xbfffe5a0
plogger2: 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 Logger
plogger1: 0x804fc30
plogger2: 0x804fc30
dtor 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 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></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;
}

分享到:
评论

相关推荐

    oracle学习笔记下载

    ### Oracle 学习笔记知识点概览 #### 一、Oracle 数据库系统参数查询与管理 在 Oracle 数据库的学习过程中,了解如何查看和管理数据库的系统参数是非常重要的。这些参数直接影响着数据库的性能和稳定性。 ##### ...

    韩顺平oracle学习笔记

    韩顺平oracle学习笔记 第0讲:如何学习oracle 一、如何学习oracle Oracle目前最流行的数据库之一,功能强大,性能卓越。学习oracle需要具备一定基础: 1.学习过一门编程语言(如:java ,c) 2.最好学习过一门别的...

    oracle10g学习笔记

    ### Oracle 10g 学习笔记 #### 一、Oracle 数据库结构 在 Oracle 10g 中,数据库的组织结构非常重要,它涉及到数据如何存储以及如何进行管理。以下是一些基本概念: 1. **参数文件(Parameter File)**: - 参数...

    Oracle学习笔记1.doc

    ### Oracle学习笔记知识点详解 #### 一、Oracle的卸载步骤及注意事项 ##### 软件环境 - **操作系统:** Windows XP - **Oracle版本:** 8.1.7 - **安装路径:** C:\ORACLE ##### 卸载方法 1. **停止Oracle服务:** -...

    oracle学习笔记

    ### Oracle学习笔记精要 #### Oracle数据库基础概念与管理 Oracle数据库系统是全球最流行的数据库管理系统之一,由美国Oracle公司开发。它提供了强大的数据管理和处理功能,广泛应用于各种企业级应用环境中。 - *...

    假期学习R软件的笔记

    ### 假期学习R软件的笔记 #### 统计学与数据分析的入门资源 对于初学者来说,选择合适的教材是十分重要的。以下是一些推荐的书籍,它们可以帮助你更好地理解R语言及其在统计分析中的应用: - **《Modern Applied ...

    Apache Web服务器配置与使用工作笔记

    ### Apache Web服务器配置与使用工作笔记 #### 一、Apache Web服务器简介 Apache HTTP Server(简称Apache)是一款开放源码的Web服务运行平台,在互联网上有着广泛的使用。它是由Apache软件基金会的一个开源项目...

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 连接字符串

    oracle学习文档 笔记 全面 深刻 详细 通俗易懂 doc word格式 清晰 第一章 Oracle入门 一、 数据库概述 数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它产生于距今五十年前。简单来说是本身可视...

    PL/SQL听课笔记

    ### PL/SQL听课笔记 #### 一、PL/SQL简介 **PL/SQL**(Procedural Language for SQL)是一种专门为Oracle数据库设计的过程化语言扩展。它是在标准SQL基础上增加了一系列高级编程特性,如变量、控制结构、函数、...

    10g备份与恢复读书笔记hxp.pdf

    通过学习这些内容,可以帮助DBA更好地掌握Oracle 10g的备份恢复机制,为保障数据安全提供有力的支持。需要注意的是,在实际操作过程中还需结合具体的业务需求和环境特点来灵活运用这些技术。 以上内容涵盖了文档中...

    [三思笔记]一步一步学rman【精华】

    C:\DocumentsandSettings\Administrator&gt;rman target / log=c:\temp\myrmanlog.log ``` 此命令将会把所有输出信息记录到指定路径下的日志文件中,这对于编写复杂的RMAN脚本或批量任务非常有用。 #### 四、深入理解...

    C-plus-plus-Notes:Udacity课程的C ++实践程序

    "C-plus-plus-Notes: Udacity课程的C++实践程序"这个标题暗示了这是一个学习资源,可能是课程笔记或项目,用于辅助学习者理解Udacity在线课程中的C++概念。 C++的特点主要包括: 1. **面向对象编程**:C++支持类和...

    oracle学习sql

    ### Oracle学习笔记与SQL知识点详解 #### 一、创建表空间与用户 在Oracle数据库管理中,表空间是存储数据的逻辑容器。每个数据库至少包含一个表空间,默认为`SYSTEM`表空间。创建表空间是数据库管理的基础操作之一...

    Oracle学习资料2

    ### Oracle学习资料2:韩顺平上课笔记,玩转oracle实战教程 #### 安装与配置 1. **停止Oracle服务**:在进行任何安装或配置之前,确保先停止Oracle服务,避免安装过程中的冲突。 2. **使用OUI(Oracle Universal ...

Global site tag (gtag.js) - Google Analytics