777 lines
28 KiB
C++
777 lines
28 KiB
C++
|
||
#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;
|
||
/////////////////////////////////////////////////////////
|
||
//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);
|
||
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) {
|
||
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 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<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) {
|
||
using namespace std::chrono;
|
||
const auto now = steady_clock::now();
|
||
|
||
std::lock_guard<std::mutex> lk(s_rate_mutex);
|
||
RateState& st = s_rate_map[key];
|
||
|
||
const int RESET_SEC = 3600;
|
||
if (st.has_emit) {
|
||
auto idle = duration_cast<seconds>(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<seconds>(now - st.last_emit).count();
|
||
if (elapsed >= period_sec) {
|
||
st.last_emit = now;
|
||
return true;
|
||
}
|
||
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(device_key.c_str(),"【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(mon_key.str().c_str(),"【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(device_key.c_str(),"【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(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) {
|
||
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;
|
||
}
|
||
*/ |