C++代码中日志打印的实现方式,并有问题求教。C++小白勿进。

徐永强 发布于 2014/08/09 22:45
阅读 1K+
收藏 3

公司项目代码中人多手杂,不止一次出现使用变参打印函数(类似printf)时,参数格式错误导致的致命问题。

例如类似这样的:

int16_t msgId = msg.GetMsgId();
if (msgId > 100) {
    printf("receive unexpect msg, msgid=%s \n", msgId);
}

正在考虑用stringstream的格式化接口和重载函数,替换目前使用的变参函数打印函数。

(后面是在家里完成的初步的代码,如果思路可行,准备放到小组的项目中评审和使用)

有几个疑问:

1、使用重载<<操作符的方法来支持一个或多个待打印参数,是否影响可读性?

2、原本并不愿意过多使用宏,但为简化打印调用点的使用,使用一个参数类Para和宏来实现 “valName=Val”的打印格式,是否有重新放宽参数性别检查的问题?

3、关于时间和空间性能,使用stringstream等方法实现格式化操作,相比原来的变参函数,可能有哪些变化?

……

希望高手和小白都能不吝赐教,谢谢。

PS:“C++小白勿进”是用来拉仇恨和吸引眼球的。如果您自谦为小白,也请不吝赐教,因为楼主相信自己比您更白,也因为楼主也想了解对C++不熟练或不感冒(或者一直只使用C做开发)的兄弟怎样看待打印是否好用,

#ifndef DOL_PRINTF_H__
#define DOL_PRINTF_H__

#include <stdint.h>
#include <list>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <ctime>

namespace dolPrint{

enum Format {
    Dec,
    Hex,
    Str,
    Arr,
    Arrc,
};

enum CtrlOutPut {
    OutPut
};

enum CtrlNoPara {
    NoPara
};

struct Array{
    const uint8_t *val;
    size_t len;
};

struct Para {
    const char *name;   //extern c-string;
    Format format;
    union {
        const char *strVal;
        uint64_t    uintVal;
        Array       arrayVal;
    };

    Para (uint64_t ull, Format format) : name(NULL), format(format), uintVal(ull) {
    }
    Para (uint64_t ull, const char *name, Format format = Dec) : name(name), format(format), uintVal(ull) {
    }
    Para (const char *str, const char *name) : name(name), format(Str), strVal(str) {
    }
    Para (const uint8_t *array, size_t len, Format format = Arr) : name(NULL), format(format) {
        arrayVal.val = array;
        arrayVal.len = len;
    }
    Para (const uint8_t *array, size_t len, const char* name, Format format = Arr) : name(name), format(format) {
        arrayVal.val = array;
        arrayVal.len = len;
    }

    Para (const Para&); // forbidden
    Para& operator = (const Para&); // forbidden
};

class Printer {
    std::ostringstream head;
    std::ostringstream body;
    bool paraExist;
    uint16_t module;
    uint16_t level;
    const char *file;
    size_t line;
    const char *func;
    const char *description;

    typedef void (*PfExternPrintFunc) (uint16_t module, uint16_t level, const char *file, size_t line, const char *funcName, const char *content);
    inline void Separator() {
        if (paraExist) {
            body<<", ";
        }
        else {
            if (description) {
                body<<": ";
            }
            paraExist = true;
        }
    }
    inline void PrintString(const char *str) {
        if (str) {
            body<<'"'<<str<<'"';
        }
        else {
            body<<"null";
        }
    }
    inline void PrintArray(const Array &arrayVal, Format format) {
        if (arrayVal.val) {
            body<<'{';
            for (size_t i = 0; i<arrayVal.len; ++i) {
                uint8_t c = arrayVal.val[i];
                if (Arrc == format) {
                    if (c >= ' ' && c <= '~') {
                        body<<c;
                    }
                    else {
                        body<<'.';
                    }
                }
                else
                {
                    if (i) {
                        body<<',';
                    }
                    body<<std::hex<<std::showbase<<(size_t)arrayVal.val[i];
                }
            }
            body<<'}';
        }
        else {
            body<<"null";
        }
    }
public:
    static PfExternPrintFunc ExternPrintFunc(PfExternPrintFunc func) {
        static PfExternPrintFunc g_func = NULL;
        if (func) {
            g_func = func;
        }
        return g_func;
    };

    Printer (uint16_t module, uint16_t level, const char *file, size_t line, const char *func, const char* description)
        : paraExist(false), module(module), level(level), file(file), line(line), func(func), description(description) {
        if (description) {
            body<<description;
        }
    }
    Printer& operator << (const Para &para) {
        Separator();

        if (para.name) {
            body<<para.name<<"=";
        }

        switch (para.format) {
            case Hex:
                body<<std::hex<<std::showbase<<para.uintVal;
                break;
            case Dec:
                body<<std::dec<<para.uintVal;
                break;
            case Str:
                PrintString(para.strVal);
                break;
            case Arr:
            case Arrc:
                PrintArray(para.arrayVal, para.format);
                break;
            default:
                break;
        }

        return *this;
    }
    Printer& operator << (const char *strPara) {
        Separator();
        PrintString(strPara);
        return *this;
    }
    Printer& operator << (uint64_t ullPara) {
        Separator();
        body<<std::dec<<ullPara;
        return *this;
    }
    Printer& operator << (CtrlNoPara) {
        return *this;
    }
    void operator << (CtrlOutPut) {
        PfExternPrintFunc printfFunc = ExternPrintFunc(NULL);
        if (printfFunc) {
            printfFunc(module, level, file, line, func, body.str().c_str());
        }
        else {
            std::cout<<std::dec;
            std::cout<<'['<<module<<']';
            std::cout<<'['<<level<<']';
            std::cout<<file<<'('<<line<<"):";
            std::cout<<func<<' ';
            std::cout<<body.str()<<std::endl;    // replace
        }
    }
};

}

#define DNOPARA (dolPrint::NoPara)
#define N(para_) dolPrint::Para((para_),(#para_))
#define H(para_) dolPrint::Para((para_),dolPrint::Hex)
#define NH(para_) dolPrint::Para((para_),(#para_),dolPrint::Hex)
#define ARR(val_, len_) dolPrint::Para((val_), (len_))
#define NARR(val_, len_) dolPrint::Para((val_), (len_), (#val_))
#define ARRC(val_, len_) dolPrint::Para((val_), (len_), dolPrint::Arrc)
#define NARRC(val_, len_) dolPrint::Para((val_), (len_), (#val_), dolPrint::Arrc)

#define DPRINT(module_,level_, description_, paras_) {\
    /*check module-level inline here*/\
    dolPrint::Printer tmpPrinter((module_), (level_), (__FILE__), (__LINE__), (__FUNCTION__), (description_));\
    tmpPrinter<<paras_<<dolPrint::OutPut;\
}

inline int TestDolPrint() {

    uint64_t first = 42;
    uint64_t second = 4242;
    uint64_t third = 424242;
    const char *fourth = "ssss";
    const char *fifth = "sssss";
    const char *sixth = NULL;
    const char seventh[] = "bpmfgknl";
    char charPara1 = 'A';
    uint8_t array[4] = {0x61,0x62,0x63,0x64};
    uint8_t zeroarray[1] = {0x61};
    uint8_t *nullarray = NULL;
    char array_ex_base[] = "cr\r" "lf\n" "tab\t" "alm\a" "fff\f" "vvv\v" "end";
    uint8_t *array_ex = (uint8_t*)array_ex_base;
    uint8_t array_ex2[] = {0x61, 0x62, 0x00, 0x64, 0x65};

    uint16_t moduleId = 100;
    uint16_t level = 3;

    time_t beginTime = time(NULL);

    // original
    dolPrint::Printer p(moduleId, level, __FILE__, __LINE__, __FUNCTION__, "original ");
    p<<first<<second<<third<<dolPrint::OutPut;

    dolPrint::Printer p2(moduleId, level, __FILE__, __LINE__, __FUNCTION__, "original nopara");
    p2<<dolPrint::NoPara<<dolPrint::OutPut;

    // macro
    DPRINT(moduleId, level, "by_macro", first<<second<<third);
    DPRINT(moduleId, level, "by_macro_no_para", DNOPARA);
    DPRINT(moduleId, level, "by_macro_?_para", first<<DNOPARA<<second<<third);

    // macro
    DPRINT(moduleId, level, NULL, first<<second<<third);  // no description
    DPRINT(moduleId, level, "test_u64", first<<second<<third);
    DPRINT(moduleId, level, "test_u32", (uint32_t)first<<(uint32_t)second<<(uint32_t)third);
    DPRINT(moduleId, level, "test_u16", (uint16_t)first<<(uint16_t)second<<(uint16_t)third);
    DPRINT(moduleId, level, "test_u8", (uint8_t)first<<(uint8_t)second<<(uint8_t)third);

    DPRINT(moduleId, level, "test_named", N(first)<<N(second)<<N(third));
    DPRINT(moduleId, level, "test_hex", H(first)<<H(second)<<H(third));
    DPRINT(moduleId, level, "test_named_hex", NH(first)<<NH(second)<<NH(third));
    DPRINT(moduleId, level, "test_named_mix", H(first)<<second<<H(third));

    DPRINT(moduleId, level, "test_string", fourth<<fifth<<sixth<<seventh);
    DPRINT(moduleId, level, "test_named_string", N(fourth)<<N(fifth)<<N(sixth)<<N(seventh));
    //DPRINT("moduleId, level, test_hex_string", H(fourth)<<H(fifth)<<H(sixth));         //type missmatch, compile error
    //DPRINT("moduleId, level, test_named_hex_string", NH(fourth)<<NH(fifth)<<NH(sixth));//type missmatch, compile error

    DPRINT(moduleId, level, "test_char", charPara1<<N(charPara1));

    DPRINT(moduleId, level, "test_array", ARR(array, 4)<<ARR(nullarray, 1000)<<ARR(zeroarray, 0));
    DPRINT(moduleId, level, "test_parray", NARR(array, 4)<<NARR(nullarray, 1000)<<NARR(zeroarray, 0));
    DPRINT(moduleId, level, "test_arrayc", ARRC(array, 4)<<ARRC(nullarray, 1000)<<ARRC(zeroarray, 0));
    DPRINT(moduleId, level, "test_parrayc", NARRC(array, 4)<<NARRC(nullarray, 1000)<<NARRC(zeroarray, 0));
    DPRINT(moduleId, level, "test_parrayc_ex", NARRC(array_ex, sizeof(array_ex_base)));
    //DPRINT(moduleId, level, "test_parrayc_ex_base", NARRC(array_ex_base, sizeof(array_ex_base)));
    DPRINT(moduleId, level, "test_parrayc_ex2", NARRC(array_ex2, sizeof(array_ex2)));

    DPRINT(moduleId, level, "test_const_string_abcde", "abcde");
    DPRINT(moduleId, level, "test_const_uint64_42", 42ULL);
    DPRINT(moduleId, level, "test_const_uint32_42", 42UL);
    DPRINT(moduleId, level, "test_const_uint16_42", (uint16_t)42);
    DPRINT(moduleId, level, "test_const_uint8_42", (uint8_t)42);

    DPRINT(moduleId, level, "test_const_uint64_42", 42ULL);
    DPRINT(moduleId, level, "test_const_uint32_42", 42UL);
    DPRINT(moduleId, level, "test_const_uint16_42", (uint16_t)42);
    DPRINT(moduleId, level, "test_const_uint8_42", (uint8_t)42);
    //DPRINT(moduleId, level, "test_const_0", 0);                 //overload ambiguous, compile error
    DPRINT(moduleId, level, "test_named_const_string_abcde", N("abcde"));
    //DPRINT(moduleId, level, "test_named_const_hex_string_abcde", NH("abcde"));
    //DPRINT(moduleId, level, "test_named_const_0", N(0));        //overload ambiguous, compile error
    //DPRINT(moduleId, level, "test_named_const_NULL", N(NULL));  //overload ambiguous, compile error

    DPRINT(moduleId, level, "testarrayc12345", ARRC((uint8_t*)"12345", 5));

    time_t endTime = time(NULL);
    DPRINT(moduleId, level, "dprint test finish", N(beginTime)<<N(endTime));

    return 0;
}

#endif //DOL_PRINTF_H__



加载中
0
淡定的wo
淡定的wo

其实,大多数时候自己写日志打印代码就满足需求了。不过,为了防止多人合作出现这方面的问题。

我是用了log4cplus库,然后将log4cplus使用自己的类MLog封装。以后要更换底层的Log库,也不会影响代码。

PS:我是小白

class MLog {
private:
    MLog();

public:
    ~MLog();
    // 单例模式: 保证至少有一个MLog对象被初始化
    static MLog* getInstance();

public:
    // 打印变量内容,变量名称=  后面没有换行
    void printVar(
        const string &fn_name,
        const string &var_name,
        const string &var_value);

    // 打印变量内容,变量名称=  后面没有换行
    void printVar(
        const string &fn_name,
        const string &var_name,
        const int32_t &var_value);

    void printTraceMsg(const string &msg);
    void printDebugMsg(const string &msg);
    void printInfoMsg(const string &msg);
    void printWarnMsg(const string &msg);
    void printErrorMsg(const string &msg);
    void printFatalMsg(const string &msg);
};

typedef MLog* MLog_ptr;

使用:
void to_gfs_path() {
    const string fn_name_("to_gfs_path");
    MLog_ptr mlog_ = MLog::getInstance();

    const string path_("/tmp");
    mlog->printVar(fn_name_, "path_", path_);
    #mlog->printInfo(fn_name_, "path_", path_);
}

输出:
进程ID 时间 to_gfs_path: path_=/tmp
进程ID 时间 [Info] /tmp



徐永强
徐永强
多谢。参考了。 因为底层log记录的实现暂时不会使用log4cplus, 目前要做的和你实现的类似:用新类对调用接口稍作封装,并尽量使用起来方便和避免错误。
0
a
autocoder
貌似你的问题是安全格式化 跟log完全无关.
徐永强
徐永强
切中要害,其实是这个意思。
返回顶部
顶部