C ++线程安全,基于上下文的基于流的日志记录

时间:2014-09-04 10:58:03

标签: c++ multithreading logging

所以我一直在写一个日志记录类,我决定更改类,以便它使用流而不是带有string / int参数组合的函数。这是我想出的: Logger.h:

#ifndef __LOG_H
#define __LOG_H
class logger {
    public:
        logger();
        void log ( int mlvl );
        int Llport;
        std::stringstream logstream;
    private:
        void errException( const int &ecode, const int &line, const std::string &file );
        void readCfg();
        void setLogLevel( int llvl );
        pid_t getPID();
        unsigned long getThread();
        std::string tStamp();
        bool writeLog( const std::string &message );
        std::string Lcfgfile;
        std::string Lfile;
        unsigned long Lthread;
        pid_t Lpid;
        int Llvl;
};
extern logger mlog;
#endif

logger.cpp:

#include<iostream>
#include<string>
#include<iomanip>
#include<fstream>
#include<time.h>
#include<stdio.h>
#include<map>
#include<sstream>
#include"log.h"
#include"ConfigFile.h"
#include"util.h"
#include<unistd.h>
#include<boost/lexical_cast.hpp>
#include<boost/thread.hpp>
#include<time.h>
logger::logger(){
    Lcfgfile="/home/ianc/www/coding/GDAgent/cfg/GDAgent.conf";
    readCfg();
}
std::string logger::tStamp(){
    time_t now = time(0);
    struct tm tstruct;
    char buf[80];
    tstruct = *localtime(&now);
    strftime( buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct );
    return buf;
}
void logger::readCfg() {
    cfg::cfg conf( Lcfgfile );
    Llvl = 0;
    if( conf.keyExists( "logfile" ) ){
        Lfile = conf.getValueOfKey<std::string>( "logfile" );
    }
    else {
        Lfile = "/var/log/GDlog";
    }
    if( conf.keyExists( "DebugLevel" ) ) {
        int debuglevel = conf.getValueOfKey<int>( "DebugLevel" );
        std::stringstream d;
        d << debuglevel;
        std::string dd = d.str();
        char dl = dd[0];
        int dlvl = ccInt( dl );
        Llvl = dlvl;
    }
    if( conf.keyExists( "ListenPort" ) ) {
        Llport = conf.getValueOfKey<int>( "ListenPort" );
    }
    else {
        Llport = 8000;
    }
}
unsigned long logger::getThread() {
    std::string threadID = boost::lexical_cast<std::string>(boost::this_thread::get_id() );
    unsigned long threadNumber = 0;
    sscanf( threadID.c_str(), "%lx", &threadNumber );
    return threadNumber;
}
pid_t logger::getPID() {
    Lpid = getpid();
    return Lpid;
}
bool logger::writeLog( const std::string &message ) {
    Lpid = getPID();
    Lthread = getThread();
    std::string ts = tStamp();
    std::ofstream lmsg( Lfile, std::ofstream::app );
    lmsg << ts << " [" << Lpid << "] (" << Lthread << ") " << message;
    lmsg.close();
    return true;
}
void logger::log( int mlvl ) {
    if( mlvl >= Llvl ){
        std::string lin = logstream.str();
        logstream.str("");
        switch( mlvl ) {
            case 0:
                logstream << "[DEBUG] " << lin << std::endl;
                lin = logstream.str();
                writeLog( lin );
                logstream.str("");
                break;
            case 1:
                logstream<< "[INFO] " << lin << std::endl;
                lin = logstream.str();
                writeLog( lin );
                logstream.str("");
                break;
            case 2:
                logstream<< "[WARN] " << lin << std::endl;
                lin = logstream.str();
                writeLog( lin );
                logstream.str("");
                break;
            case 3:
                logstream<< "[ERR] " << lin << std::endl;
                lin = logstream.str();
                writeLog( lin );
                logstream.str("");
                break;
             case 4:
                 logstream<< "[CRITICAL] " << lin << std::endl;
                 lin = logstream.str();
                 writeLog( lin );
                 logstream.str("");
                 break;
             case 5:
                 logstream<< "[EXCEPTION] " << lin << std::endl;
                 lin = logstream.str();
                 logstream.str("");
                 writeLog( lin );
             default:
                 logstream<< "[GENERAL] " << lin << std::endl;
                 lin = logstream.str();
                 writeLog( lin );
                 logstream.str("");
                 writeLog( "[ERROR] Invalid identifier given to logger.\n" );
                 break;
         }
     }
 }
 logger mlog;

基本上,我写入字符串流,然后用消息级别“刷新”流。这在理论上都很可爱,但在实践中......

[4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408) 2014-09-04.06:21:23 [4297] (140377696753408)

之所以这样,是因为它是一个在多个线程中全局实例化的对象 - 如果我将一个参数传递给一个函数,那就没问题了......但是这些流会相互覆盖,导致一个巨大的混乱,最终一个段错误。我尝试在每个线程中实例化一次记录器并将其作为参数传递给所有函数,但是我得到一个关于复制构造函数和logger()的合成方法的神秘消息,所以我尝试在每个模块中实例化一次,每次一次函数,虽然每个模块的解决方案(每个.cpp文件中的一个与其他文件不同)在大多数情况下都有效,但最终我收到一条错误,说“file _ (我的配置文件)无法找到!“程序崩溃了。 所以我的问题是,是否有更好的方法不像将字符串传递给函数那样笨拙,但仍允许我将上下文(如消息级别)添加到我的日志条目中?

1 个答案:

答案 0 :(得分:1)

您可以在单独的线程中执行使用共享资源的实际日志记录。该线程在共享队列上等待,因此其他线程分别收集它们的信息并通过队列将其发送到日志记录线程。

优点是不需要共享资源,并且工作线程不必访问外部系统进行日志记录(网络,磁盘,终端)。缺点是崩溃之前的日志可能会丢失,但有一些方法可以改善这一点(例如,当严重性严重时等待记录器结束)。