From 748f8481bcea8b049da29d4540e2a8512e66f2d6 Mon Sep 17 00:00:00 2001 From: lnk Date: Fri, 6 Mar 2026 16:27:58 +0800 Subject: [PATCH] modify log4 function and add data trace function --- cfg_parse/cfg_parser.cpp | 673 ++++++++++++++++++++++----------------- cfg_parse/log4.cpp | 444 ++++++++++++++++++-------- json/create_json.cpp | 111 +++++++ json/mms_json_inter.h | 2 + json/save2json.cpp | 56 +++- json/save2json.h | 3 + log4cplus/log4.h | 14 +- mms/mms_process.c | 2 + mms/rdb_client.h | 7 +- 9 files changed, 888 insertions(+), 424 deletions(-) diff --git a/cfg_parse/cfg_parser.cpp b/cfg_parse/cfg_parser.cpp index 4ce2de4..e26dcb5 100644 --- a/cfg_parse/cfg_parser.cpp +++ b/cfg_parse/cfg_parser.cpp @@ -21,7 +21,7 @@ using namespace std; #define OTL_ODBC_UNIX #include -#include "otlv4.h" +//#include "otlv4.h" #include #include //lnk 2024-10-16 @@ -40,6 +40,17 @@ using namespace std; #include "../log4cplus/log4.h"//lnk添加log4 #include +//同步arm,移除otlv头文件 +class otl_datetime { +public: + int year; + int month; + int day; + int hour; + int minute; + int second; +}; + //用于测试时防止数据激增 #ifndef apr_time_from_msec #define apr_time_from_msec(ms) ((apr_time_t)(ms) * 1000) @@ -128,6 +139,8 @@ public: char timestamp[64]; char status[255]; char count_cfg[64]; //不是台账的一部分,用来记录数据库或业务中台的台账数量 + + int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG }; class terminal_dev //终端台账 @@ -154,6 +167,8 @@ public: ledger_monitor line[10]; char count_cfg[64]; //不是台账的一部分,用来记录数据库或业务中台的台账数量 + + int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG }; class icd_model //icd模型 @@ -1603,6 +1618,22 @@ void parse_terminal_from_data(trigger_update_xml_t* trigger_update_xml, const st strcpy(work_terminal.port, extract_value(data, "port").c_str()); strcpy(work_terminal.timestamp, extract_value(data, "updateTime").c_str()); + //添加日志等级 + std::string level = extract_value(data, "loglevel"); + int tmp_level = -1; + if (!level.empty()) { + try { + tmp_level = std::stoi(level); + } catch (...) { + tmp_level = -1; + } + } + if (tmp_level >= 0 && tmp_level <= 3) { + work_terminal.log_level = tmp_level; + } else { + work_terminal.log_level = 1; // 默认 warn + } + //添加guid20250506 strncpy(work_terminal.guid, guid_value.c_str(), sizeof(work_terminal.guid) - 1); work_terminal.guid[sizeof(work_terminal.guid) - 1] = '\0'; @@ -1637,6 +1668,27 @@ void parse_terminal_from_data(trigger_update_xml_t* trigger_update_xml, const st strcpy(work_monitor.timestamp, timestamp.empty() ? "N/A" : timestamp.c_str()); strcpy(work_monitor.terminal_code, terminal_code.empty() ? "N/A" : terminal_code.c_str()); strcpy(work_monitor.status, status.empty() ? "N/A" : status.c_str()); + + std::string log_level = extract_value(data, "loglevel"); + int tmp_level = -1; + // 尝试解析 monitor loglevel + if (!log_level.empty()) { + try { + tmp_level = std::stoi(log_level); + } catch (...) { + tmp_level = -1; + } + } + // 判断是否合法 + if (tmp_level >= 0 && tmp_level <= 3) { + work_monitor.log_level = tmp_level; + } + else if (work_terminal.log_level >= 0 && work_terminal.log_level <= 3) { + work_monitor.log_level = work_terminal.log_level; // 继承 terminal + } + else { + work_monitor.log_level = 1; // 默认 warn + } // 将提取的 monitor 数据存入 work_terminal.line[monitor_count] work_terminal.line[monitor_count] = work_monitor; @@ -3270,6 +3322,7 @@ void printTerminalDevMap(const QMap& terminal_dev_map) { << ", Device maxProcessNum:" << QString(dev->maxProcessNum) << ", Address:" << QString(dev->addr_str) << ", Port:" << QString(dev->port) + << ", log_level:" << QString(dev->log_level) << ", Timestamp:" << QString(dev->timestamp); // 打印监测点信息 @@ -3281,6 +3334,7 @@ void printTerminalDevMap(const QMap& terminal_dev_map) { << ", Voltage Level:" << QString(dev->line[i].voltage_level) << ", Terminal Connect:" << QString(dev->line[i].terminal_connect) << ", Timestamp:" << QString(dev->line[i].timestamp) + << ", log_level:" << QString(dev->line[i].log_level) << ", Status:" << QString(dev->line[i].status); } } else { @@ -3310,6 +3364,7 @@ void printLedger(const ied_usr_t& ied_usr) { std::cout << "|-- tmnl_status: " << ied_usr.tmnl_status << std::endl; std::cout << "|-- terminal_code: " << ied_usr.terminal_code << std::endl; std::cout << "|-- update_flag: " << ied_usr.update_flag << std::endl; + std::cout << "|-- log_level: " << ied_usr.log_level << std::endl; // 打印每个LD_info的内容 for (int i = 0; i < 10; ++i) { @@ -3321,6 +3376,7 @@ void printLedger(const ied_usr_t& ied_usr) { << (strlen(ied_usr.LD_info[i].LD_name) == 0 ? "NA" : ied_usr.LD_info[i].LD_name) << std::endl; std::cout << " |-- read_flag: " << ied_usr.LD_info[i].read_flag << std::endl; + std::cout << " |-- log_level: " << ied_usr.LD_info[i].log_level << std::endl; //index std::cout << " |-- line_id: " << ied_usr.LD_info[i].line_id << std::endl; //monitorledger @@ -3401,6 +3457,7 @@ void printLedgerinshell(const ied_usr_t& ied_usr, QIODevice* outputDevice) { outputDevice->write("\r\x1B[K");outputDevice->write("|-- tmnl_status: " + QByteArray(ied_usr.tmnl_status) + "\n"); outputDevice->write("\r\x1B[K");outputDevice->write("|-- terminal_code: " + QByteArray(ied_usr.terminal_code) + "\n"); outputDevice->write("\r\x1B[K");outputDevice->write("|-- update_flag: " + QByteArray::number(ied_usr.update_flag) + "\n"); + outputDevice->write("\r\x1B[K");outputDevice->write("|-- log_level: " + QByteArray::number(ied_usr.log_level) + "\n"); // 打印每个LD_info的内容 for (int i = 0; i < 10; ++i) { @@ -3412,7 +3469,9 @@ void printLedgerinshell(const ied_usr_t& ied_usr, QIODevice* outputDevice) { outputDevice->write("\r\x1B[K");outputDevice->write(" |-- LD_name: " + (strlen(ied_usr.LD_info[i].LD_name) == 0 ? QByteArray("NA") : QByteArray(ied_usr.LD_info[i].LD_name)) + "\n"); - outputDevice->write("\r\x1B[K");outputDevice->write(" |-- read_flag: " + QByteArray::number(ied_usr.LD_info[i].read_flag) + "\n"); + outputDevice->write("\r\x1B[K");outputDevice->write(" |-- read_flag: " + QByteArray::number(ied_usr.LD_info[i].read_flag) + "\n"); + + outputDevice->write("\r\x1B[K");outputDevice->write(" |-- log_level: " + QByteArray::number(ied_usr.LD_info[i].log_level) + "\n"); // index outputDevice->write("\r\x1B[K");outputDevice->write(" |-- line_id: " + QByteArray::number(ied_usr.LD_info[i].line_id) + "\n"); @@ -4004,6 +4063,22 @@ int terminal_ledger_web(QMap* terminal_dev_map, if (updateTime && updateTime->type == cJSON_String) strncpy(dev->timestamp, updateTime->valuestring, sizeof(dev->timestamp) - 1); else strncpy(dev->timestamp, "N/A", sizeof(dev->timestamp) - 1); + cJSON* logLevel = cJSON_GetObjectItem(item, "log_level"); // log_level + int tmp_level = -1; + if (logLevel && logLevel->type == cJSON_Number) { + tmp_level = logLevel->valueint; + } + else if (logLevel && logLevel->type == cJSON_String && logLevel->valuestring) { + tmp_level = atoi(logLevel->valuestring); + } + // 判断是否合法 0~3 + if (tmp_level >= 0 && tmp_level <= 3) { + dev->log_level = tmp_level; + } else { + dev->log_level = 1; // 默认 WARN + } + printf("dev->log_level: %d\n", dev->log_level); + // 解析 monitorData 数组 cJSON* monitorData = cJSON_GetObjectItem(item, "monitorData"); if (monitorData && monitorData->type == cJSON_Array) { @@ -4040,6 +4115,22 @@ int terminal_ledger_web(QMap* terminal_dev_map, if (monitorstatus && monitorstatus->type == cJSON_String) strncpy(dev->line[j].status, monitorstatus->valuestring, sizeof(dev->line[j].status) - 1); else strncpy(dev->line[j].status, "N/A", sizeof(dev->line[j].status) - 1); + cJSON* logLevel_m = cJSON_GetObjectItem(item, "log_level"); // log_level + int tmp_level = -1; + if (logLevel_m && logLevel_m->type == cJSON_Number) { + tmp_level = logLevel_m->valueint; + } + else if (logLevel_m && logLevel_m->type == cJSON_String && logLevel_m->valuestring) { + tmp_level = atoi(logLevel_m->valuestring); + } + // 判断是否合法 (0~3) + if (tmp_level >= 0 && tmp_level <= 3) { + dev->line[j].log_level = tmp_level; + } else { + dev->line[j].log_level = 1; // 默认 WARN + } + printf("line[%d].log_level: %d\n", j, dev->line[j].log_level); + j++; } } @@ -4213,6 +4304,9 @@ int parse_device_cfg_web() //lnk20250210添加进程号 char processNo[64]; + //lnk20260304添加日志等级 + int log_level; + otl_datetime timestamp; // 遍历终端台账容器 @@ -4238,305 +4332,279 @@ int parse_device_cfg_web() strncpy(processNo, value->processNo, sizeof(processNo) - 1);//进程号 timestamp = parseTimestamp(value->timestamp); - //处理终端台账 - ied = g_node->clients[count_real++]; - //这里申请的空间基于ied的数量,挂载到ied上 - ied_usr = (ied_usr_t*)apr_pcalloc(g_init_pool, sizeof(ied_usr_t));//终端台账在initpool中申请空间 - ied->usr_ext = ied_usr; + log_level = value->log_level;//日志等级 + } else { + std::cerr << "Warning: terminal_dev pointer is null for key: " << it.key().toStdString() << std::endl; + continue; // 跳过空指针 + } - if (ied_usr == NULL) - return APR_ENOMEM; - - ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000; - //这里申请的空间基于ied的数量,挂载到ied上 - ied_usr->LD_info = (LD_info_t*)apr_pcalloc(g_init_pool, MAX_CPUNO * sizeof(LD_info_t));//监测点台账在initpool中申请空间 - - if (ied_usr->LD_info == NULL) - return APR_ENOMEM; - - ied_usr->dev_flag = ENABLE;//终端有效 - ied->chncount = 1;//设备通信端口总数 - //这里申请的空间基于ied的数量,挂载到ied上 - ied->channel = (channel_t*)apr_pcalloc(g_cfg_pool, sizeof(channel_t) * ied->chncount);//通信结构在g_cfg_pool中申请空间:终端的ip端口等 - ied->channel[0].ied = ied; - ied->channel[0].status = STATUS_BREAKOFF; - ied->cpucount = 0; - - if (strlen(terminal_id) != 0) { - apr_snprintf(ied_usr->terminal_id, sizeof(ied_usr->terminal_id), "%s", terminal_id);//terminal_id - cout << "ied_usr->terminal_id:" << ied_usr->terminal_id << endl; + //处理终端台账 + ied = g_node->clients[count_real++]; + //这里申请的空间基于ied的数量,挂载到ied上 + ied_usr = (ied_usr_t*)apr_pcalloc(g_init_pool, sizeof(ied_usr_t));//终端台账在initpool中申请空间 + ied->usr_ext = ied_usr; + if (ied_usr == NULL) + return APR_ENOMEM; + ied_usr->last_call_wavelist_time = sGetMsTime() + g_pt61850app->giTime * 1000; + //这里申请的空间基于ied的数量,挂载到ied上 + ied_usr->LD_info = (LD_info_t*)apr_pcalloc(g_init_pool, MAX_CPUNO * sizeof(LD_info_t));//监测点台账在initpool中申请空间 + if (ied_usr->LD_info == NULL) + return APR_ENOMEM; + ied_usr->dev_flag = ENABLE;//终端有效 + ied->chncount = 1;//设备通信端口总数 + //这里申请的空间基于ied的数量,挂载到ied上 + ied->channel = (channel_t*)apr_pcalloc(g_cfg_pool, sizeof(channel_t) * ied->chncount);//通信结构在g_cfg_pool中申请空间:终端的ip端口等 + ied->channel[0].ied = ied; + ied->channel[0].status = STATUS_BREAKOFF; + ied->cpucount = 0; + if (strlen(terminal_id) != 0) { + apr_snprintf(ied_usr->terminal_id, sizeof(ied_usr->terminal_id), "%s", terminal_id);//terminal_id + cout << "ied_usr->terminal_id:" << ied_usr->terminal_id << endl; + } + if (terminal_code != NULL) { + apr_snprintf(ied_usr->terminal_code, sizeof(ied_usr->terminal_code), "%s", terminal_code);//terminal_code + cout << "ied_usr->terminal_code:" << ied_usr->terminal_code << endl; + } + /*不需要这三个信息 + if (org_name != NULL) { + apr_snprintf(ied_usr->org_name, sizeof(ied_usr->org_name), "%s", org_name);//org_name + cout << "ied_usr->org_name:" << ied_usr->org_name << endl; + } + if (maint_name != NULL) { + apr_snprintf(ied_usr->maint_name, sizeof(ied_usr->maint_name), "%s", maint_name);//maint_name + cout << "ied_usr->maint_name:" << ied_usr->maint_name << endl; + } + if (station_name != NULL) { + apr_snprintf(ied_usr->station_name, sizeof(ied_usr->station_name), "%s", station_name);//station_name + cout << "ied_usr->station_name:" << ied_usr->station_name << endl; + } + */ + if (tmnl_factory != NULL) { + apr_snprintf(ied_usr->tmnl_factory, sizeof(ied_usr->tmnl_factory), "%s", tmnl_factory);//tmnl_factory + cout << "ied_usr->tmnl_factory:" << ied_usr->tmnl_factory << endl; + } + if (tmnl_status != NULL) { + apr_snprintf(ied_usr->tmnl_status, sizeof(ied_usr->tmnl_status), "%s", tmnl_status);//tmnl_status + cout << "ied_usr->tmnl_status:" << ied_usr->tmnl_status << endl; + } + if (dev_type != NULL) { + apr_snprintf(ied_usr->dev_type, sizeof(ied_usr->dev_type), "%s", dev_type);//dev_type + cout << "ied_usr->dev_type:" << ied_usr->dev_type << endl; + } + //lnk20250210台账进程号 + if (processNo != NULL) { + apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", processNo);//processNo + cout << "ied_usr->processNo:" << ied_usr->processNo << endl; + } + if (dev_series != NULL) { + apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", dev_series);//DEV_Series + cout << "defalut dev_series:" << ied_usr->dev_series << endl; + } + else + { + apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", "");//DEV_Series + cout << "defalut dev_series:" << ied_usr->dev_series << endl; + } + if (dev_key != NULL) { + apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", dev_key);//DEV_Key + cout << "defalut dev_key:" << ied_usr->dev_key << endl; + } + else + { + apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", "");//DEV_Key + cout << "defalut dev_key:" << ied_usr->dev_key << endl; + } + //lnk20260304 + ied_usr->log_level = log_level;//日志等级 + cout << "ied_usr->log_level:" << ied_usr->log_level << endl; + //lnk20241125实时数据用 + ied_usr->dev_idx = count_real; + cout << "dev_idx:" << ied_usr->dev_idx << endl; + ied->channel[0].channel_type = CHANNEL_TYPE_IPV4;//channel + ied->channel[0].addr_str[LONGNAME - 1] = 0;//DEV_IP + if (addr_str != NULL) { + ied->channel[0].addr = ntohl(inet_addr(addr_str));//DEV_IP + strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP + cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl; + } + else + { + ied->channel[0].addr = ntohl(inet_addr("0.0.0.0"));//DEV_IP + strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP + cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl; + } + if (port_char != NULL) { + int port = 102; + if (stringToInt(port_char, &port)) { + // 转换成功,portStr全为数字,并且已经转换为int类型的port + ied->channel[0].port = port;//DEV_PortID + cout << "ied_usr->port:" << ied->channel[0].port << endl;//DEV_PortID } - if (terminal_code != NULL) { - apr_snprintf(ied_usr->terminal_code, sizeof(ied_usr->terminal_code), "%s", terminal_code);//terminal_code - cout << "ied_usr->terminal_code:" << ied_usr->terminal_code << endl; + else { + ied->channel[0].port = 102;//DEV_PortID + cout << "ied_usr->port:" << port_char << ",非合法端口.使用默认端口:" << ied->channel[0].port << endl;//DEV_PortID } - /*不需要这三个信息 - if (org_name != NULL) { - apr_snprintf(ied_usr->org_name, sizeof(ied_usr->org_name), "%s", org_name);//org_name - cout << "ied_usr->org_name:" << ied_usr->org_name << endl; - + } + if (timestamp.year != 0) { + //// 构造struct tm对象 + struct tm timeinfo; + timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900 + timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1 + timeinfo.tm_mday = timestamp.day; + timeinfo.tm_hour = timestamp.hour; + timeinfo.tm_min = timestamp.minute; + timeinfo.tm_sec = timestamp.second; + time_t time = std::mktime(&timeinfo); + ied_usr->time = static_cast(time); + cout << "ied_usr->time:" << ied_usr->time << endl; + } + //这里申请的空间基于ied的数量,挂载到ied上 + chnl_usr = (chnl_usr_t*)apr_pcalloc(g_init_pool, sizeof(chnl_usr_t));//拓展定义的通信结构在g_init_pool中申请空间:拓展记录一些开关时间信息 + ied->channel[0].connect = chnl_usr; + chnl_usr->chnl = &(ied->channel[0]); + chnl_usr->chnl_id = 0; + chnl_usr->m_state = CHANNEL_DISCONNECTED; + chnl_usr->m_ClosedMsTime = NEXT_CONNECT_TIME * (-1); + g_pt61850app->chnl_counts++; + //调试用 + //std::cout << "value:" << terminal_id <<" "<n_clients << std::endl; + if (g_node->n_clients <= 0) { + std::cout << "no terminal exist " << std::endl; + return APR_EBADF; + } + char monitor_id[64]; + //char terminal_code[64]; + char monitor_name[64]; + char logical_device_seq[64]; + char voltage_level[64]; + char terminal_connect[64]; + char monitor_status[64]; + //otl_datetime timestamp; + int monitor_log_level = 1;//监测点日志等级 + //for (int j = 0; j < 10; ++j) { // 假设最多有10个监测点 + for (int j = 0; value->line[j].monitor_id[0] != '\0'; ++j){ + ledger_monitor& monitor = value->line[j]; + // 检查监测点 ID 是否为空以避免访问无效数据 + /*if (monitor.monitor_id[0] != '\0') { + std::cout << " Monitor ID: " << monitor.monitor_id << std::endl; + std::cout << " Terminal Code: " << monitor.terminal_code << std::endl; //应该为空,json不带 + std::cout << " Monitor Name: " << monitor.monitor_name << std::endl; + std::cout << " Logical Device Seq: " << monitor.logical_device_seq << std::endl; + std::cout << " Voltage Level: " << monitor.voltage_level << std::endl; + std::cout << " Terminal Connect: " << monitor.terminal_connect << std::endl; + std::cout << " Timestamp: " << monitor.timestamp << std::endl; //应该为空json不带 + std::cout << " monitor_status: " << monitor.status << std::endl;*/ + strncpy(monitor_id, monitor.monitor_id, sizeof(monitor_id) - 1); + //strncpy(terminal_code, monitor.terminal_code, sizeof(terminal_code) - 1); //从上级获取 + strncpy(monitor_name, monitor.monitor_name, sizeof(monitor_name) - 1); + strncpy(logical_device_seq, monitor.logical_device_seq, sizeof(logical_device_seq) - 1); + strncpy(voltage_level, monitor.voltage_level, sizeof(voltage_level) - 1); + strncpy(terminal_connect, monitor.terminal_connect, sizeof(terminal_connect) - 1); + //timestamp = parseTimestamp(monitor.timestamp); //从上级获取 + strncpy(monitor_status, monitor.status, sizeof(monitor_status) - 1);//添加监测点状态 + monitor_log_level = monitor.log_level;//监测点日志等级 + //监测点台账处理 + count_real_monitor++; + memset(&line_info, 0, sizeof(line_info)); + line_info.line_id = count_real_monitor; //监测点排号 + cout << "line_id:" << line_info.line_id << endl; + strcpy(line_info.mp_id, monitor_id); + cout << "mp_id:" << line_info.mp_id << endl; + strcpy(line_info.terminal_code, terminal_code); //从上级获取的终端号 + cout << "terminal_code:" << line_info.terminal_code << endl; + if (isCharPtrEmpty(logical_device_seq)) { + line_info.cpuno = 1; //默认监测点实例号1 + cout << "logical_device_seq:is null,set cpuno:"<< line_info.cpuno << endl; } - if (maint_name != NULL) { - apr_snprintf(ied_usr->maint_name, sizeof(ied_usr->maint_name), "%s", maint_name);//maint_name - cout << "ied_usr->maint_name:" << ied_usr->maint_name << endl; - + else { + line_info.cpuno = std::atoi(logical_device_seq); + cout << "logical_device_seq:"<< line_info.cpuno << endl; } - if (station_name != NULL) { - apr_snprintf(ied_usr->station_name, sizeof(ied_usr->station_name), "%s", station_name);//station_name - cout << "ied_usr->station_name:" << ied_usr->station_name << endl; - } - */ - if (tmnl_factory != NULL) { - apr_snprintf(ied_usr->tmnl_factory, sizeof(ied_usr->tmnl_factory), "%s", tmnl_factory);//tmnl_factory - cout << "ied_usr->tmnl_factory:" << ied_usr->tmnl_factory << endl; - } - if (tmnl_status != NULL) { - apr_snprintf(ied_usr->tmnl_status, sizeof(ied_usr->tmnl_status), "%s", tmnl_status);//tmnl_status - cout << "ied_usr->tmnl_status:" << ied_usr->tmnl_status << endl; - } - if (dev_type != NULL) { - apr_snprintf(ied_usr->dev_type, sizeof(ied_usr->dev_type), "%s", dev_type);//dev_type - cout << "ied_usr->dev_type:" << ied_usr->dev_type << endl; - } - - //lnk20250210台账进程号 - if (processNo != NULL) { - apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", processNo);//processNo - cout << "ied_usr->processNo:" << ied_usr->processNo << endl; - } - - if (dev_series != NULL) { - apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", dev_series);//DEV_Series - cout << "defalut dev_series:" << ied_usr->dev_series << endl; - } - else + //cout << "cpuno:" << line_info.cpuno << endl; + strcpy(line_info.voltage_level, voltage_level); + cout << "voltage_level:" << line_info.voltage_level << endl; + strcpy(line_info.v_wiring_type, terminal_connect); + cout << "v_wiring_type:" << line_info.v_wiring_type << endl; + //lnk2024-8-14记录接线标志 + if (strcmp(line_info.v_wiring_type, "0") != 0) { - apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", "");//DEV_Series - cout << "defalut dev_series:" << ied_usr->dev_series << endl; + isdelta_flag = 1; //存在一个监测点为角型接线则这个前置就要启动第二个配置列表 + cout << "monitor_id" << monitor_id << "v_wiring_type:" << line_info.v_wiring_type << "is delta wiring:" << isdelta_flag << endl; + DIY_WARNLOG_CODE("process",LOG_CODE_LEDGER,"【WARN】前置连接的监测点 %s 是角形接线,对应终端为%s 终端类型是%s",line_info.mp_id,ied_usr->terminal_id,ied_usr->dev_type); } - if (dev_key != NULL) { - apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", dev_key);//DEV_Key - cout << "defalut dev_key:" << ied_usr->dev_key << endl; - } - else - { - apr_snprintf(ied_usr->dev_key, sizeof(ied_usr->dev_key), "%s", "");//DEV_Key - cout << "defalut dev_key:" << ied_usr->dev_key << endl; - } - - //lnk20241125实时数据用 - ied_usr->dev_idx = count_real; - cout << "dev_idx:" << ied_usr->dev_idx << endl; - - ied->channel[0].channel_type = CHANNEL_TYPE_IPV4;//channel - ied->channel[0].addr_str[LONGNAME - 1] = 0;//DEV_IP - if (addr_str != NULL) { - ied->channel[0].addr = ntohl(inet_addr(addr_str));//DEV_IP - strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP - cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl; - } - else - { - ied->channel[0].addr = ntohl(inet_addr("0.0.0.0"));//DEV_IP - strncpy(ied->channel[0].addr_str, addr_str, LONGNAME - 1);//DEV_IP - cout << "ied_usr->addr_str:" << ied->channel[0].addr_str << endl; - } - if (port_char != NULL) { - int port = 102; - if (stringToInt(port_char, &port)) { - // 转换成功,portStr全为数字,并且已经转换为int类型的port - ied->channel[0].port = port;//DEV_PortID - cout << "ied_usr->port:" << ied->channel[0].port << endl;//DEV_PortID + strcpy(line_info.monitor_status, monitor_status); + cout << "monitor_status:" << line_info.monitor_status << endl; + //// 构造struct tm对象 + struct tm timeinfo; + timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900 //从上级获取的timestamp + timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1 + timeinfo.tm_mday = timestamp.day; + timeinfo.tm_hour = timestamp.hour; + timeinfo.tm_min = timestamp.minute; + timeinfo.tm_sec = timestamp.second; + time_t time = std::mktime(&timeinfo); + line_info.time = static_cast(time); + cout << "time:" << line_info.time << endl; + strcpy(line_info.name, monitor_name); + cout << "name:" << line_info.name << endl; + line_info.read_flag = ENABLE; //监测点有效 + line_info.log_level = monitor_log_level; //监测点日志等级 + cout << "log_level_monitor:" << line_info.log_level << endl; + //ied = find_ied_from_dev_code(line_info.terminal_code); //不需要再找上级终端了,已经在终端里了 + if (ied && ied->usr_ext && line_info.cpuno && (static_cast(line_info.cpuno) < 10)) { + char str[256]; //256大小 + byte_t cpuno = line_info.cpuno; + cout << "cpuno:" << (int)line_info.cpuno << endl; + cout << "index cpuno:" << cpuno-1 << endl; + ied_usr = (ied_usr_t*)ied->usr_ext; + ied_usr->LD_info[cpuno - 1] = line_info; //cpuno默认是1 + ied_usr->LD_info[cpuno - 1].ied = ied; + apr_snprintf(str, sizeof(str), "PQMonitorPQM%d", cpuno);//将监测点逻辑号转为PQMonitorPQM+逻辑号 + //lnk20250208不使用apr_pstrdup,后续直接复用 + //ied_usr->LD_info[cpuno - 1].LD_name = apr_pstrdup(g_init_pool, str);//将 str 中的格式化字符串复制到内存池 g_init_pool 中。ied_usr->LD_info[cpuno - 1].LD_name 存储了这个字符串的副本,LD_name 现在是 PQMonitorPQM{cpuno} 的形式。 + // 从 g_init_pool 内存池中分配固定 256 字节的内存 + ied_usr->LD_info[cpuno - 1].LD_name = (char *)apr_palloc(g_init_pool, 256); + //调试用,申请的地址 + printf("%s分配内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno - 1, (void*)ied_usr->LD_info[cpuno - 1].LD_name); + // 清空内存,防止残留数据 + memset(ied_usr->LD_info[cpuno - 1].LD_name, 0, 256); + // 将 str 中的内容复制到预先分配的内存中,最多复制 256 字节(包含结束符) + apr_cpystrn(ied_usr->LD_info[cpuno - 1].LD_name, str, 256); + + //这里申请的空间基于ied的数量,挂载到ied上 + ied_usr->LD_info[cpuno - 1].ht_fcd = apr_hash_make(g_init_pool); //这两行代码分别为 ied_usr->LD_info[cpuno - 1] 的两个成员(ht_fcd 和 ht_full_fcda)创建了空的哈希表。apr_hash_make(g_init_pool) 会在 g_init_pool 内存池中为这两个哈希表分配内存空间 + ied_usr->LD_info[cpuno - 1].ht_full_fcda = apr_hash_make(g_init_pool);//它们的 key 值和 value 在后续的代码中可能会被填充 + ied_usr->LD_info[cpuno - 1].rptcount = 0; + cout << "rptcount:" << ied_usr->LD_info[cpuno - 1].rptcount << endl; + if (cpuno > ied->cpucount) { + ied->cpucount = cpuno; } - else { - ied->channel[0].port = 102;//DEV_PortID - cout << "ied_usr->port:" << port_char << ",非合法端口.使用默认端口:" << ied->channel[0].port << endl;//DEV_PortID - } - } - if (timestamp.year != 0) { - //// 构造struct tm对象 - struct tm timeinfo; - timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900 - timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1 - timeinfo.tm_mday = timestamp.day; - timeinfo.tm_hour = timestamp.hour; - timeinfo.tm_min = timestamp.minute; - timeinfo.tm_sec = timestamp.second; - time_t time = std::mktime(&timeinfo); - ied_usr->time = static_cast(time); - cout << "ied_usr->time:" << ied_usr->time << endl; - } - - //这里申请的空间基于ied的数量,挂载到ied上 - chnl_usr = (chnl_usr_t*)apr_pcalloc(g_init_pool, sizeof(chnl_usr_t));//拓展定义的通信结构在g_init_pool中申请空间:拓展记录一些开关时间信息 - ied->channel[0].connect = chnl_usr; - chnl_usr->chnl = &(ied->channel[0]); - chnl_usr->chnl_id = 0; - chnl_usr->m_state = CHANNEL_DISCONNECTED; - chnl_usr->m_ClosedMsTime = NEXT_CONNECT_TIME * (-1); - g_pt61850app->chnl_counts++; - - //调试用 - //std::cout << "value:" << terminal_id <<" "<n_clients << std::endl; - - if (g_node->n_clients <= 0) { - std::cout << "no terminal exist " << std::endl; - return APR_EBADF; - } - - char monitor_id[64]; - //char terminal_code[64]; - char monitor_name[64]; - char logical_device_seq[64]; - char voltage_level[64]; - char terminal_connect[64]; - char monitor_status[64]; - //otl_datetime timestamp; - - //for (int j = 0; j < 10; ++j) { // 假设最多有10个监测点 - for (int j = 0; value->line[j].monitor_id[0] != '\0'; ++j){ - ledger_monitor& monitor = value->line[j]; - - // 检查监测点 ID 是否为空以避免访问无效数据 - /*if (monitor.monitor_id[0] != '\0') { - std::cout << " Monitor ID: " << monitor.monitor_id << std::endl; - std::cout << " Terminal Code: " << monitor.terminal_code << std::endl; //应该为空,json不带 - std::cout << " Monitor Name: " << monitor.monitor_name << std::endl; - std::cout << " Logical Device Seq: " << monitor.logical_device_seq << std::endl; - std::cout << " Voltage Level: " << monitor.voltage_level << std::endl; - std::cout << " Terminal Connect: " << monitor.terminal_connect << std::endl; - std::cout << " Timestamp: " << monitor.timestamp << std::endl; //应该为空json不带 - std::cout << " monitor_status: " << monitor.status << std::endl;*/ - - strncpy(monitor_id, monitor.monitor_id, sizeof(monitor_id) - 1); - //strncpy(terminal_code, monitor.terminal_code, sizeof(terminal_code) - 1); //从上级获取 - strncpy(monitor_name, monitor.monitor_name, sizeof(monitor_name) - 1); - strncpy(logical_device_seq, monitor.logical_device_seq, sizeof(logical_device_seq) - 1); - strncpy(voltage_level, monitor.voltage_level, sizeof(voltage_level) - 1); - strncpy(terminal_connect, monitor.terminal_connect, sizeof(terminal_connect) - 1); - //timestamp = parseTimestamp(monitor.timestamp); //从上级获取 - strncpy(monitor_status, monitor.status, sizeof(monitor_status) - 1);//添加监测点状态 - //监测点台账处理 - count_real_monitor++; - - memset(&line_info, 0, sizeof(line_info)); - - line_info.line_id = count_real_monitor; //监测点排号 - cout << "line_id:" << line_info.line_id << endl; - - strcpy(line_info.mp_id, monitor_id); - cout << "mp_id:" << line_info.mp_id << endl; - - strcpy(line_info.terminal_code, terminal_code); //从上级获取的终端号 - cout << "terminal_code:" << line_info.terminal_code << endl; - - if (isCharPtrEmpty(logical_device_seq)) { - line_info.cpuno = 1; //默认监测点实例号1 - cout << "logical_device_seq:is null,set cpuno:"<< line_info.cpuno << endl; - } - else { - line_info.cpuno = std::atoi(logical_device_seq); - cout << "logical_device_seq:"<< line_info.cpuno << endl; - } - //cout << "cpuno:" << line_info.cpuno << endl; - - strcpy(line_info.voltage_level, voltage_level); - cout << "voltage_level:" << line_info.voltage_level << endl; - - strcpy(line_info.v_wiring_type, terminal_connect); - cout << "v_wiring_type:" << line_info.v_wiring_type << endl; - - //lnk2024-8-14记录接线标志 - if (strcmp(line_info.v_wiring_type, "0") != 0) - { - isdelta_flag = 1; //存在一个监测点为角型接线则这个前置就要启动第二个配置列表 - cout << "monitor_id" << monitor_id << "v_wiring_type:" << line_info.v_wiring_type << "is delta wiring:" << isdelta_flag << endl; - - DIY_WARNLOG_CODE("process",LOG_CODE_LEDGER,"【WARN】前置连接的监测点 %s 是角形接线,对应终端为%s 终端类型是%s",line_info.mp_id,ied_usr->terminal_id,ied_usr->dev_type); - } - - strcpy(line_info.monitor_status, monitor_status); - cout << "monitor_status:" << line_info.monitor_status << endl; - - //// 构造struct tm对象 - struct tm timeinfo; - timeinfo.tm_year = timestamp.year - 1900; // 年份需要减去1900 //从上级获取的timestamp - timeinfo.tm_mon = timestamp.month - 1; // 月份需要减去1 - timeinfo.tm_mday = timestamp.day; - timeinfo.tm_hour = timestamp.hour; - timeinfo.tm_min = timestamp.minute; - timeinfo.tm_sec = timestamp.second; - - time_t time = std::mktime(&timeinfo); - line_info.time = static_cast(time); - cout << "time:" << line_info.time << endl; - - strcpy(line_info.name, monitor_name); - cout << "name:" << line_info.name << endl; - - line_info.read_flag = ENABLE; //监测点有效 - - //ied = find_ied_from_dev_code(line_info.terminal_code); //不需要再找上级终端了,已经在终端里了 - - if (ied && ied->usr_ext && line_info.cpuno && (static_cast(line_info.cpuno) < 10)) { - char str[256]; //256大小 - byte_t cpuno = line_info.cpuno; - - cout << "cpuno:" << (int)line_info.cpuno << endl; - cout << "index cpuno:" << cpuno-1 << endl; - - ied_usr = (ied_usr_t*)ied->usr_ext; - ied_usr->LD_info[cpuno - 1] = line_info; //cpuno默认是1 - ied_usr->LD_info[cpuno - 1].ied = ied; - apr_snprintf(str, sizeof(str), "PQMonitorPQM%d", cpuno);//将监测点逻辑号转为PQMonitorPQM+逻辑号 - - //lnk20250208不使用apr_pstrdup,后续直接复用 - //ied_usr->LD_info[cpuno - 1].LD_name = apr_pstrdup(g_init_pool, str);//将 str 中的格式化字符串复制到内存池 g_init_pool 中。ied_usr->LD_info[cpuno - 1].LD_name 存储了这个字符串的副本,LD_name 现在是 PQMonitorPQM{cpuno} 的形式。 - // 从 g_init_pool 内存池中分配固定 256 字节的内存 - ied_usr->LD_info[cpuno - 1].LD_name = (char *)apr_palloc(g_init_pool, 256); - //调试用,申请的地址 - printf("%s分配内存地址 LD_name[%d]: %p\n", ied_usr->terminal_id, cpuno - 1, (void*)ied_usr->LD_info[cpuno - 1].LD_name); - - // 清空内存,防止残留数据 - memset(ied_usr->LD_info[cpuno - 1].LD_name, 0, 256); - - // 将 str 中的内容复制到预先分配的内存中,最多复制 256 字节(包含结束符) - apr_cpystrn(ied_usr->LD_info[cpuno - 1].LD_name, str, 256); - - //这里申请的空间基于ied的数量,挂载到ied上 - ied_usr->LD_info[cpuno - 1].ht_fcd = apr_hash_make(g_init_pool); //这两行代码分别为 ied_usr->LD_info[cpuno - 1] 的两个成员(ht_fcd 和 ht_full_fcda)创建了空的哈希表。apr_hash_make(g_init_pool) 会在 g_init_pool 内存池中为这两个哈希表分配内存空间 - ied_usr->LD_info[cpuno - 1].ht_full_fcda = apr_hash_make(g_init_pool);//它们的 key 值和 value 在后续的代码中可能会被填充 - ied_usr->LD_info[cpuno - 1].rptcount = 0; - cout << "rptcount:" << ied_usr->LD_info[cpuno - 1].rptcount << endl; - - if (cpuno > ied->cpucount) { - ied->cpucount = cpuno; - } - } - //} - } - } + } + } } ////////////////////////////////////////////////////////////////////////////////////////////////// - if (count_real < count_cfg) + if (count_real < count_cfg){ g_node->n_clients = count_real; - if (count_cfg != count_real) + } + + if (count_cfg != count_real){ return APR_EBADF; + } + cout << "dev init create count:" << count_real; + return APR_SUCCESS; } - catch (otl_exception& e) + catch (...) { - printf("\n device error,ERROR code= %d,msg= %s \n", e.code, e.msg); - return e.code; + printf("\n device error \n"); + return APR_EBADF; } } @@ -4727,10 +4795,10 @@ int parse_model_cfg_web() delete_icd_model_map(icd_model_map); return APR_SUCCESS; } - catch (otl_exception& e) + catch (...) { - printf("\n icd model error,ERROR code= %d,msg= %s \n", e.code, e.msg); - return e.code; + printf("\n icd model error\n"); + return APR_EBADF; } } ////////////////////////////////////////////////////////////icd模型重构函数lnk20250116 @@ -4798,9 +4866,9 @@ char* parse_model_cfg_web_one(ied_t* ied, char* out_model) delete_icd_model_map(icd_model_map);//lnk20250701 return NULL; } - catch (otl_exception& e) + catch (...) { - printf("\n icd model error,ERROR code= %d,msg= %s \n", e.code, e.msg); + printf("\n icd model error\n"); return NULL; } } @@ -5404,6 +5472,16 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i apr_snprintf(ied_usr->processNo, sizeof(ied_usr->processNo), "%s", update[i].processNo); printf("ied_usr->processNo: %s\n", ied_usr->processNo); } + + //log_level拷贝到ied_usr中 + if (update[i].log_level >= 0 && update[i].log_level <= 3) { + ied_usr->log_level = update[i].log_level; + printf("ied_usr->log_level: %d\n", ied_usr->log_level); + } else { + ied_usr->log_level = 1; // 默认为1,表示warn级别 + printf("ied_usr->log_level (default): %d\n", ied_usr->log_level); + } + if (update[i].dev_series != NULL) { apr_snprintf(ied_usr->dev_series, sizeof(ied_usr->dev_series), "%s", update[i].dev_series); printf("ied_usr->dev_series: %s\n", ied_usr->dev_series); @@ -5510,6 +5588,19 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i strncpy(line_info.monitor_status, monitor_data.status, sizeof(line_info.monitor_status) - 1); strncpy(line_info.terminal_code, monitor_data.terminal_code, sizeof(line_info.terminal_code) - 1); strncpy(logical_device_seq, monitor_data.logical_device_seq, sizeof(logical_device_seq) - 1); + + //log_level拷贝到line_info中 + if (monitor_data.log_level >= 0 && monitor_data.log_level <= 3) { //优先使用监测点的log_level + line_info.log_level = monitor_data.log_level; + printf("line_info.log_level (monitor): %d\n", line_info.log_level); + } else if (ied_usr->log_level >= 0 && ied_usr->log_level <= 3) { //继承终端的log_level + line_info.log_level = ied_usr->log_level; + printf("line_info.log_level (inherit terminal): %d\n", line_info.log_level); + } else { + line_info.log_level = 1; + printf("line_info.log_level (default): %d\n", line_info.log_level); + } + if (isCharPtrEmpty(logical_device_seq)) { line_info.cpuno = 1; // 默认监测点实例号1 printf("logical_device_seq: is null, set cpuno: %d\n", (int)line_info.cpuno); @@ -5526,6 +5617,7 @@ int update_one_terminal_ledger(terminal* update, int i,ied_t* ied,int terminal_i printf("v_wiring_type: %s\n", line_info.v_wiring_type); printf("monitor_status: %s\n", line_info.monitor_status); printf("name: %s\n", line_info.name); + printf("log_level: %d\n", line_info.log_level); //lnk20250214角形 if (strcmp(line_info.v_wiring_type, "0") != 0) @@ -6137,6 +6229,8 @@ void clearLDInfo(LD_info_t *ld_info) { memset(ld_info->FltNum, 0, sizeof(ld_info->FltNum)); ld_info->RDRE_FltNum = 0; //录波号清零 + + ld_info->log_level = 1; //log_level设为1,默认日志级别为1,后续根据需要调整 } @@ -6185,6 +6279,7 @@ void clearIedUsr(ied_usr_t *ied_usr) { memset(ied_usr->tmnl_factory, 0, sizeof(ied_usr->tmnl_factory)); memset(ied_usr->tmnl_status, 0, sizeof(ied_usr->tmnl_status)); memset(ied_usr->terminal_code, 0, sizeof(ied_usr->terminal_code)); + ied_usr->log_level = 1; //log_level设为1,默认日志级别为1,后续根据需要调整 } // 清空 channel 和 cpuinfo 的非指针部分 diff --git a/cfg_parse/log4.cpp b/cfg_parse/log4.cpp index 87d7967..de5ce0f 100644 --- a/cfg_parse/log4.cpp +++ b/cfg_parse/log4.cpp @@ -4,7 +4,7 @@ #include "../log4cplus/fileappender.h" #include "../log4cplus/layout.h" #include "../log4cplus/ndc.h" -#include "../log4cplus/log4.h" +//#include "../log4cplus/log4.h" #include "../log4cplus/spi/loggingevent.h" #include #include @@ -18,21 +18,47 @@ //目录创建 #include #include -//kafka结构定义 #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发送数据链表 @@ -42,12 +68,153 @@ extern std::string intToString(int number); //日志主题 extern std::string G_LOG_TOPIC; - +///////////////////////////////////////////////////////// //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"; @@ -79,12 +246,11 @@ bool create_directory_recursive(const std::string& path) { } ////////////////////////////////////////////////////////////////////// std::string extract_logger_id(const std::string& logger_name) { - size_t first = logger_name.find('.'); - size_t last = logger_name.rfind('.'); - if (first != std::string::npos && last != std::string::npos && first + 1 < last) { - return logger_name.substr(first + 1, last - first - 1); // 去掉开头"terminal."和结尾".COM" - } - return ""; + 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) { @@ -98,7 +264,7 @@ std::string get_level_str(int level) { } ////////////////////////////////////////////////////////////////////// TypedLogger::TypedLogger() {} -TypedLogger::TypedLogger(const Logger& l, int t) : logger(l), logtype(t) {} +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; } @@ -132,46 +298,65 @@ LOG_TLS int g_log_code_tls = 0; class SendAppender : public Appender { protected: - void append(const spi::InternalLoggingEvent& event) { + void append(const spi::InternalLoggingEvent& event) override { std::string logger_name = event.getLoggerName(); int level = event.getLogLevel(); std::string msg = event.getMessage(); - int logtype = (logger_name.find(".COM") != std::string::npos) ? LOGTYPE_COM : LOGTYPE_DATA; 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 + else if (logger_name.find("terminal") != std::string::npos) level_str = "terminal"; + else + level_str = "process"; - // ★读取 TLS 中的 code(在打日志的线程里由宏设定) - int code = g_log_code_tls; // 若未显式传入,则为 0 + int code = g_log_code_tls; // TLS code + int safe_logtype = code; // ★关键:用 code 当 logtype 维度 - if (level == ERROR_LOG_LEVEL || level == WARN_LOG_LEVEL || g_debug_switch.match(logger_name, level, logtype)) { - 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\":\"" << (logtype == LOGTYPE_COM ? "com" : "data") - << "\",\"frontType\":\"" << get_front_type_from_subdir() - // ★新增:输出 code 字段(整型) - << "\",\"code\":\"" << code - << "\",\"log\":\"" << escape_json(msg) << "\"}"; + bool allow_send = false; + int min_send_level = get_min_send_level_cached(level_str, logger_name); - std::string jsonString = oss.str(); + // ① 高于台账阈值:直接上送 + 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); - Ckafka_data_t connect_info; - connect_info.strTopic = QString::fromStdString(G_LOG_TOPIC); - connect_info.strText = QString::fromStdString(jsonString); - - kafka_data_list_mutex.lock(); - kafka_data_list.append(connect_info); - kafka_data_list_mutex.unlock(); + 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); + if (!should_emit(rkey)) return; + + 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(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) { @@ -198,25 +383,58 @@ public: virtual ~SendAppender() { destructorImpl(); // 重要!释放 log4cplus 基类资源 } +//////////////////////////////////////////////////////////////////20260303添加日志上送控制 - 频率限制实现 +private: + struct RateState { + uint64_t hit_count; + std::chrono::steady_clock::time_point last_emit; + bool has_emit; + + RateState() : hit_count(0), last_emit(), 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) { + using namespace std::chrono; + const auto now = steady_clock::now(); + + std::lock_guard lk(s_rate_mutex); + RateState& st = s_rate_map[key]; + + const int RESET_SEC = 3600; + if (st.has_emit) { + auto idle = duration_cast(now - st.last_emit).count(); + if (idle >= RESET_SEC) st.hit_count = 0; + } + + st.hit_count++; + const int period_sec = (st.hit_count > 3) ? 300 : 1; + + if (!st.has_emit) { + st.last_emit = now; + st.has_emit = true; + return true; + } + + const auto elapsed = duration_cast(now - st.last_emit).count(); + if (elapsed >= period_sec) { + st.last_emit = now; + return true; + } + return false; + } }; - -//用来控制日志上送的结构 -struct LOGEntry { - std::string id; - std::string level; // terminal / measurepoint - int logtype; // com / data - int min_grade; - int countdown; -}; - -//日志上送map管理 -std::map g_log_entries; -pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER; - -// 生成唯一 key -std::string build_debug_key(const std::string& id, const std::string& level, int logtype) { - return id + "|" + level + "|" + (logtype == 1 ? "COM" : "DATA"); -} +//////////////////////////////////////////////////////////////////20260303添加日志上送控制 - 频率限制实现 +std::unordered_map SendAppender::s_rate_map; +std::mutex SendAppender::s_rate_mutex; // 外部线程中调用:每秒更新所有倒计时,0 则删除 void update_log_entries_countdown() { @@ -236,25 +454,25 @@ void update_log_entries_countdown() { pthread_mutex_unlock(&g_log_mutex); } -void process_log_command(const std::string& id, const std::string& level, const std::string& grade, const std::string& logtype_str) { - if (level != "terminal" && level != "measurepoint") return; +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 type = (logtype_str == "com") ? LOGTYPE_COM : LOGTYPE_DATA; int grade_level = (grade == "DEBUG") ? DEBUG_LOG_LEVEL : INFO_LOG_LEVEL; - std::string key = build_debug_key(id, level, type); + std::string key = build_debug_key(id, level, code); pthread_mutex_lock(&g_log_mutex); - - LOGEntry& entry = g_log_entries[key]; // 会自动 insert 或取已有 + LOGEntry& entry = g_log_entries[key]; entry.id = id; entry.level = level; - entry.logtype = type; + entry.code = code; entry.min_grade = grade_level; - entry.countdown = 60; // 重置倒计时 - + 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) { @@ -286,7 +504,7 @@ log4cplus::Logger init_logger(const std::string& full_name, //进程的日志 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_DATA); + logger_map["process"] = TypedLogger(init_logger(std::string("process"), base_dir, std::string("process")), LOGTYPE_DEFAULT); std::cout << "process log init ok" << std::endl; } @@ -321,12 +539,10 @@ void init_loggers_bydevid(const char* dev_id) std::string device_dir = base_dir + "/" + ip_str; - std::string device_key_c = std::string("terminal.") + dev_id + ".COM"; - std::string device_key_d = std::string("terminal.") + dev_id + ".DATA"; + std::string device_key = std::string("terminal.") + dev_id; // 添加判断:终端日志 logger 是否已存在 - if (logger_map.find(device_key_c) == logger_map.end() && - logger_map.find(device_key_d) == logger_map.end()) { + if (logger_map.find(device_key) == logger_map.end()) { // 所有终端日志(com 和 data)写到同一个 device 日志文件中 std::string file_path_t = device_dir + "/" + dev_id + ".log"; @@ -335,27 +551,23 @@ void init_loggers_bydevid(const char* dev_id) 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_c = init_logger(device_key_c, device_dir, dev_id, device_appender); //用终端id作为日志文件名 - Logger device_logger_d = init_logger(device_key_d, device_dir, dev_id, device_appender); //用终端id作为日志文件名 - logger_map[device_key_c] = TypedLogger(device_logger_c, LOGTYPE_COM); - logger_map[device_key_d] = TypedLogger(device_logger_d, LOGTYPE_DATA); + 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(device_key_d.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id); + DIY_INFOLOG(device_key.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id); } // 初始化监测点 - // 监测点 logger 名称格式:monitor..COM / .DATA + // 监测点 logger 名称格式:monitor. for (int i = 0; i < 10; ++i) { if (strlen(ied_usr->LD_info[i].mp_id) > 0){ - std::ostringstream mon_key_c, mon_key_d, mon_path, mon_name; - mon_key_c << "monitor." << ied_usr->LD_info[i].mp_id << ".COM"; - mon_key_d << "monitor." << ied_usr->LD_info[i].mp_id << ".DATA"; + 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_c.str()) == logger_map.end() && - logger_map.find(mon_key_d.str()) == logger_map.end()) { + 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"; @@ -364,12 +576,10 @@ void init_loggers_bydevid(const char* dev_id) 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_c = init_logger(mon_key_c.str(), mon_path.str(), mon_name.str(),monitor_appender);//用监测点号作为日志文件名 - Logger mon_logger_d = init_logger(mon_key_d.str(), mon_path.str(), mon_name.str(),monitor_appender); - logger_map[mon_key_c.str()] = TypedLogger(mon_logger_c, LOGTYPE_COM); - logger_map[mon_key_d.str()] = TypedLogger(mon_logger_d, LOGTYPE_DATA); + 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(mon_key_d.str().c_str(),"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id); + DIY_INFOLOG(mon_key.str().c_str(),"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id); } } @@ -377,6 +587,9 @@ void init_loggers_bydevid(const char* dev_id) break; // 只匹配一个 terminal_id } + + //lnk20260303添加日志上送控制 - 初始化时构建日志等级缓存 + refresh_log_level_cache_locked(); } void init_loggers() { @@ -402,8 +615,7 @@ void init_loggers() { std::string device_dir = base_dir + "/" + ip_str; - std::string device_key_c = std::string("terminal.") + ied_usr->terminal_id + ".COM"; - std::string device_key_d = std::string("terminal.") + ied_usr->terminal_id + ".DATA"; + 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"; @@ -412,21 +624,18 @@ void init_loggers() { 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_c = init_logger(device_key_c, device_dir, ied_usr->terminal_id, device_appender); //用终端id作为日志文件名 - Logger device_logger_d = init_logger(device_key_d, device_dir, ied_usr->terminal_id, device_appender); //用终端id作为日志文件名 + Logger device_logger = init_logger(device_key, device_dir, ied_usr->terminal_id, device_appender); //用终端id作为日志文件名 - logger_map[device_key_c] = TypedLogger(device_logger_c, LOGTYPE_COM); - logger_map[device_key_d] = TypedLogger(device_logger_d, LOGTYPE_DATA); + logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DEFAULT); - DIY_INFOLOG(device_key_d.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id); + DIY_INFOLOG(device_key.c_str(),"【NORMAL】终端id:%s终端级日志初始化完毕", ied_usr->terminal_id); // 初始化监测点 - // 监测点 logger 名称格式:monitor..COM / .DATA + // 监测点 logger 名称格式:monitor. for (int i = 0; i < 10; ++i) { if (strlen(ied_usr->LD_info[i].mp_id) > 0){ - std::ostringstream mon_key_c, mon_key_d, mon_path, mon_name; - mon_key_c << "monitor." << ied_usr->LD_info[i].mp_id << ".COM"; - mon_key_d << "monitor." << ied_usr->LD_info[i].mp_id << ".DATA"; + 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; @@ -437,19 +646,19 @@ void init_loggers() { 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_c = init_logger(mon_key_c.str(), mon_path.str(), mon_name.str(), monitor_appender); - Logger mon_logger_d = init_logger(mon_key_d.str(), mon_path.str(), mon_name.str(), monitor_appender); + Logger mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str(), monitor_appender); - logger_map[mon_key_c.str()] = TypedLogger(mon_logger_c, LOGTYPE_COM); - logger_map[mon_key_d.str()] = TypedLogger(mon_logger_d, LOGTYPE_DATA); + logger_map[mon_key.str()] = TypedLogger(mon_logger, LOGTYPE_DEFAULT); - DIY_INFOLOG(mon_key_d.str().c_str(),"【NORMAL】监测点:%s - id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].name,ied_usr->LD_info[i].mp_id); + DIY_INFOLOG(mon_key.str().c_str(),"【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) { @@ -465,37 +674,24 @@ void remove_loggers_by_terminal_id(const char* terminal_id_cstr) { if (strcmp(ied_usr->terminal_id, terminal_id.c_str()) != 0) continue; // 删除终端日志 logger - std::string com_key = "terminal." + terminal_id + ".COM"; - std::string data_key = "terminal." + terminal_id + ".DATA"; + std::string terminal_key = "terminal." + terminal_id;; - if (logger_map.count(com_key)) { - logger_map[com_key].logger.removeAllAppenders(); - logger_map.erase(com_key); - } - - if (logger_map.count(data_key)) { - logger_map[data_key].logger.removeAllAppenders(); - logger_map.erase(data_key); + 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_prefix = std::string("monitor.") + mp_id; + std::string mon_key = std::string("monitor.") + mp_id; - std::string mon_com_key = mon_prefix + ".COM"; - std::string mon_data_key = mon_prefix + ".DATA"; - - if (logger_map.count(mon_com_key)) { - logger_map[mon_com_key].logger.removeAllAppenders(); - logger_map.erase(mon_com_key); + if (logger_map.count(mon_key)) { + logger_map[mon_key].logger.removeAllAppenders(); + logger_map.erase(mon_key); } - if (logger_map.count(mon_data_key)) { - logger_map[mon_data_key].logger.removeAllAppenders(); - logger_map.erase(mon_data_key); - } } } diff --git a/json/create_json.cpp b/json/create_json.cpp index d212d24..b09f5bf 100644 --- a/json/create_json.cpp +++ b/json/create_json.cpp @@ -25,6 +25,18 @@ #include "../json/cjson.h" #include "../log4cplus/log4.h"//lnk添加log4 + +///////////////////////////////////////////////////////////////////////lnk20260305数据追踪相关 +#include +#include +#include +#include +#include + +// ★MOD: 全局追踪表:mp_id -> remaining times +static QMutex g_trace_mutex; +static QHash g_trace_map; + ///////////////////////////////////////////////////lnk2024-10-21//////////////////////////////////////////////////////// extern void SendJsonAPI_web(const std::string& strUrl, const char* code, const std::string& json, char** ptr); extern std::string WEB_INTEGRITY; @@ -230,7 +242,106 @@ extern int isdelta_flag;//lnk2024-8-16 角型接线标志 void connectlog_pgsql(char* id,char* datetime,int status); ///////////////////////////////////////////////lnk20241021替换web接口////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////lnk20260305数据追踪 +static QString escape_json_string(const QString& s) +{ + QString out = s; + out.replace("\\", "\\\\"); + out.replace("\"", "\\\""); + out.replace("\r", "\\r"); + out.replace("\n", "\\n"); + out.replace("\t", "\\t"); + return out; +} +// 打开追踪:次数 times(比如 5) +void process_trace_command(const std::string& id, int times) +{ + if (times <= 0) return; + QString qid = QString::fromStdString(id).trimmed(); + if (qid.isEmpty()) return; + + QMutexLocker lk(&g_trace_mutex); + g_trace_map[qid] = times; // 重新打开就覆盖/重置次数 +} + +// 查询是否要追踪 +static bool trace_is_enabled(const QString& mp_id) +{ + QMutexLocker lk(&g_trace_mutex); + auto it = g_trace_map.constFind(mp_id); + return (it != g_trace_map.constEnd() && it.value() > 0); +} + +// 命中一次并扣减;扣到 0 自动删 +static void trace_hit_and_decrement(const QString& mp_id) +{ + QMutexLocker lk(&g_trace_mutex); + auto it = g_trace_map.find(mp_id); + if (it == g_trace_map.end()) return; + + int left = it.value(); + left -= 1; + if (left <= 0) g_trace_map.erase(it); + else it.value() = left; +} + +//追踪61850原始数据 +static QString build_mms_multiline_text(const json_block_data* data) +{ + QStringList lines; + + QMapIterator it(data->mms_str_map); + while (it.hasNext()) { + it.next(); + lines << QString("%1 = %2").arg(it.key()).arg(it.value(), 0, 'g', 15); + } + + return lines.join("\n"); +} + +static QString build_trace_json(const json_block_data* data) +{ + if (!data) return "{}"; + + QString mms_text = build_mms_multiline_text(data); + + QString json; + json += "{"; + json += QString("\"mp_id\":\"%1\",").arg(escape_json_string(data->mp_id)); + json += QString("\"func_type\":%1,").arg(data->func_type); + json += QString("\"data_time\":%1,").arg(QString::number((qlonglong)data->time)); + json += QString("\"voltage_level\":\"%1\",").arg(QString::number(data->voltage_level, 'f', 6)); + json += QString("\"dev_type\":\"%1\",").arg(escape_json_string(data->dev_type)); + json += QString("\"mms_text\":\"%1\"").arg(escape_json_string(mms_text)); + json += "}"; + + return json; +} + +static void send_trace_if_needed(json_block_data* pdata) +{ + const QString mp_id_q = pdata->mp_id; + if (trace_is_enabled(mp_id_q)) { + + // 1) 组 json + QString jsonText = build_trace_json(pdata); + + // 2) 组 KafkaData + Ckafka_data_t KafkaData; + KafkaData.monitor_id = pdata->monitorId; + KafkaData.mp_id = pdata->mp_id; + KafkaData.strTopic = "DATA_TRACE_TOPIC"; + KafkaData.strText = jsonText; + + kafka_data_list_mutex.lock(); + kafka_data_list.append(KafkaData); + kafka_data_list_mutex.unlock(); + + // 3) 次数 -1 + trace_hit_and_decrement(mp_id_q); + } +} ////////////////////////////////////////////////////////////////////////////////////////////lnk20250710添加频率值存储 struct mp_freq_save { double G_FREQ; diff --git a/json/mms_json_inter.h b/json/mms_json_inter.h index 2cb164c..62d043f 100644 --- a/json/mms_json_inter.h +++ b/json/mms_json_inter.h @@ -41,6 +41,8 @@ public: bool data_have_statistic;//是否有统计数据,0没有,1有 QMap mms_str_map; //数据值(61850数据属性名, 数据值) + + bool data_trace_flag; //数据追踪标记,0不追踪,1追踪 }; class Ckafka_data_t //kafka发送数据结构类 diff --git a/json/save2json.cpp b/json/save2json.cpp index 4771efd..354dcfb 100644 --- a/json/save2json.cpp +++ b/json/save2json.cpp @@ -1167,9 +1167,13 @@ std::string prepare_update(const std::string& code_str, const terminal& json_dat add_indent(xmlStream, indentLevel); xmlStream << "" << json_data.processNo << "" << std::endl; + //lnk20250305 add_indent(xmlStream, indentLevel); xmlStream << "" << json_data.dev_key << "" << std::endl; + add_indent(xmlStream, indentLevel); + xmlStream << "" << json_data.log_level << "" << std::endl; + // monitorData 部分 for (int i = 0; json_data.line[i].monitor_id[0] != '\0'; i++) { const monitor& monitor = json_data.line[i]; @@ -1202,6 +1206,9 @@ std::string prepare_update(const std::string& code_str, const terminal& json_dat add_indent(xmlStream, indentLevel); xmlStream << "" << monitor.status << "" << std::endl; + add_indent(xmlStream, indentLevel); + xmlStream << "" << monitor.log_level << "" << std::endl; + indentLevel--; add_indent(xmlStream, indentLevel); xmlStream << "" << std::endl; @@ -1376,7 +1383,7 @@ int parse_log(const std::string& json_str) { return 1; } - std::string logtype = logtypestr->valuestring; + int logtype = logtypestr->valueint; // 获取 frontType 字段 cJSON* frontTypestr = cJSON_GetObjectItem(messageBody, "frontType"); @@ -1413,13 +1420,16 @@ int parse_log(const std::string& json_str) { //校验数据 if((level == "terminal" || level == "measurepoint") && (grade == "NORMAL" || grade == "DEBUG") && - (logtype == "com" || logtype == "data") && (!id.empty() && !is_blank(id))){ - //开启开关 + //开启日志临时开关 process_log_command(id, level, grade, logtype); } + else if((level == "measurepoint") && (grade == "TRACE") && (!id.empty() && !is_blank(id))){ //数据追踪 + //打开监测点数据追踪开关 + process_trace_command(id,5); //5表示追踪次数 + } else{ std::cout << "type doesnt match" <type == cJSON_Number) { + tmp_level = loglevel->valueint; + } + else if (loglevel && loglevel->type == cJSON_String) { + tmp_level = atoi(loglevel->valuestring); + } + // 判断是否合法 (0~3) + if (tmp_level >= 0 && tmp_level <= 3) { + json_data.log_level = tmp_level; + } else { + json_data.log_level = 1; // 默认日志级别 + } + printf("terminal log_level: %d\n", json_data.log_level); + // monitorData 解析,填充到 line 数组中 cJSON* monitorData = cJSON_GetObjectItem(item, "monitorData"); if (monitorData != nullptr && monitorData->type == cJSON_Array) { @@ -1626,6 +1655,27 @@ int parse_control(const std::string& json_str, const std::string& output_dir) { else std::strncpy(monitor_data.monitor_id, "N/A", sizeof(monitor_data.monitor_id) - 1); + //添加loglevel字段 + cJSON* loglevel = cJSON_GetObjectItem(monitor_item, "loglevel"); // log_level + int tmp_level = -1; + // 先尝试读取 loglevel + if (loglevel && loglevel->type == cJSON_Number) { + tmp_level = loglevel->valueint; + } + else if (loglevel && loglevel->type == cJSON_String) { + tmp_level = atoi(loglevel->valuestring); + } + // 判断是否合法 (0~3) + if (tmp_level >= 0 && tmp_level <= 3) { + monitor_data.log_level = tmp_level; + } + else if (json_data.log_level >= 0 && json_data.log_level <= 3) { // 继承 terminal loglevel + monitor_data.log_level = json_data.log_level; + } + else { + monitor_data.log_level = 1; // 默认 warn + } + cJSON* monitor_name = cJSON_GetObjectItem(monitor_item, "name"); // monitor_name if (monitor_name && monitor_name->type == cJSON_String) std::strncpy(monitor_data.monitor_name, monitor_name->valuestring, sizeof(monitor_data.monitor_name) - 1); diff --git a/json/save2json.h b/json/save2json.h index 99ad1d6..d96206c 100644 --- a/json/save2json.h +++ b/json/save2json.h @@ -670,6 +670,8 @@ struct monitor // 监测点台账 char timestamp[64]; char status[255]; + int log_level;//日志级别,0:ERROR,1:WARN,2:NORMAL,3:DEBUG + }; struct terminal // 终端台账 { @@ -692,6 +694,7 @@ struct terminal // 终端台账 char timestamp[64]; monitor line[10]; // 最多 10 个监测点 + int log_level;//日志级别,0:ERROR,1:WARN,2:NORMAL,3:DEBUG }; #ifdef __cplusplus diff --git a/log4cplus/log4.h b/log4cplus/log4.h index 34a8112..5ddec1b 100644 --- a/log4cplus/log4.h +++ b/log4cplus/log4.h @@ -24,12 +24,9 @@ #include "appender.h" -#define LOGTYPE_COM 1 -#define LOGTYPE_DATA 2 - struct TypedLogger { log4cplus::Logger logger; - int logtype; + int code; TypedLogger(); TypedLogger(const log4cplus::Logger& l, int t); }; @@ -47,7 +44,7 @@ struct DebugSwitch { void set_level(int level); void enable_type(int type); void disable_type(int type); - bool match(const std::string& logger_name, int level, int logtype); + bool match(const std::string& logger_name, int level, int code); }; extern std::map logger_map; @@ -71,11 +68,14 @@ log4cplus::Logger init_logger(const std::string& full_name, const std::string& base_file, log4cplus::SharedAppenderPtr fileAppender); -void process_log_command(const std::string& id, const std::string& level, const std::string& grade, const std::string& logtype_str); +void process_log_command(const std::string& id, const std::string& level, const std::string& grade, int code); void update_log_entries_countdown(); +/////////////////////////////////////////////////////////////////////lnk20260306数据追踪 +void process_trace_command(const std::string& id, int times); + extern "C" { #endif void remove_loggers_by_terminal_id(const char* terminal_id_cstr); @@ -89,7 +89,7 @@ void log_warn(const char* key, const char* msg); void log_error(const char* key, const char* msg); void send_reply_to_kafka_c(const char* guid, const char* step, const char* result); -void send_reply_to_kafka_recall(const char* guid, const char* step, const char* result,const char* lineIndex,const char* recallStartDate,const char* recallEndDate); +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); void format_log_msg(char* buf, size_t buf_size, const char* fmt, ...); // ====================== ★新增:线程局部变量透传 code ====================== diff --git a/mms/mms_process.c b/mms/mms_process.c index f85a5e7..eaa559f 100644 --- a/mms/mms_process.c +++ b/mms/mms_process.c @@ -1328,6 +1328,7 @@ void print_monitor(const monitor* mon) { printf("Terminal Connect: %s\n", is_empty(mon->terminal_connect) ? "N/A" : mon->terminal_connect); printf("Timestamp: %s\n", is_empty(mon->timestamp) ? "N/A" : mon->timestamp); printf("Status: %s\n", is_empty(mon->status) ? "N/A" : mon->status); + printf("Log Level: %d\n", mon->log_level); } // 打印 terminal 结构体信息 @@ -1346,6 +1347,7 @@ void print_terminal(const terminal* tmnl) { printf("Address: %s\n", is_empty(tmnl->addr_str) ? "N/A" : tmnl->addr_str); printf("Port: %s\n", is_empty(tmnl->port) ? "N/A" : tmnl->port); printf("Timestamp: %s\n", is_empty(tmnl->timestamp) ? "N/A" : tmnl->timestamp); + printf("Log Level: %d\n", tmnl->log_level); // 打印监测点信息,如果监测点字段为空,则打印 N/A int i; diff --git a/mms/rdb_client.h b/mms/rdb_client.h index b9ba6b4..c628692 100644 --- a/mms/rdb_client.h +++ b/mms/rdb_client.h @@ -255,6 +255,9 @@ struct LD_info_t{ rptinfo_t **rptinfo; /**< rptinfo_t* 数组 */ int read_flag ; //CZY 2023-02-28 判断是否将监测点是否有效 + //测点日志等级 + int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG + char mp_id[256];//CZY 2023-08-20 监测点编码,例:8afaa char terminal_code[256];//CZY 2023-08-20 终端编码 //int ld_ins;//CZY 2023-08-20 逻辑设备实例号,例:1 @@ -273,8 +276,8 @@ struct LD_info_t{ int registcount;//lnk20250812 bool has_logged_regist;//lnk20250812 -//不使用 int iUnitOfTime;//CZY 2023-08-17 WW 2022年12月7日15:43:34 装置上送事件持续时间单位切换(0-ms; 1-s) +//不使用 int iStatOfTime;//CZY 2023-08-17 WW 2022年12月7日15:48:33 统计数据时间 0-北京时间 1-UTC时间 int iJournalTime;//CZY 2023-08-17 WW 2022年12月7日15:52:32 补招日志时间(0-UTC时间(日志、录波文件均为UTC时间); 1-北京时间(日志北京时间、录波文件UTC时间 注:仅四川地区+8小时读取补招日志)) //不使用 @@ -327,6 +330,8 @@ struct ied_usr_t{ void *cookie; double last_call_wavelist_time ; //上次召录波列表时间 + int log_level; //0 ERROR 1 WARN 2 NORMAL 3 DEBUG + char terminal_id[256];//CZY 2023-08-20 终端id,例:8afaa9a15707483a0157262f8e78077d char org_name[256];//CZY 2023-08-20 所属单位,例:南京供公司 char maint_name[256];//CZY 2023-08-20 运维单位,例:南京供公司