Files
microser/cfg_parse/log4.cpp
2026-04-17 16:35:35 +08:00

868 lines
32 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 <iostream>
#include <map>
#include <set>
#include <string>
#include <ctime>
#include <sstream>
#include <pthread.h>
#include <unistd.h>
#include <cstring>
//目录创建
#include <sys/stat.h>
#include <sys/types.h>
#include "../json/mms_json_inter.h"
#include "../mms/rdb_client.h"
#include "../include/node.h"//lnk20241223
#include "../log4cplus/log4.h"//后移防止min/max定义冲突
///////////////////////////////////////////////lnk20260303添加日志上送控制
#include <unordered_map>
#include <chrono>
#include <mutex>
#include <atomic>
#include <memory>
struct LogLevelCache { //记录日志等级的缓存减少频繁访问全局map的开销
std::unordered_map<std::string, int> term_min; // terminal_id -> min level
std::unordered_map<std::string, int> mp_min; // mp_id -> min level
};
// append线程只读不加锁
static std::shared_ptr<LogLevelCache> 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<std::string, LOGEntry> 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<Ckafka_data_t> 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<LogLevelCache> 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<std::string, LOGEntry>::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.<id> / monitor.<mp>
if (id.empty()) return DEFAULT_LEVEL;
std::shared_ptr<LogLevelCache> 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.<id> / monitor.<mp>
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<std::string>::iterator it;
for (it = targets.begin(); it != targets.end(); ++it) {
if (logger_name.find(*it) != std::string::npos)
return true;
}
return false;
}
std::map<std::string, TypedLogger> 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<std::string, RateState> 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<std::mutex> 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<seconds>(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<milliseconds>(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<seconds>(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<std::string, SendAppender::RateState> SendAppender::s_rate_map;
std::mutex SendAppender::s_rate_mutex;
// 外部线程中调用每秒更新所有倒计时0 则删除
void update_log_entries_countdown() {
pthread_mutex_lock(&g_log_mutex);
std::map<std::string, LOGEntry>::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<Layout>(
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<Layout>(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.<mp_id>
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<Layout>(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<Layout>(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.<mp_id>
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<Layout>(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<std::string, TypedLogger>::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;
}
*/