浮萍晓生的开发日志

记录学习的旅程,把握可预见的未来

导航

简单的后台日志组件

Posted on 2013-12-31 10:57  浮萍晓生  阅读(373)  评论(0编辑  收藏  举报

  日志对于软件开发者几乎是绕不开的墙,一个强大的程序需要开发者能够随时监控到每个细节,而对于记录程序的运行状态、操作记录等等更是必不可少的,当然,有很多高大上的开源日志系统可供选择,在开源中国上能搜一大堆...

  对于后台程序,我就需要一个简单、实用、稳定日志,不需要各种花里胡哨的功能,控制台输出也无妨,但最好能落地便于查找。基于此类需求,就自己开发了一个日志组件,能够融合到各类后台系统中,包含了常规的功能,能满足最低需求啦。

  功能主要包括:可同步/异步记录日志、落地/控制台(可高亮)输出、简单格式化、读取/写入一组日志到指定文件中,具体的看贴上的代码,是否对于开发者是否有帮助,请批评指正咯..

  

  ILog.h

#ifndef _UTIL_LOG_H_
#define _UTIL_LOG_H_
#include <vector>
#include <string>

#define UTIL_LOG_API

//日志信息默认包裹符( "[]", "()", ... )
#define DEFAULT_TEXT_LEFT_WRAP "["
#define DEFAULT_TEXT_RIGHT_WRAP "]"

//日志信息默认分割符( "\t", "-", " " )
#define DEFAULT_TEXT_SEPARATE " "

///日志信息级别定义
enum EU_LOG_SEVERITY
{
    ///调试信息输出(非DEBUG模式无效)
    EU_LOG_DEBUG=0,
    
    ///正常信息输出
    EU_LOG_MSG=1,

    ///警告信息输出
    EU_LOG_WARN=2,

    ///错误信息输出
    EU_LOG_ERR=3,

    ///无标记信息输出
    EU_LOG_CLEAR=4
};

///日志输出高亮颜色定义
enum EU_TEXT_COLOR
{
    EU_TEXT_BLACK=0,
    EU_TEXT_RED=1,
    EU_TEXT_GREEN=2,
    EU_TEXT_BROWN=3,
    EU_TEXT_BLUE=4,
    EU_TEXT_MAGENTA=5,
    EU_TEXT_CYAN=6,
    EU_TEXT_WHITE=7,
    EU_TEXT_YELLOW=8,
    EU_TEXT_LRED=9,
    EU_TEXT_LGREEN=10,
    EU_TEXT_LBLUE=11,
    EU_TEXT_LMAGENTA=12,
    EU_TEXT_LCYAN=13,
    EU_TEXT_LWHITE=14
};

///日志回调函数定义
///@param severity信息级别
///@param msg日志信息
///@param len日志信息长度
typedef void (* util_log_cb)(int severity, const char *msg, size_t len);

///类ILog
///@remark 日志组件接口定义
class ILog
{
public:
    ///初始化日志组件
    ///@param async是否异步
    ///@param print是否输出显示
    ///@param save是否保存至文件
    ///@param defaultPath日志默认文件路径, 格式"\\xxx\\xxx"
    ///@param cb记录日志后回调函数指针
    ///@return 成功返回0, 失败返回负值
    virtual int init(bool async=true, bool print=true, bool save=true, 
        const char *defaultPath=".\\log", util_log_cb cb=0) = 0;

    ///设置日志打包符号
    ///@param left_wrap左包裹符
    ///@param right_wrap右包裹符
    ///@param separate分割符
    virtual void set_text_packsign(const char *left_wrap="", const char *right_wrap="", const char *separate="\t") = 0;

    ///设置日志输出高亮配置
    ///@param config_path配置路径
    virtual void set_text_highlight(const char *config_path) = 0;

    ///运行日志组件
    virtual void run() = 0;

    ///停止日志组件
    virtual void stop() = 0;

    ///记录日志, 包括屏幕显示和储存至默认路径下文件
    ///@param severity信息级别
    ///@param fmt参数传入格式
    virtual void log(EU_LOG_SEVERITY severity, const char *fmt, ...) = 0;

    ///记录日志, 储存至指定路径下文件, 文件格式"\\xxx\\xxx"
    ///@param filePath指定路径下文件
    ///@param severity信息级别
    ///@param fmt参数传入格式
    virtual void log2file(const char *filePath, EU_LOG_SEVERITY severity, const char *fmt, ...) = 0;

    ///读取指定路径下日志文件所有内容
    ///@param fileName文件名称
    ///@param result文件所有日志信息
    virtual void read_file_text(const char *fileName, std::vector<std::string> &result) = 0;

    ///写入内容到指定路径下日志文件
    ///@param fileName文件名称
    ///@param result日志内容
    virtual void write_file_text(const char *fileName, std::vector<std::string> &result) = 0;

protected:
    virtual ~ILog(){}
};

///创建日志组件
UTIL_LOG_API ILog *CreateLoger();

///销毁日志组件
UTIL_LOG_API void DestroyLoger(ILog *&p);

#endif//_UTIL_LOG_H_

  

  Log.h

#ifndef _UTIL_LOG_IMPL_H_
#define _UTIL_LOG_IMPL_H_
#include "ILog.h"
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/make_shared.hpp>
#include <list>

///日志信息最大长度定义, 10k
const int log_text_max_len = 1024 * 10;

///日志信息结构定义
struct log_message
{
public:
    log_message(EU_LOG_SEVERITY s,
        const std::string &p, 
        const std::string &t) : 
    m_severity(s),
        m_path(p),
        m_text(t){}

    EU_LOG_SEVERITY m_severity;
    std::string m_path;
    std::string m_text;
};
typedef boost::shared_ptr<log_message> MessagePtr;

class util_log_impl : public ILog
{
public:
    util_log_impl();
    virtual ~util_log_impl();

    virtual int init(bool async, bool print, bool save, const char *defaultPath, util_log_cb cb);

    virtual void set_text_packsign(const char *left_wrap, const char *right_wrap, const char *separate);

    virtual void set_text_highlight(const char *config_path);

    virtual void run();

    virtual void stop();

    virtual void log(EU_LOG_SEVERITY severity, const char *fmt, ...);

    virtual void log2file(const char *filePath, EU_LOG_SEVERITY severity, const char *fmt, ...);

    virtual void read_file_text(const char *fileName, std::vector<std::string> &result);

    virtual void write_file_text(const char *fileName, std::vector<std::string> &result);

private:
    ///设置日志输出高亮
    void set_text_highlight(std::string text, EU_TEXT_COLOR color);

    ///打包日志信息
    void pack_log_text(const char *filePath, EU_LOG_SEVERITY severity, const char *fmt, va_list ap);
    
    ///处理日志信息
    void handle_log_msg(const log_message &msg);

    ///打印日志信息
    void print_log_text(const std::string &logText);

    ///保存日志信息
    void save_log_text(const std::string &filePath, const std::string &logText);

    ///获取日志文件名称包括路径
    std::string get_log_file_name(const std::string &filePath);

    ///获取日志文件路径
    std::string get_log_file_path(const std::string &fileName);

    ///更新默认日志文件
    void update_default_file();

    ///异步处理日志
    void async_process_log();

    ///克隆日志队列当前所有信息
    void clone_log_list(std::list<MessagePtr> &messages);

    ///检查/创建路径
    bool check_file_path(const std::string &filePath);
    void create_file_path(const std::string &filePath);

private:
    //日志状态控制
    bool m_run_state;
    bool m_async_state;
    bool m_print_state;
    bool m_save_state;
    std::string m_default_path;
    util_log_cb m_log_cb;

    //日志打包符号
    std::string m_left_wrap;
    std::string m_right_wrap;
    std::string m_separate;

    //日志输出高亮
    bool m_custom_highlight;
    std::map<std::string, EU_TEXT_COLOR> m_text_highlight_grp;
    
    //当前日志保存文件句柄
    FILE *m_current_file;
    std::string m_current_date;

    //日志信息队列
    boost::mutex m_mt_msg;
    boost::condition m_con_msg;
    std::list<MessagePtr> m_message_list;
    boost::shared_ptr<boost::thread> m_th_process_log;
};

#endif//_UTIL_LOG_IMPL_H_
View Code

 

  Log.cpp

#include "Log.h"
#include "utils.h"
#include "IniFile.h"
#include <io.h>
#include <direct.h>
#include <stdarg.h>
#include <Windows.h>
#include <iostream>

static const char *log_text_color_key[EU_TEXT_LWHITE+1] = 
{
    "text_black",
    "text_red",
    "text_green",
    "text_brown",
    "text_blue",
    "text_magenta",
    "text_cyan",
    "text_white",
    "text_yellow",
    "text_lred",
    "text_lgreen",
    "text_lblue",
    "text_lmagenta",
    "text_lcyan",
    "text_lwhite"
};

static const WORD log_text_color_value[EU_TEXT_LWHITE+1] =
{
    0, //BLACK
    FOREGROUND_RED, //RED
    FOREGROUND_GREEN, //GREEN
    FOREGROUND_RED | FOREGROUND_GREEN, //BROWN
    FOREGROUND_BLUE, //BLUE
    FOREGROUND_RED | FOREGROUND_BLUE, //MAGENTA
    FOREGROUND_GREEN | FOREGROUND_BLUE, //CYAN
    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, //WHITE
    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, //YELLOW
    FOREGROUND_RED | FOREGROUND_INTENSITY, //RED_BOLD
    FOREGROUND_GREEN | FOREGROUND_INTENSITY, //GREEN_BOLD
    FOREGROUND_BLUE | FOREGROUND_INTENSITY, //BLUE_BOLD
    FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY, //MAGENTA_BOLD
    FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, //CYAN_BOLD
    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY //WHITE_BOLD
};

ILog *CreateLoger()
{
    return new util_log_impl();
}
void DestroyLoger(ILog *&p)
{
    if (p)
    {
        delete (util_log_impl *)p;
        p = 0;
    }
}

util_log_impl::util_log_impl()
{
    m_run_state = false;
    m_async_state = false;
    m_print_state = false;
    m_save_state  = false;
    m_log_cb = 0;
    m_current_file = 0;

    m_custom_highlight = false;
}

util_log_impl::~util_log_impl()
{
    stop();
}

int util_log_impl::init(bool async, bool print, bool save, const char *defaultPath, util_log_cb cb)
{
    m_run_state = false;
    m_async_state = async;
    m_print_state = print;
    m_save_state  = save;

    if (m_save_state && defaultPath)
    {
        m_default_path = defaultPath;

        if (!check_file_path(m_default_path))
        {
            create_file_path(m_default_path);
        }
    }

    m_log_cb = cb;
    m_current_file = 0;

    char temp[32] = {0};
    NS_UTILS::get_current_date(temp, sizeof(temp));
    m_current_date = temp;

    m_left_wrap = DEFAULT_TEXT_LEFT_WRAP;
    m_right_wrap = DEFAULT_TEXT_RIGHT_WRAP;
    m_separate = DEFAULT_TEXT_SEPARATE;

    return 0;
}

void util_log_impl::set_text_packsign(const char *left_wrap, const char *right_wrap, const char *separate)
{
    if (m_run_state)
    {
        //已启动不处理
        return;
    }

    m_left_wrap = left_wrap;
    m_right_wrap = right_wrap;
    m_separate = separate;
}

void util_log_impl::set_text_highlight(const char *config_path)
{
    if (m_run_state)
    {
        return;
    }

    if (config_path)
    {
        IniFile config(config_path);
        config.setSection("LOGHIGHLIGHTCONFIG");

        std::string color_value;
        for (int i=EU_TEXT_BLACK; i<EU_TEXT_LWHITE; ++i)
        {
            color_value = config.readStr(log_text_color_key[i], "");
            if (!color_value.empty())
            {
                set_text_highlight(color_value, (EU_TEXT_COLOR)i);
            }
        }

        if (!m_text_highlight_grp.empty())
        {
            m_custom_highlight = true;
        }
    }
}

void util_log_impl::set_text_highlight(std::string text, EU_TEXT_COLOR color)
{
    text = text + std::string("|");

    char *temp = (char *)text.c_str();
    char *p = strtok(temp, "|");
    while (p)
    {
        if (strlen(p)>0)
        {
            m_text_highlight_grp.insert(std::make_pair(std::string(p), color));
        }

        p = strtok(NULL, "|");
    }
}

void util_log_impl::run()
{
    if (m_run_state)
    {
        return;
    }
    m_run_state = true;

    if (m_async_state && !m_th_process_log)
    {
        m_th_process_log = boost::make_shared<boost::thread>(boost::bind(&util_log_impl::async_process_log, this));
    }
}

void util_log_impl::stop()
{
    if (!m_run_state)
    {
        //已停止不处理
        return;
    }
    m_run_state = false;

    if (m_async_state && m_th_process_log)
    {
        //通知线程退出
        {
            boost::mutex::scoped_lock lock(m_mt_msg);
            m_con_msg.notify_all();
        }
        m_th_process_log->join();
        m_th_process_log.reset();
    }

    if (m_current_file)
    {
        //关闭句柄
        fclose(m_current_file);
        m_current_file = 0;
    }
}

void util_log_impl::log(EU_LOG_SEVERITY severity, const char *fmt, ...)
{
    assert(fmt);

    if (!m_run_state)
    {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    pack_log_text(0, severity, fmt, ap);
    va_end(ap);
}

void util_log_impl::log2file(const char *filePath, EU_LOG_SEVERITY severity, const char *fmt, ...)
{
    assert(filePath && fmt);

    if (!m_run_state)
    {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    pack_log_text(filePath, severity, fmt, ap);
    va_end(ap);
}

void util_log_impl::read_file_text(const char *fileName, std::vector<std::string> &result)
{
    if (fileName)
    {
        FILE *file = _fsopen(fileName, "r", _SH_DENYWR);
        if (file)
        {
            char temp[log_text_max_len] = {0};
            while (fgets(temp, log_text_max_len, file))
            {
                result.push_back(temp);
                temp[0] = '\0';
            }

            fclose(file);
        }
    }
}

void util_log_impl::write_file_text(const char *fileName, std::vector<std::string> &result)
{
    if (fileName)
    {
        std::string file_path = get_log_file_path(fileName);
        if (!file_path.empty() && !check_file_path(file_path))
        {
            create_file_path(file_path);
        }

        FILE *file = _fsopen(fileName, "w+", _SH_DENYWR);
        if (file)
        {
            std::string temp;
            for (size_t i=0; i<result.size(); ++i)
            {
                temp = result[i] + std::string("\r\n");
                fwrite(temp.c_str(), 1, temp.length(), file);
            }

            fflush(file);
            fclose(file);
        }
    }
}

void util_log_impl::pack_log_text(const char *filePath, EU_LOG_SEVERITY severity, const char *fmt, va_list ap)
{
    assert(fmt);

    char buffer[log_text_max_len] = {0};
    my_vsprintf(buffer, sizeof(buffer)-1, fmt, ap);

    char temp[32] = {0};
    NS_UTILS::get_current_time(temp, sizeof(temp));
    std::string now_time = temp;

    //设置信息级别
    const char *severity_str = 0;
    switch(severity)
    {
    case EU_LOG_DEBUG:
        {
#ifndef _DEBUG
            return;
#endif
            severity_str = "调试";
        }
        break;
    case EU_LOG_MSG:
        {
            severity_str = "正常";
        }
        break;
    case EU_LOG_WARN:
        {
            severity_str = "警告";
        }
        break;
    case EU_LOG_ERR:
        {
            severity_str = "错误";
        }
        break;
    case EU_LOG_CLEAR:
    default:
        {
            severity_str = "";
        }
    }

    std::string text;
    if (strlen(severity_str) > 0)
    {
        text = m_left_wrap + now_time + m_right_wrap + m_separate
            + m_left_wrap + severity_str + m_right_wrap + m_separate
            + buffer + "\r\n";
    }
    else
    {
        text = m_left_wrap + now_time + m_right_wrap + m_separate
            + buffer + "\r\n";
    }

    std::string path("");
    if (filePath)
    {
        //指定特殊存储路径
        path = filePath;
    }

    MessagePtr packet = boost::make_shared<log_message>(severity, path, text);
    if (m_async_state)
    {
        //数据进队列, 通知线程处理
        boost::mutex::scoped_lock lock(m_mt_msg);
        m_message_list.push_back(packet);
        m_con_msg.notify_one();
    }
    else
    {
        handle_log_msg(*packet.get());
    }
}

void util_log_impl::handle_log_msg(const log_message &msg)
{
    if (m_save_state || !msg.m_path.empty())
    {
        save_log_text(msg.m_path, msg.m_text);
    }

    if (m_print_state)
    {
        print_log_text(msg.m_text);
    }

    if (m_log_cb)
    {
        //回调通知
        m_log_cb(msg.m_severity, msg.m_text.c_str(), msg.m_text.length());
    }
}

void util_log_impl::print_log_text(const std::string &logText)
{
    if (m_custom_highlight)
    {
        int color_index = -1;

        //查找相应的颜色
        std::map<std::string, EU_TEXT_COLOR>::iterator iter = m_text_highlight_grp.begin();
        for (; iter!=m_text_highlight_grp.end(); ++iter)
        {
            if (logText.find(iter->first) != std::string::npos)
            {
                color_index = iter->second;
                break;
            }
        }

        if (color_index != -1)
        {
            //设置输出颜色
            HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
            SetConsoleTextAttribute(hConsole, log_text_color_value[color_index]);

            printf(logText.c_str());

            //重置输出颜色
            SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED);
            return;
        }
    }

    printf(logText.c_str());
}

void util_log_impl::save_log_text(const std::string &filePath, const std::string &logText)
{
    if (!filePath.empty())
    {
        if (!check_file_path(filePath))
        {
            create_file_path(filePath);
        }

        std::string fileName = get_log_file_name(filePath);
        FILE *file = _fsopen(fileName.c_str(), "a", _SH_DENYWR);
        if (file)
        {
            fwrite(logText.c_str(), 1, logText.length(), file);
            fflush(file);
            fclose(file);
        }
    }
    else
    {
        //更新日志文件
        update_default_file();

        if (m_current_file)
        {
            fwrite(logText.c_str(), 1, logText.length(), m_current_file);
            fflush(m_current_file);
        }
    }
}

std::string util_log_impl::get_log_file_name(const std::string &filePath)
{
    char temp[32] = {0};
    NS_UTILS::get_current_date(temp, sizeof(temp));
    std::string now_date = temp;

    return (filePath + "\\"+ now_date + ".log");
}

std::string util_log_impl::get_log_file_path(const std::string &fileName)
{
    size_t pos = fileName.find_last_of("\\");
    if (pos != std::string::npos)
    {
        return fileName.substr(0, pos);
    }
    else
    {
        return std::string("");
    }
}

void util_log_impl::update_default_file()
{
    char temp[32] = {0};
    NS_UTILS::get_current_date(temp, sizeof(temp));
    std::string now_date = temp;

    if (!m_current_file || now_date!=m_current_date)
    {
        if (m_current_file)
        {
            fclose(m_current_file);
            m_current_file = 0;
        }

        //更新文件日期
        m_current_date = now_date;

        std::string fileName = m_default_path+ "\\"+ m_current_date + ".log";
        m_current_file = _fsopen(fileName.c_str(), "a", _SH_DENYWR);
    }
}

void util_log_impl::async_process_log()
{
    while (m_run_state)
    {
        std::list<MessagePtr> messages;
        clone_log_list(messages);

        while (!messages.empty())
        {
            handle_log_msg(*messages.front());
            messages.pop_front();
        }
    }
}

void util_log_impl::clone_log_list(std::list<MessagePtr> &messages)
{
    boost::mutex::scoped_lock lock(m_mt_msg);
    if (m_message_list.empty())
    {
        //等待日志信息写入
        m_con_msg.wait(m_mt_msg);
    }
    
    messages = m_message_list;
    m_message_list.clear();
}

bool util_log_impl::check_file_path(const std::string &filePath)
{
    return (::access(filePath.c_str(), 0)==0);
}

void util_log_impl::create_file_path(const std::string &filePath)
{
    char *tmp = (char *)filePath.c_str();
    int len = strlen(tmp);
    for(int i=0; i<len; ++i)
    {
        if(tmp[i]=='\\')
        {
            tmp[i]='\0';
            _mkdir(tmp);
            tmp[i]='\\';
        }
    }

    if(len>0)
    {
        _mkdir(tmp);
    }
}
View Code