microLog 后端开发指南 microLog 后端开发指南概述microLog 采用抽象工厂模式与门面模式进行架构设计能够方便地扩展新的日志后端。本文档将逐步说明如何开发自定义后端实现并提供完整的示例与最佳实践。文中示例代码仅为展示原理并没有进行编译测试架构设计持有 mutex、threadPool格式化在调用线程完成用户代码auto log createLogtinyLogType::CUSTOM(args...);log-info(hello world);LOGBackend 门面层模板类后端层自定义实现- 继承 logItfc- 实现 vlog / get_fd__ / is_valid__logFactoryHelper 抽象工厂注册所有后端类型通过索引创建实例核心接口logItfc 基类所有后端必须继承logItfc并实现以下纯虚函数classlogItfc{public:enumclasslevel_t{DEBUG0,INFO1,WARN2,ERROR3};// 必须实现日志记录核心方法virtualvoidvlog(level_t level,constchar*fmt,va_list args)0;// 必须实现获取文件描述符无则返回 -1virtualintget_fd__()const0;// 必须实现检查后端是否有效virtualboolis_valid__()const0;protected:// 可用的工具方法constchar*level_2_string__(level_t level);// 级别转字符串std::stringnowStr();// 获取当前时间字符串std::shared_ptrwheels::threadPoolpt_runner__;// 线程池指针};开发步骤步骤 1定义后端枚举类型在include/log.hpp的tinyLogType枚举中添加新类型enumclasstinyLogType{UDP0,LOCAL1,PIPE2,UNIXSOCK3,TERMINAL4,TCP5,SQLITE36,MYSQL7,POSTGRESQL8,CUSTOM9,// 添加新类型按顺序递增};步骤 2创建后端头文件创建include/logDetail/custom_log.hpp#pragmaonce#includestring#includecstdint#includelogDetail/itfc.hppnamespacemicroLog{classcustomLog:publiclogItfc{private:// 自定义成员变量std::string m_config__;boolm_inited__;public:// 构造函数接收自定义参数customLog(conststd::stringconfig);~customLog();// 必须实现的接口virtualintget_fd__()constoverride;virtualboolis_valid__()constoverride;virtualvoidvlog(level_t level,constchar*fmt,va_list args)override;// 自定义方法可选boolinit();};}// namespace microLog步骤 3实现后端创建src/logDetail/custom_log.cpp#includecstdarg#includecstdio#includelogDetail/custom_log.hppnamespacemicroLog{customLog::customLog(conststd::stringconfig):m_config__(config),m_inited__(false){if(!init()){throwstd::runtime_error(customLog 初始化失败);}}customLog::~customLog(){// 清理资源}boolcustomLog::init(){// 初始化逻辑连接资源、创建对象等m_inited__true;returntrue;}intcustomLog::get_fd__()const{// 如果后端使用文件描述符返回 fd否则返回 -1return-1;}boolcustomLog::is_valid__()const{// 返回后端是否有效returnm_inited__;}voidcustomLog::vlog(level_t level,constchar*fmt,va_list args){if(!is_valid__()){return;}// 1. 获取时间戳std::string tsnowStr();// 2. 获取级别字符串constchar*level_strlevel_2_string__(level);// 3. 格式化日志内容charbuf[4096]{0};va_list args_copy;va_copy(args_copy,args);vsnprintf(buf,sizeof(buf),fmt,args_copy);va_end(args_copy);// 4. 输出到自定义目标示例控制台printf([%s] [%s] %s\n,ts.c_str(),level_str,buf);}}// namespace microLog步骤 4注册到抽象工厂在include/log.hpp的logFactoryHelper中添加新后端#includelogDetail/custom_log.hpp// 添加头文件namespacemicroLog{usinglogFactoryHelperwheels::dm::abstractFactoryLOGudpLog,LOGlocalLog,LOGpipeLog,LOGunixSock,LOGterminal,LOGtcpLog,LOGsqlite3Log,LOGmysqlLog,LOGpostgresqlLog,LOGcustomLog// 添加新后端;}步骤 5配置 CMakeLists.txt在src/logDetail/CMakeLists.txt中添加新源文件set(LOG_DETAIL_SRC ... custom_log.cpp ... )如果新后端依赖外部库需添加相应的find_package和target_link_libraries。使用示例#includelog.hpp// 创建自定义后端实例autologcreateLogtinyLogType::CUSTOM(4,my_config);// 使用日志log-info(系统启动);log-debug(调试信息: %d,42);log-warn(警告: 资源不足);log-error(错误: %s,连接失败);完整示例Redis 后端头文件redis_log.hpp#pragmaonce#includestring#includecstdint#includelogDetail/itfc.hpp#ifdefHAVE_REDIS#includehiredis/hiredis.h#endifnamespacemicroLog{classredisLog:publiclogItfc{private:#ifdefHAVE_REDISredisContext*m_ctx__;#endifstd::string m_host__;uint16_tm_port__;std::string m_key__;boolm_inited__;public:redisLog(conststd::stringhostlocalhost,uint16_tport6379,conststd::stringkeymicroLog);~redisLog();virtualintget_fd__()constoverride;virtualboolis_valid__()constoverride;virtualvoidvlog(level_t level,constchar*fmt,va_list args)override;};}// namespace microLog实现文件redis_log.cpp#includecstdarg#includecstdio#includestring#includelogDetail/redis_log.hppnamespacemicroLog{redisLog::redisLog(conststd::stringhost,uint16_tport,conststd::stringkey):m_host__(host),m_port__(port),m_key__(key),m_inited__(false){#ifdefHAVE_REDISm_ctx__redisConnect(host.c_str(),port);if(!m_ctx__||m_ctx__-err){if(m_ctx__){std::cerrRedis 连接失败: m_ctx__-errstrstd::endl;redisFree(m_ctx__);m_ctx__nullptr;}else{std::cerrRedis 连接失败: 内存分配错误std::endl;}return;}m_inited__true;#elsethrowstd::runtime_error(Redis 支持未启用);#endif}redisLog::~redisLog(){#ifdefHAVE_REDISif(m_ctx__){redisFree(m_ctx__);m_ctx__nullptr;}#endif}intredisLog::get_fd__()const{return-1;}boolredisLog::is_valid__()const{#ifdefHAVE_REDISreturnm_inited__m_ctx__!m_ctx__-err;#elsereturnfalse;#endif}voidredisLog::vlog(level_t level,constchar*fmt,va_list args){#ifdefHAVE_REDISif(!is_valid__()){return;}charbuf[4096]{0};std::string tsnowStr();snprintf(buf,sizeof(buf),[%s] [%s] ,ts.c_str(),level_2_string__(level));charmsg[4096]{0};va_list args_copy;va_copy(args_copy,args);vsnprintf(msg,sizeof(msg),fmt,args_copy);va_end(args_copy);std::string log_entrystd::string(buf)msg;redisReply*reply(redisReply*)redisCommand(m_ctx__,LPUSH %s %s,m_key__.c_str(),log_entry.c_str());if(reply){freeReplyObject(reply);}#endif}}// namespace microLog最佳实践1. 线程安全若后端包含共享状态应使用互斥锁保护classcustomLog:publiclogItfc{private:std::mutex m_mutex__;// ...voidvlog(level_t level,constchar*fmt,va_list args){std::lock_guardstd::mutexlock(m_mutex__);// ...}};2. 资源管理在析构函数中正确释放资源customLog::~customLog(){close(m_fd__);deletem_object__;// ...}3. 错误处理使用异常或返回值处理初始化失败customLog::customLog(){if(!init()){throwstd::runtime_error(customLog 初始化失败);}}4. 条件编译对于依赖外部库的后端使用条件编译#ifdefHAVE_REDIS#includehiredis/hiredis.h#endifvoidvlog(...){#ifdefHAVE_REDIS// 实现逻辑#endif}5. 批量写入优化对于网络或数据库后端建议实现批量提交classcustomLog:publiclogItfc{private:std::vectorstd::stringm_buffer__;size_t m_batch_size__;std::mutex m_mutex__;voidflush(){// 批量提交所有缓冲数据}voidvlog(...){{std::lock_guardstd::mutexlock(m_mutex__);m_buffer__.push_back(entry);if(m_buffer__.size()m_batch_size__){flush();}}}};6. 连接重连对于网络后端实现自动重连机制boolcustomLog::check_connection(){if(!m_connected__){reconnect();}returnm_connected__;}voidvlog(...){if(!check_connection()){return;}// 写入逻辑}调试技巧1. 验证编译mkdir-pbuildcdbuild cmake..-DBUILD_TESTSONmake-j42. 添加测试创建test/test_custom.cpp#includelog.hppintmain(){autologcreateLogtinyLogType::CUSTOM(4,test_config);if(!log){std::cerr创建失败std::endl;return1;}log-info(测试信息);log-debug(测试调试);log-warn(测试警告);log-error(测试错误);std::cout测试通过std::endl;return0;}3. 启用调试模式cmake..-DBUILD_DEBUGON已有后端参考后端文件路径特点LOCALlogDetail/local.hpp环形文件嵌入式友好UDPlogDetail/udp.hpp无连接高性能TCPlogDetail/tcp.hpp可靠传输PIPElogDetail/pipe.hpp进程间通信UNIXSOCKlogDetail/unixSock.hpp本地套接字TERMINALlogDetail/terminal.hpp控制台输出彩色支持SQLITE3logDetail/sqlite3_log.hpp嵌入式数据库MYSQLlogDetail/mysql_log.hpp双连接 事务批量提交POSTGRESQLlogDetail/postgresql_log.hpp双连接 事务批量提交常见问题Q1: 如何处理阻塞操作A: 在vlog中不要执行长时间阻塞操作。如果必须阻塞可考虑使用异步线程处理。Q2: 是否需要继承std::enable_shared_from_thisA: 若需要在回调中获取自身的shared_ptr建议继承classcustomLog:publiclogItfc,publicstd::enable_shared_from_thiscustomLog{// 使用 shared_from_this() 获取 shared_ptr};Q3: 如何传递配置参数A: 通过构造函数传递。createLog使用完美转发autologcreateLogtinyLogType::CUSTOM(4,arg1,arg2,arg3);Q4: 线程池如何使用A:pt_runner__是线程池指针可用于异步任务voidvlog(...){if(pt_runner__){pt_runner__-pushTask([this,entry](){// 异步处理});}}