日志对于软件开发者几乎是绕不开的墙,一个强大的程序需要开发者能够随时监控到每个细节,而对于记录程序的运行状态、操作记录等等更是必不可少的,当然,有很多高大上的开源日志系统可供选择,在开源中国上能搜一大堆...
对于后台程序,我就需要一个简单、实用、稳定日志,不需要各种花里胡哨的功能,控制台输出也无妨,但最好能落地便于查找。基于此类需求,就自己开发了一个日志组件,能够融合到各类后台系统中,包含了常规的功能,能满足最低需求啦。
功能主要包括:可同步/异步记录日志、落地/控制台(可高亮)输出、简单格式化、读取/写入一组日志到指定文件中,具体的看贴上的代码,是否对于开发者是否有帮助,请批评指正咯..
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_
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); } }