Files
microser/cfg_parse/log4.cpp
2025-05-09 16:53:07 +08:00

478 lines
17 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>
//kafka结构定义
#include "../json/mms_json_inter.h"
#include "../mms/rdb_client.h"
#include "../include/node.h"//lnk20241223
extern unsigned int g_node_id;
extern int g_front_seg_index;
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;
//log4命名空间
using namespace log4cplus;
using namespace log4cplus::helpers;
////////////////////////////////////////////////////////辅助函数
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;
}
//////////////////////////////////////////////////////////////////////
TypedLogger::TypedLogger() {}
TypedLogger::TypedLogger(const Logger& l, int t) : logger(l), logtype(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;
class SendAppender : public Appender {
protected:
void append(const spi::InternalLoggingEvent& event) {
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
level_str = "terminal";
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)
<< "\",\"id\":\"" << logger_name
<< "\",\"level\":\"" << level_str
<< "\",\"grade\":\"" << level
<< "\",\"logtype\":\"" << (logtype == LOGTYPE_COM ? "com" : "data")
<< "\",\"frontType\":\"" << get_front_type_from_subdir()
<< "\",\"log\":\"" << escape_json(msg) << "\"}";
std::string jsonString = oss.str();
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();
}
}
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() {}
};
//用来控制日志上送的结构
struct LOGEntry {
std::string id;
std::string level; // terminal / measurepoint
int logtype; // com / data
int min_grade;
int countdown;
};
//日志上送map管理
std::map<std::string, LOGEntry> 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");
}
// 外部线程中调用每秒更新所有倒计时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, const std::string& logtype_str) {
if (level != "terminal" && level != "measurepoint") 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);
pthread_mutex_lock(&g_log_mutex);
LOGEntry& entry = g_log_entries[key]; // 会自动 insert 或取已有
entry.id = id;
entry.level = level;
entry.logtype = type;
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) {
// 确保日志目录存在
create_directory_recursive(file_dir);
Logger logger = Logger::getInstance(full_name);
std::string file_path = file_dir + "/" + base_file + ".log";
// 使用滚动日志(大小轮转),最多保留 2 个备份,每个最大 1MB
SharedAppenderPtr fileAppender(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;
}
//进程的日志
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("process", base_dir, "process"), LOGTYPE_DATA);
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(ip_str + ".COM") == logger_map.end() &&
logger_map.find(ip_str + ".DATA") == logger_map.end()) {
// 所有终端日志com 和 data写到同一个 device 日志文件中
Logger device_logger = init_logger(device_key, device_dir, dev_id); //用终端id作为日志文件名
logger_map[ip_str + ".COM"] = TypedLogger(device_logger, LOGTYPE_COM);
logger_map[ip_str + ".DATA"] = TypedLogger(device_logger, LOGTYPE_DATA);
}
// 初始化监测点
// 监测点 logger 名称格式monitor.<mp_id>.COM / .DATA
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() + ".COM") == logger_map.end() &&
logger_map.find(mon_key.str() + ".DATA") == logger_map.end()) {
Logger mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str());
logger_map[mon_key.str() + ".COM"] = TypedLogger(mon_logger, LOGTYPE_COM);
logger_map[mon_key.str() + ".DATA"] = TypedLogger(mon_logger, LOGTYPE_DATA);
}
}
}
break; // 只匹配一个 terminal_id
}
}
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 日志文件中
Logger device_logger = init_logger(device_key, device_dir, ied_usr->terminal_id); //用终端id作为日志文件名
logger_map[device_key + ".COM"] = TypedLogger(device_logger, LOGTYPE_COM);
logger_map[device_key + ".DATA"] = TypedLogger(device_logger, LOGTYPE_DATA);
char buf[256];
sprintf(buf, "终端id:%s终端级日志初始化完毕", ied_usr->terminal_id);
LOG4CPLUS_DEBUG(logger_map[device_key + ".DATA"].logger, buf);
// 初始化监测点
// 监测点 logger 名称格式monitor.<mp_id>.COM / .DATA
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 mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str());
logger_map[mon_key.str() + ".COM"] = TypedLogger(mon_logger, LOGTYPE_COM);
logger_map[mon_key.str() + ".DATA"] = TypedLogger(mon_logger, LOGTYPE_DATA);
char buf[256];
sprintf(buf, "监测点id:%s监测点级日志初始化完毕", ied_usr->LD_info[i].mp_id);
LOG4CPLUS_DEBUG(logger_map[mon_key.str() + ".DATA"].logger, buf);
}
}
}
}
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 com_key = "terminal." + terminal_id + ".COM";
std::string data_key = "terminal." + terminal_id + ".DATA";
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);
}
// 删除监测点日志 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_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_data_key)) {
logger_map[mon_data_key].logger.removeAllAppenders();
logger_map.erase(mon_data_key);
}
}
}
std::cout << "[LOG] Logger for terminal_id=" << terminal_id << " removed.\n";
break; // 找到匹配终端后退出
}
}
extern "C" {
// 公共函数
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); }
}
/*
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;
}
*/