log4cpp 阅读笔记

目前log4cpp的原始项目主页在 http://log4cpp.sourceforge.net/

最新的更新在2017年,且更新的频率相对较慢

在github上有一个orocos-toolchain维护的版本,https://github.com/orocos-toolchain/log4cpp

此处以原始项目的代码为蓝本。

公司的使用习惯为文本配置,使用log4cpp::PropertyConfigurator::configure(_cfg_file); 来获取配置文件,并解析

包装类的git地址为:https://github.com/zhoutong12589/log4cpp_pack

此项目包括了一个使用的demo

 

1、配置解析

配置解析与初始化主要在PropertyConfiguratorImpl::doConfigure(std::istream& in)

void PropertyConfiguratorImpl::doConfigure(std::istream& in) {
        // parse the file to get all of the configuration
        _properties.load(in);

        instantiateAllAppenders();
        // get categories
        std::vector<std::string> catList;
        getCategories(catList);

        // configure each category
        for(std::vector<std::string>::const_iterator iter = catList.begin();
            iter != catList.end(); ++iter) {
            configureCategory(*iter);
        }
    }

_properties.load(in) 

读取配置文件,按照行来进行读取,

// strip off the "log4j" or "log4cpp"
            length = leftSide.find('.');
            if (leftSide.substr(0, length) == "log4j" ||
                leftSide.substr(0, length) == "log4cpp")
                leftSide = leftSide.substr(length + 1);

代码说明,可以以log4j或者log4cpp或者直接使用配置

instantiateAllAppenders();

按照顺序初始化各个appender

在该函数中调用PropertyConfiguratorImpl::instantiateAppender(const std::string& appenderName)

显示log4cpp支持的appender种类为ConsoleAppender、FileAppender、RollingFileAppender、DailyRollingFileAppender、SyslogAppender、LocalSyslogAppender、AbortAppender、Win32DebugAppender、NTEventLogAppender

PropertyConfiguratorImpl::setLayout(Appender* appender, const std::string& appenderName)

表明log4cpp支持三种layout

BasicLayout、SimpleLayout、PatternLayout

void PropertyConfiguratorImpl::getCategories(std::vector<std::string>& categories) const {
        categories.clear();

        // add the root category first
        categories.push_back(std::string("rootCategory"));

        // then look for "category."
        std::string prefix("category");
        Properties::const_iterator from = _properties.lower_bound(prefix + '.');
        Properties::const_iterator to = _properties.lower_bound(prefix + (char)('.' + 1)); 
        for (Properties::const_iterator iter = from; iter != to; iter++) {
            categories.push_back((*iter).first.substr(prefix.size() + 1));
        }
    }

必须包含rootCategory,程序中默认就直接将该选项包含

可以包含子category

PropertyConfiguratorImpl::configureCategory(const std::string& categoryName)

第一步获取日志级别

Priority::Value priority = Priority::NOTSET;
        if (i != iEnd) {
            std::string priorityName = StringUtil::trim(*i++);
            try {
                if (priorityName != "") {
                    priority = Priority::getPriorityValue(priorityName);
                }
            } catch(std::invalid_argument& e) {
                throw ConfigureFailure(std::string(e.what()) + 
                    " for category '" + categoryName + "'");
            }
        }

        try {
        	category.setPriority(priority);
        } catch (std::invalid_argument& e) {
        	throw ConfigureFailure(std::string(e.what()) +
                    " for category '" + categoryName + "'");
        }

第二部,将appender加入category中

for(/**/; i != iEnd; ++i) {           
            std::string appenderName = StringUtil::trim(*i);
            AppenderMap::const_iterator appIt = 
                _allAppenders.find(appenderName);
            if (appIt == _allAppenders.end()) {
                // appender not found;
                throw ConfigureFailure(std::string("Appender '") +
                    appenderName + "' not found for category '" + categoryName + "'");
            } else {
                /* pass by reference, i.e. don't transfer ownership
                 */
                category.addAppender(*((*appIt).second));
            }
        }

2、写日志

如info级别

void Category::info(const char* stringFormat, ...) throw() { 
        if (isPriorityEnabled(Priority::INFO)) {
            va_list va;
            va_start(va,stringFormat);
            _logUnconditionally(Priority::INFO, stringFormat, va);
            va_end(va);
        }
    }
    
    void Category::info(const std::string& message) throw() { 
        if (isPriorityEnabled(Priority::INFO))
            _logUnconditionally2(Priority::INFO, message);
    }

表明是支持可变参数的,首先判断是否达到相应的写日志解级别,然后写日志

创建日志事件,并调用相应的appender

void Category::_logUnconditionally2(Priority::Value priority, 
                                        const std::string& message) throw() {
        LoggingEvent event(getName(), message, NDC::get(), priority);
        callAppenders(event);
    }

此处有线程锁,确保线程安全,但additivity的具体作用暂时未知

void Category::callAppenders(const LoggingEvent& event) throw() {
        threading::ScopedLock lock(_appenderSetMutex);
        {
            if (!_appender.empty()) {
                for(AppenderSet::const_iterator i = _appender.begin();
                    i != _appender.end(); i++) {
                    (*i)->doAppend(event);
                }
            }
        }
        if (getAdditivity() && (getParent() != NULL)) {
            getParent()->callAppenders(event);
        }
    }

这里的doAppend是接口函数,AppenderSkeleton基类拥有虚函数。

virtual void _append(const LoggingEvent& event) = 0;

class LOG4CPP_EXPORT FileAppender : public LayoutAppender
class LOG4CPP_EXPORT LayoutAppender : public AppenderSkeleton

通过类的继承来实现最终调用相应的appender,比如上面的fileAppender

FileAppender直接调用write来写文件,直到卸完成才返回。

void FileAppender::_append(const LoggingEvent& event) {
        std::string message(_getLayout().format(event));
        if (!::write(_fd, message.data(), message.length())) {
            // XXX help! help!
        }
    }

RollingFileAppender通过每次调用都获取文件的大下,然后调用FileAppender来实现固定大小文件的回滚

void RollingFileAppender::_append(const LoggingEvent& event) {
        FileAppender::_append(event);
        off_t offset = ::lseek(_fd, 0, SEEK_END);
        if (offset < 0) {
            // XXX we got an error, ignore for now
        } else {
            if(static_cast<size_t>(offset) >= _maxFileSize) {
                rollOver();
            }
        }
    }

每次文件名的改写都是一个过程

void RollingFileAppender::rollOver() {
        ::close(_fd);
        if (_maxBackupIndex > 0) {
            std::ostringstream filename_stream;
        	filename_stream << _fileName << "." << std::setw( _maxBackupIndexWidth ) << std::setfill( '0' ) << _maxBackupIndex << std::ends;
        	// remove the very last (oldest) file
        	std::string last_log_filename = filename_stream.str();
            // std::cout << last_log_filename << std::endl; // removed by request on sf.net #140
            ::remove(last_log_filename.c_str());
            
            // rename each existing file to the consequent one
            for(unsigned int i = _maxBackupIndex; i > 1; i--) {
                filename_stream.str(std::string());
                filename_stream << _fileName << '.' << std::setw( _maxBackupIndexWidth ) << std::setfill( '0' ) << i - 1 << std::ends;  // set padding so the files are listed in order
                ::rename(filename_stream.str().c_str(), last_log_filename.c_str());
                last_log_filename = filename_stream.str();
            }
            // new file will be numbered 1
            ::rename(_fileName.c_str(), last_log_filename.c_str());
        }
        _fd = ::open(_fileName.c_str(), _flags, _mode);
    }

DailyRollingFileAppender根据时间来进行切割,这里使用localtime_s是因为localtime是一个不可重入的函数,具体可以参考“man localtime”

	void DailyRollingFileAppender::_append(const log4cpp::LoggingEvent &event)
	{
		struct tm now;
		time_t t = time(NULL);

#ifndef WIN32    // only available on Win32
		bool timeok = localtime_r(&t, &now) != NULL;
#else
		bool timeok = localtime_s(&now, &t) == 0;
#endif
		if (timeok) {
			if ((now.tm_mday != _logsTime.tm_mday) ||
				(now.tm_mon != _logsTime.tm_mon) ||
				(now.tm_year != _logsTime.tm_year)) {
				rollOver();
				_logsTime = now;
			}
		}
		log4cpp::FileAppender::_append(event);
	}

由此可见,DailyRollingFileAppender是按照天来切割,年、月、日,有一个不同,则需要进行rollOver,然后调用FileAppender进行写入。

 

如果强调日志不影响执行,则建议给log4cpp加入缓冲队列来使用,或者通过改造log4cpp的代码来完成。

接下来的计划是分析ACE的Task的原理,并剥离出来,和log4cpp结合使用,再使用C11中的线程和各种互斥、原子操作来实现相关的队列和写日志,可参考glog来改造log4cpp来优化日志的速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值