#include "../log4cplus/logger.h" #include "../log4cplus/configurator.h" #include "../log4cplus/fileappender.h" #include "../log4cplus/layout.h" #include "../log4cplus/ndc.h" //#include "../log4cplus/log4.h" #include "../log4cplus/spi/loggingevent.h" #include #include #include #include #include #include #include #include #include //目录创建 #include #include #include "../json/mms_json_inter.h" #include "../mms/rdb_client.h" #include "../include/node.h"//lnk20241223 #include "../log4cplus/log4.h"//后移防止min/max定义冲突 ///////////////////////////////////////////////lnk20260303添加日志上送控制 #include #include #include #include #include struct LogLevelCache { //记录日志等级的缓存,减少频繁访问全局map的开销 std::unordered_map term_min; // terminal_id -> min level std::unordered_map mp_min; // mp_id -> min level }; // append线程只读,不加锁 static std::shared_ptr g_level_cache_sp; /////////////////////////////////////////////// //用来控制日志上送的结构 struct LOGEntry { std::string id; std::string level; // terminal / measurepoint int code; //code int min_grade; //允许上送的最低日志等级 int countdown; //自动关闭的倒计时,单位秒 }; //日志上送map管理 std::map g_log_entries; pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER; /////////////////////////////////////////////外部定义 extern unsigned int g_node_id; extern int g_front_seg_index; extern std::string FRONT_INST; extern char subdir[128]; extern node_t* g_node; //mq extern QMutex kafka_data_list_mutex; //Kafka发送数据锁 extern QList kafka_data_list; //kafka发送数据链表 //辅助函数 extern std::string intToString(int number); //日志主题 extern std::string G_LOG_TOPIC; // 日志限流配置 extern int G_LOG_RATE_RESET_SEC; extern int G_LOG_RATE_LIMIT_SEC; extern int G_LOG_RATE_KEEP_ALL_MS; extern int G_LOG_RATE_KEEP_BURST_MS; extern int G_LOG_RATE_KEEP_BURST_COUNT; extern int G_LOG_RATE_KEEP_HIGHFREQ_COUNT; ///////////////////////////////////////////////////////// //log4命名空间 using namespace log4cplus; using namespace log4cplus::helpers; /////////////////////////////////////////////////////////////// static std::string extract_logger_id(const std::string& logger_name); ////////////////////////////////////////////////////////辅助函数 ///////////////////////////////////////////////////////lnk20260303添加日志上送控制 //用于在台账中查询日志上送控制项的日志等级字符串转换为整数 static int int_to_loglevel(int v, int default_level = WARN_LOG_LEVEL) { switch (v) { case 0: return ERROR_LOG_LEVEL; case 1: return WARN_LOG_LEVEL; case 2: return INFO_LOG_LEVEL; case 3: return DEBUG_LOG_LEVEL; default: return default_level; } } // 构建日志等级缓存,减少 append 线程访问全局 map 的开销 static LogLevelCache* build_cache_unlocked() { LogLevelCache* nc = new LogLevelCache; // 没 node / 没 clients:返回空 cache(查不到会兜底 WARN) if (!g_node || g_node->n_clients <= 0 || !g_node->clients) { return nc; } // 你 LD_info 是固定 10 个就用 10;如果不是,改成实际长度 const int MAX_LD = 10; for (int iedno = 0; iedno < g_node->n_clients; ++iedno) { ied_t* ied = g_node->clients[iedno]; if (!ied || !ied->usr_ext) continue; ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext; // terminal_id 必须有效 const char* tid = ied_usr->terminal_id; if (!tid || tid[0] == '\0') continue; const std::string terminal_id(tid); // 装置级阈值:0~3 -> log4cplus level(默认 WARN) const int term_lv = int_to_loglevel(ied_usr->log_level, WARN_LOG_LEVEL); nc->term_min[terminal_id] = term_lv; // 监测点阈值:若非法/缺失,兜底用装置级 term_lv for (int i = 0; i < MAX_LD; ++i) { const char* mp = ied_usr->LD_info[i].mp_id; if (!mp || mp[0] == '\0') continue; const std::string mp_id(mp); // mp 的 log_level 优先;非法则用 term_lv const int mp_lv = int_to_loglevel(ied_usr->LD_info[i].log_level, term_lv); nc->mp_min[mp_id] = mp_lv; } } return nc; } //用于更新日志等级缓存的函数,获取最新的日志上送控制项并更新到缓存中,调用时需持锁 void refresh_log_level_cache_locked() { std::shared_ptr nc(build_cache_unlocked()); std::atomic_store(&g_level_cache_sp, nc); } const int LOGTYPE_DEFAULT = LOG_CODE_OTHER; // 默认日志类型,表示不区分具体类型的日志上送控制 static const int LOGTYPE_WILDCARD = 999; // 用于匹配任意日志类型的特殊值 static const char* ID_WILDCARD = "all"; // 用于匹配任意 ID 的特殊值 static std::string build_debug_key(const std::string& id, const std::string& level, // terminal/measurepoint/process int code) // 日志类型 { std::ostringstream oss; oss << id << "|" << level << "|" << code; return oss.str(); } static bool find_entry_allow(const std::string& key, int level_val) { std::map::iterator it = g_log_entries.find(key); if (it == g_log_entries.end() || it->second.countdown <= 0) return false; return level_val >= it->second.min_grade; } static bool allow_low_level_send(const std::string& id, const std::string& level_str, int code, int level_val) { pthread_mutex_lock(&g_log_mutex); // 1) 精确 if (find_entry_allow(build_debug_key(id, level_str, code), level_val)) { pthread_mutex_unlock(&g_log_mutex); return true; } // 2) logtype 通配 if (find_entry_allow(build_debug_key(id, level_str, LOGTYPE_WILDCARD), level_val)) { pthread_mutex_unlock(&g_log_mutex); return true; } // 3) id 通配 if (find_entry_allow(build_debug_key(ID_WILDCARD, level_str, code), level_val)) { pthread_mutex_unlock(&g_log_mutex); return true; } // 4) 双通配 if (find_entry_allow(build_debug_key(ID_WILDCARD, level_str, LOGTYPE_WILDCARD), level_val)) { pthread_mutex_unlock(&g_log_mutex); return true; } pthread_mutex_unlock(&g_log_mutex); return false; } static int get_min_send_level_cached(const std::string& level_str, const std::string& logger_name) { const int DEFAULT_LEVEL = WARN_LOG_LEVEL; if (level_str == "process") return DEFAULT_LEVEL; const std::string id = extract_logger_id(logger_name); //terminal. / monitor. if (id.empty()) return DEFAULT_LEVEL; std::shared_ptr c = std::atomic_load(&g_level_cache_sp); if (!c) return DEFAULT_LEVEL; if (level_str == "terminal") { auto it = c->term_min.find(id); return (it != c->term_min.end()) ? it->second : DEFAULT_LEVEL; } if (level_str == "measurepoint") { auto it = c->mp_min.find(id); return (it != c->mp_min.end()) ? it->second : DEFAULT_LEVEL; } return DEFAULT_LEVEL; } ///////////////////////////////////////////////////////lnk20260303添加日志上送控制 std::string get_front_type_from_subdir() { if (std::strstr(subdir, "cfg_3s_data") != NULL) return "realTime"; else if (std::strstr(subdir, "cfg_soe_comtrade") != NULL) return "comtrade"; else if (std::strstr(subdir, "cfg_recallhis_data") != NULL) return "recall"; else if (std::strstr(subdir, "cfg_stat_data") != NULL) return "stat"; else return "unknown"; } // 递归创建目录 bool create_directory_recursive(const std::string& path) { size_t pos = 0; std::string current; while (pos != std::string::npos) { pos = path.find('/', pos + 1); current = path.substr(0, pos); if (!current.empty() && access(current.c_str(), F_OK) != 0) { if (mkdir(current.c_str(), 0755) != 0) { perror(("mkdir failed: " + current).c_str()); return false; } } } return true; } ////////////////////////////////////////////////////////////////////// std::string extract_logger_id(const std::string& logger_name) { if (logger_name == "process") return "process"; size_t pos = logger_name.find('.'); if (pos == std::string::npos) return ""; // 取第一个 '.' 后面的全部:terminal. / monitor. return logger_name.substr(pos + 1); } std::string get_level_str(int level) { switch (level) { case 10000: return "DEBUG"; case 20000: return "NORMAL"; // 或 "INFO" 根据你业务定义 case 30000: return "WARN"; case 40000: return "ERROR"; default: return "UNKNOWN"; } } ////////////////////////////////////////////////////////////////////// TypedLogger::TypedLogger() {} TypedLogger::TypedLogger(const Logger& l, int t) : logger(l), code(t) {} DebugSwitch::DebugSwitch() : debug_open(false), min_level(WARN_LOG_LEVEL) {} void DebugSwitch::open() { debug_open = true; } void DebugSwitch::close() { debug_open = false; targets.clear(); type_enable.clear(); } void DebugSwitch::set_target(const std::string& name) { targets.insert(name); } void DebugSwitch::set_level(int level) { min_level = level; } void DebugSwitch::enable_type(int type) { type_enable[type] = true; } void DebugSwitch::disable_type(int type) { type_enable[type] = false; } bool DebugSwitch::match(const std::string& logger_name, int level, int logtype) { if (!debug_open) return false; if (level < min_level) return false; if (type_enable.count(logtype) && !type_enable[logtype]) return false; std::set::iterator it; for (it = targets.begin(); it != targets.end(); ++it) { if (logger_name.find(*it) != std::string::npos) return true; } return false; } std::map logger_map; DebugSwitch g_debug_switch; // ★新增:定义 TLS 变量,默认 0 LOG_TLS int g_log_code_tls = 0; class SendAppender : public Appender { protected: void append(const spi::InternalLoggingEvent& event) override { std::string logger_name = event.getLoggerName(); int level = event.getLogLevel(); std::string msg = event.getMessage(); std::string level_str; if (logger_name.find("process") == 0) level_str = "process"; else if (logger_name.find("monitor") != std::string::npos) level_str = "measurepoint"; else if (logger_name.find("terminal") != std::string::npos) level_str = "terminal"; else level_str = "process"; int code = g_log_code_tls; // TLS code int safe_logtype = code; // ★关键:用 code 当 logtype 维度 bool allow_send = false; int min_send_level = get_min_send_level_cached(level_str, logger_name); // ① 高于台账阈值:直接上送 if (level >= min_send_level) { allow_send = true; } else { // ② 低等级:默认不上送,除非命令打开 std::string ctrl_id; if (level_str == "process") ctrl_id = "process"; else ctrl_id = extract_logger_id(logger_name); if (!ctrl_id.empty()) { allow_send = allow_low_level_send(ctrl_id, level_str, safe_logtype, level); } } if (!allow_send) return; // ③ 限频:同一条日志 const std::string rkey = make_key(logger_name, level, code, msg); uint64_t suppressed_before_emit = 0; if (!should_emit(rkey, suppressed_before_emit)) return; // 如果本次输出前压掉过日志,则在 log 文本后追加统计 std::string final_msg = msg; if (suppressed_before_emit > 0) { std::ostringstream suppressed_oss; suppressed_oss << msg << " 【中间重复抑制 " << suppressed_before_emit << " 条】"; final_msg = suppressed_oss.str(); } std::ostringstream oss; oss << "{\"processNo\":\"" << intToString(g_front_seg_index) << "\",\"nodeId\":\"" << FRONT_INST << "\",\"businessId\":\"" << extract_logger_id(logger_name) << "\",\"level\":\"" << level_str << "\",\"grade\":\"" << get_level_str(level) << "\",\"logtype\":\"" << safe_logtype << "\",\"frontType\":\"" << get_front_type_from_subdir() << "\",\"code\":" << code << ",\"log\":\"" << escape_json(final_msg) << "\"}"; Ckafka_data_t connect_info; connect_info.strTopic = QString::fromStdString(G_LOG_TOPIC); connect_info.strText = QString::fromStdString(oss.str()); kafka_data_list_mutex.lock(); kafka_data_list.append(connect_info); kafka_data_list_mutex.unlock(); } std::string escape_json(const std::string& input) { std::ostringstream ss; for (unsigned int i = 0; i < input.size(); ++i) { switch (input[i]) { case '\\': ss << "\\\\"; break; case '"': ss << "\\\""; break; case '\n': ss << "\\n"; break; case '\r': ss << "\\r"; break; case '\t': ss << "\\t"; break; default: ss << input[i]; break; } } return ss.str(); } virtual void close() { // 可空实现 } public: SendAppender() {} virtual ~SendAppender() { destructorImpl(); // 重要!释放 log4cplus 基类资源 } //////////////////////////////////////////////////////////////////20260303添加日志上送控制 - 频率限制实现 private: struct RateState { uint64_t pass_count; // 当前周期内已放行条数 uint64_t suppressed_count; // 当前被抑制条数 std::chrono::steady_clock::time_point last_emit; std::chrono::steady_clock::time_point last_seen; std::chrono::steady_clock::time_point last_reset; bool has_emit; RateState() : pass_count(0), suppressed_count(0), last_emit(), last_seen(), last_reset(), has_emit(false) {} }; static std::unordered_map s_rate_map; static std::mutex s_rate_mutex; static std::string make_key(const std::string& logger_name, int level, int code, const std::string& msg) { std::ostringstream oss; oss << logger_name << "|" << level << "|" << code ; //<< "|" << msg; return oss.str(); } static bool should_emit(const std::string& key, uint64_t& suppressed_before_emit) { using namespace std::chrono; const auto now = steady_clock::now(); suppressed_before_emit = 0; std::lock_guard lk(s_rate_mutex); RateState& st = s_rate_map[key]; const int RESET_SEC = G_LOG_RATE_RESET_SEC ; // 1小时重置 const int LIMIT_SEC = G_LOG_RATE_LIMIT_SEC ; // 进入限流后:1分钟1条 // 初始化 / 强制每小时重置 if (st.last_reset.time_since_epoch().count() == 0) { st.last_reset = now; } else { auto since_reset = duration_cast(now - st.last_reset).count(); if (since_reset >= RESET_SEC) { st = RateState(); st.last_reset = now; } } // 计算当前频率档位(按“本次与上次看到该key的间隔”判断) // >=60秒/条:全部保留 // [1秒, 60秒):保留前60条,然后1分钟1条 // <1秒:保留前10条,然后1分钟1条 int allow_burst = 0; if (st.last_seen.time_since_epoch().count() == 0) { // 第一次看到,先按“全部保留”处理 allow_burst = -1; } else { auto gap_ms = duration_cast(now - st.last_seen).count(); if (gap_ms >= G_LOG_RATE_KEEP_ALL_MS) { allow_burst = -1; // 全部保留 } else if (gap_ms >= G_LOG_RATE_KEEP_BURST_MS) { allow_burst = G_LOG_RATE_KEEP_BURST_COUNT; // 前60条 } else { allow_burst = G_LOG_RATE_KEEP_HIGHFREQ_COUNT; // 前10条 } } st.last_seen = now; // 档位1:全部保留 if (allow_burst == -1) { suppressed_before_emit = st.suppressed_count; st.suppressed_count = 0; st.pass_count++; st.last_emit = now; st.has_emit = true; return true; } // 档位2/3:先放前N条 if (st.pass_count < (uint64_t)allow_burst) { suppressed_before_emit = st.suppressed_count; st.suppressed_count = 0; st.pass_count++; st.last_emit = now; st.has_emit = true; return true; } // 超过前N条后:进入 1分钟1条 if (!st.has_emit) { suppressed_before_emit = st.suppressed_count; st.suppressed_count = 0; st.pass_count++; st.last_emit = now; st.has_emit = true; return true; } auto elapsed = duration_cast(now - st.last_emit).count(); if (elapsed >= LIMIT_SEC) { suppressed_before_emit = st.suppressed_count; st.suppressed_count = 0; st.pass_count++; st.last_emit = now; st.has_emit = true; return true; } // 本条被抑制 st.suppressed_count++; return false; } }; //////////////////////////////////////////////////////////////////20260303添加日志上送控制 - 频率限制实现 std::unordered_map SendAppender::s_rate_map; std::mutex SendAppender::s_rate_mutex; // 外部线程中调用:每秒更新所有倒计时,0 则删除 void update_log_entries_countdown() { pthread_mutex_lock(&g_log_mutex); std::map::iterator it = g_log_entries.begin(); while (it != g_log_entries.end()) { if (it->second.countdown > 0) { it->second.countdown--; if (it->second.countdown == 0) { std::cout << "[LOG] debug日志上送自动关闭: " << it->first << std::endl; it = g_log_entries.erase(it); continue; } } ++it; } pthread_mutex_unlock(&g_log_mutex); } void process_log_command(const std::string& id, const std::string& level, const std::string& grade, int code) { if (level != "terminal" && level != "measurepoint" && level != "process") return; int grade_level = (grade == "DEBUG") ? DEBUG_LOG_LEVEL : INFO_LOG_LEVEL; std::string key = build_debug_key(id, level, code); pthread_mutex_lock(&g_log_mutex); LOGEntry& entry = g_log_entries[key]; entry.id = id; entry.level = level; entry.code = code; entry.min_grade = grade_level; entry.countdown = 60; pthread_mutex_unlock(&g_log_mutex); } Logger init_logger(const std::string& full_name, const std::string& file_dir, const std::string& base_file, SharedAppenderPtr fileAppender) { create_directory_recursive(file_dir); Logger logger = Logger::getInstance(full_name); if (!fileAppender) { std::string file_path = file_dir + "/" + base_file + ".log"; fileAppender = SharedAppenderPtr(new RollingFileAppender(file_path, 1 * 1024 * 1024, 2)); fileAppender->setLayout(std::auto_ptr( new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); } SharedAppenderPtr sendAppender(new SendAppender()); logger.addAppender(fileAppender); logger.addAppender(sendAppender); logger.setLogLevel(DEBUG_LOG_LEVEL); return logger; } // 重载版本:无 Appender 传入时调用上面的实现 log4cplus::Logger init_logger(const std::string& full_name, const std::string& file_dir, const std::string& base_file) { return init_logger(full_name, file_dir, base_file, log4cplus::SharedAppenderPtr()); // 空指针 } //进程的日志 void init_logger_process() { std::string base_dir = std::string("/FeProject/") + subdir + "/processNo" + intToString(g_front_seg_index) + "/log"; logger_map["process"] = TypedLogger(init_logger(std::string("process"), base_dir, std::string("process")), LOGTYPE_DEFAULT); std::cout << "process log init ok" << std::endl; } //终端的日志 void init_loggers_bydevid(const char* dev_id) { if (!dev_id) return; std::string terminal_id(dev_id); // 转为 std::string std::string base_dir = std::string("/FeProject/") + subdir + "/processNo" + intToString(g_front_seg_index) + "/log"; ied_t* ied = NULL; int iedno; ied_usr_t* ied_usr = NULL; for (iedno = 0; iedno < g_node->n_clients; iedno++) { ied = g_node->clients[iedno]; if (!ied || !ied->usr_ext) { std::cout << "ied No."<< iedno << " is null" << std::endl; continue; } ied_usr = (ied_usr_t*)ied->usr_ext; //跳过不匹配的终端 if (strcmp(ied_usr->terminal_id, dev_id) != 0) continue; const char* ip_cstr = (ied->channel[0].addr_str != NULL) ? ied->channel[0].addr_str : "unknown"; std::string ip_str(ip_cstr); std::string device_dir = base_dir + "/" + ip_str; std::string device_key = std::string("terminal.") + dev_id; // 添加判断:终端日志 logger 是否已存在 if (logger_map.find(device_key) == logger_map.end()) { // 所有终端日志(com 和 data)写到同一个 device 日志文件中 std::string file_path_t = device_dir + "/" + dev_id + ".log"; // 共用一个 appender 实例 SharedAppenderPtr device_appender = SharedAppenderPtr(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2)); device_appender->setLayout(std::auto_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); Logger device_logger = init_logger(device_key, device_dir, dev_id, device_appender); //用终端id作为日志文件名 logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DEFAULT); DIY_INFOLOG_CODE(device_key.c_str(),1,LOG_CODE_OTHER,"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id); } // 初始化监测点 // 监测点 logger 名称格式:monitor. for (int i = 0; i < 10; ++i) { if (strlen(ied_usr->LD_info[i].mp_id) > 0){ std::ostringstream mon_key, mon_path, mon_name; mon_key << "monitor." << ied_usr->LD_info[i].mp_id; mon_path << device_dir << "/monitor" << i;//终端路径下用monitor+序号作为目录 mon_name << ied_usr->LD_info[i].mp_id; // 添加判断:监测点 logger 是否已存在 if (logger_map.find(mon_key.str()) == logger_map.end()) { // 所有监测点日志(com 和 data)写到同一个 monitor 日志文件中 std::string file_path_m = mon_path.str() + "/" + mon_name.str() + ".log"; // 共用一个 appender 实例 SharedAppenderPtr monitor_appender = SharedAppenderPtr(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2)); monitor_appender->setLayout(std::auto_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); Logger mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str(),monitor_appender);//用监测点号作为日志文件名 logger_map[mon_key.str()] = TypedLogger(mon_logger, LOGTYPE_DEFAULT); DIY_INFOLOG_CODE(mon_key.str().c_str(),2,LOG_CODE_OTHER,"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id); } } } break; // 只匹配一个 terminal_id } //lnk20260303添加日志上送控制 - 初始化时构建日志等级缓存 refresh_log_level_cache_locked(); } void init_loggers() { std::string base_dir = std::string("/FeProject/") + subdir + "/processNo" + intToString(g_front_seg_index) + "/log"; ied_t* ied = NULL; int iedno; ied_usr_t* ied_usr = NULL; for (iedno = 0; iedno < g_node->n_clients; iedno++) { ied = g_node->clients[iedno]; if (!ied || !ied->usr_ext) { std::cout << "ied No."<< iedno << " is null" << std::endl; continue; } ied_usr = (ied_usr_t*)ied->usr_ext; const char* ip_cstr = (ied->channel[0].addr_str != NULL) ? ied->channel[0].addr_str : "unknown"; std::string ip_str(ip_cstr); std::string device_dir = base_dir + "/" + ip_str; std::string device_key = std::string("terminal.") + ied_usr->terminal_id; // 所有终端日志(com 和 data)写到同一个 device 日志文件中 std::string file_path_t = device_dir + "/" + ied_usr->terminal_id + ".log"; // 共用一个 appender 实例 SharedAppenderPtr device_appender = SharedAppenderPtr(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2)); device_appender->setLayout(std::auto_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); Logger device_logger = init_logger(device_key, device_dir, ied_usr->terminal_id, device_appender); //用终端id作为日志文件名 logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DEFAULT); DIY_INFOLOG_CODE(device_key.c_str(),1,LOG_CODE_OTHER,"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id); // 初始化监测点 // 监测点 logger 名称格式:monitor. for (int i = 0; i < 10; ++i) { if (strlen(ied_usr->LD_info[i].mp_id) > 0){ std::ostringstream mon_key, mon_path, mon_name; mon_key << "monitor." << ied_usr->LD_info[i].mp_id; mon_path << device_dir << "/monitor" << i;//终端路径下用monitor+序号作为目录 mon_name << ied_usr->LD_info[i].mp_id; std::string file_path_m = mon_path.str() + "/" + mon_name.str() + ".log"; // 共用一个 appender 实例 SharedAppenderPtr monitor_appender = SharedAppenderPtr(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2)); monitor_appender->setLayout(std::auto_ptr(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n"))); Logger mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str(), monitor_appender); logger_map[mon_key.str()] = TypedLogger(mon_logger, LOGTYPE_DEFAULT); DIY_INFOLOG_CODE(mon_key.str().c_str(),2,LOG_CODE_OTHER,"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id); } } } //lnk20260303添加日志上送控制 - 初始化时构建日志等级缓存 refresh_log_level_cache_locked(); } void remove_loggers_by_terminal_id(const char* terminal_id_cstr) { if (!g_node || !terminal_id_cstr) return; std::string terminal_id(terminal_id_cstr); // 转为 std::string for (int iedno = 0; iedno < g_node->n_clients; ++iedno) { ied_t* ied = g_node->clients[iedno]; if (!ied || !ied->usr_ext) continue; ied_usr_t* ied_usr = (ied_usr_t*)ied->usr_ext; if (strcmp(ied_usr->terminal_id, terminal_id.c_str()) != 0) continue; // 删除终端日志 logger std::string terminal_key = "terminal." + terminal_id;; if (logger_map.count(terminal_key)) { logger_map[terminal_key].logger.removeAllAppenders(); logger_map.erase(terminal_key); } // 删除监测点日志 logger for (int i = 0; i < 10; ++i) { const char* mp_id = ied_usr->LD_info[i].mp_id; if (strlen(mp_id) > 0) { std::string mon_key = std::string("monitor.") + mp_id; if (logger_map.count(mon_key)) { logger_map[mon_key].logger.removeAllAppenders(); logger_map.erase(mon_key); } } } std::cout << "[LOG] Logger for terminal_id=" << terminal_id << " removed.\n"; break; // 找到匹配终端后退出 } } #ifdef __cplusplus extern "C" { #endif // 公共函数 void log4_log_with_level(const char* key, const char* msg, int level) { std::map::iterator it = logger_map.find(key); if (it == logger_map.end()) return; Logger logger = it->second.logger; switch (level) { case 0: LOG4CPLUS_DEBUG(logger, msg); break; case 1: LOG4CPLUS_INFO(logger, msg); break; case 2: LOG4CPLUS_WARN(logger, msg); break; case 3: LOG4CPLUS_ERROR(logger, msg); break; default: break; } } // 四个包装函数 void log_debug(const char* key, const char* msg) { log4_log_with_level(key, msg, 0); } void log_info (const char* key, const char* msg) { log4_log_with_level(key, msg, 1); } void log_warn (const char* key, const char* msg) { log4_log_with_level(key, msg, 2); } void log_error(const char* key, const char* msg) { log4_log_with_level(key, msg, 3); } void send_reply_to_kafka_c(const char* guid, const char* step, const char* result) { send_reply_to_kafka(std::string(guid), std::string(step), std::string(result)); } void send_reply_to_kafka_recall_c(const char* guid, const char* step, const char* result,const char* lineIndex,const char* recallStartDate,const char* recallEndDate) { send_reply_to_kafka_recall(std::string(guid), std::string(step), std::string(result),std::string(lineIndex), std::string(recallStartDate), std::string(recallEndDate)); } //标准化日志接口 void format_log_msg(char* buf, size_t buf_size, const char* fmt, ...) { // 写入时间 time_t now = time(NULL); struct tm tm_info; localtime_r(&now, &tm_info); strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S ", &tm_info); // 时间+空格 // 处理可变参数并写入剩余内容 va_list args; va_start(args, fmt); vsnprintf(buf + strlen(buf), buf_size - strlen(buf), fmt, args); va_end(args); } #ifdef __cplusplus } #endif /* int main() { initialize(); init_loggers(); LOG4CPLUS_INFO(logger_map["process.1.data"].logger, "程序启动,PID=" << getpid()); LOG4CPLUS_WARN(logger_map["terminal.192.168.1.1.com"].logger, "串口异常,尝试重连"); LOG4CPLUS_INFO(logger_map["terminal.192.168.1.1.data"].logger, "终端运行状态正常"); LOG4CPLUS_DEBUG(logger_map["measurepoint.192.168.1.1.monitor1.data"].logger, "电压测量值=220.1V"); process_log_command("192.168.1.1.monitor1", "measurepoint", "DEBUG", "data"); LOG4CPLUS_DEBUG(logger_map["measurepoint.192.168.1.1.monitor1.data"].logger, "频率测量值=50.0Hz"); sleep(65); LOG4CPLUS_ERROR(logger_map["terminal.192.168.1.2.data"].logger, "终端掉线"); return 0; } */