Files
front_linux/LFtid1056/cloudfront/code/log4.cpp

505 lines
19 KiB
C++
Raw Normal View History

2025-06-24 17:55:34 +08:00
//////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <ctime>
#include <sstream>
#include <mutex>
#include <thread>
#include <atomic>
#include <cstdio>
#include <unistd.h>
#include <cstring>
#include <sys/stat.h>
#include <sys/types.h>
#include <list>
#include <vector>
#include <array>
#include <fnmatch.h>
//////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "log4cplus/logger.h"
#include "log4cplus/configurator.h"
#include "log4cplus/fileappender.h"
#include "log4cplus/layout.h"
#include "log4cplus/ndc.h"
#include "log4cplus/spi/loggingevent.h"
#include "rocketmq.h"
#include "interface.h"
2025-09-23 10:19:53 +08:00
#include "log4.h"
2025-06-24 17:55:34 +08:00
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//log4命名空间
using namespace log4cplus;
using namespace log4cplus::helpers;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//queue结构定义
extern std::mutex queue_data_list_mutex; //queue发送数据锁
extern std::list<queue_data_t> queue_data_list; //queue发送数据链表
extern unsigned int g_node_id;
extern int g_front_seg_index;
extern std::string FRONT_INST;
extern std::string subdir;
//日志主题
extern std::string G_LOG_TOPIC;
2025-09-10 17:09:12 +08:00
//////////////////////////////////////////////////////////
/* log4.cpp 顶部 */
#if __cplusplus >= 201103L
thread_local int g_log_code_tls = 0;
#else
__thread int g_log_code_tls = 0;
#endif
2025-06-24 17:55:34 +08:00
////////////////////////////////////////////////////////辅助函数
2025-09-10 17:09:12 +08:00
2025-06-24 17:55:34 +08:00
// 递归创建目录
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) {
size_t pos = logger_name.find('.');
if (pos != std::string::npos && pos + 1 < logger_name.size()) {
return logger_name.substr(pos + 1);
}
return ""; // 没有找到 '.' 或 '.' 后为空
}
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), 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";
// ★读取 TLS 中的 code在打日志的线程里由宏设定
int code = g_log_code_tls; // 若未显式传入,则为 0
2025-06-24 17:55:34 +08:00
if (level == ERROR_LOG_LEVEL || level == WARN_LOG_LEVEL || g_debug_switch.match(logger_name, level, logtype)) {
std::ostringstream oss;
oss << "{\"processNo\":\"" << std::to_string(g_front_seg_index)
<< "\",\"nodeId\":\"" << FRONT_INST
<< "\",\"businessId\":\"" << extract_logger_id(logger_name)
<< "\",\"level\":\"" << level_str
<< "\",\"grade\":\"" << get_level_str(level)
// ★新增:输出 code 字段(整型)
<< "\",\"code\":\"" << code
2025-06-24 17:55:34 +08:00
<< "\",\"log\":\"" << escape_json(msg) << "\"}";
std::string jsonString = oss.str();
queue_data_t connect_info;
connect_info.strTopic = G_LOG_TOPIC;
connect_info.strText = jsonString;
2025-09-22 13:26:52 +08:00
connect_info.tag = G_LOG_TAG;
connect_info.key = G_LOG_KEY;
2025-06-24 17:55:34 +08:00
std::lock_guard<std::mutex> lock(queue_data_list_mutex);
queue_data_list.push_back(connect_info);
}
}
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 基类资源
}
};
//用来控制日志上送的结构
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, 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::unique_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 = FRONT_PATH + "/" + subdir + "/processNo" + std::to_string(g_front_seg_index) + "/log";
logger_map["process"] = TypedLogger(init_logger(std::string("process"), base_dir, std::string("process")), LOGTYPE_DATA);
std::cout << "process log init ok" << std::endl;
}
//单个终端的日志初始化
void init_loggers_bydevid(const std::string& dev_id)
{
if (dev_id.empty()) return;
std::string terminal_id = dev_id;
std::string base_dir = FRONT_PATH + "/" + subdir + "/processNo" + std::to_string(g_front_seg_index) + "/log";
for (size_t i = 0; i < terminal_devlist.size(); ++i) {
terminal_dev& term = terminal_devlist[i];
// 跳过终端台账信息为空的节点
if (term.terminal_id.empty()) {
std::cout << "terminal_dev No." << i << " is null" << std::endl;
continue;
}
// 跳过不匹配的终端
if (term.terminal_id != dev_id) continue;
std::string ip_str = term.addr_str.empty() ? "unknown" : term.addr_str;
std::string device_dir = base_dir + "/" + ip_str;
2025-09-22 16:46:33 +08:00
std::string device_key = std::string("terminal.") + dev_id;
2025-06-24 17:55:34 +08:00
// 添加判断:终端日志 logger 是否已存在
2025-09-22 16:46:33 +08:00
if (logger_map.find(device_key) == logger_map.end()) {
2025-06-24 17:55:34 +08:00
// 所有终端日志com 和 data写到同一个 device 日志文件中
std::string file_path_t = device_dir + "/" + dev_id + ".log";
// 共用一个 appender 实例
SharedAppenderPtr device_appender(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2));
device_appender->setLayout(std::unique_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
2025-09-22 16:46:33 +08:00
Logger device_logger = init_logger(device_key, device_dir, dev_id, device_appender);
logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DATA);
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
DIY_WARNLOG(dev_id.c_str(), "【WARN】终端id:%s终端级日志初始化完毕", term.terminal_id.c_str());
2025-06-24 17:55:34 +08:00
}
// 初始化监测点日志monitor.<mp_id>.COM / .DATA
for (size_t j = 0; j < term.line.size(); ++j) {
const ledger_monitor& monitor = term.line[j];
if (!monitor.monitor_id.empty()) {
2025-09-22 16:46:33 +08:00
std::ostringstream mon_key, mon_path, mon_name;
mon_key << "monitor." << monitor.monitor_id;
2025-06-24 17:55:34 +08:00
mon_path << device_dir << "/monitor" << j;
mon_name << monitor.monitor_id;
// 判断监测点 logger 是否已存在
2025-09-22 16:46:33 +08:00
if (logger_map.find(mon_key.str()) == logger_map.end()) {
2025-06-24 17:55:34 +08:00
// 所有监测点日志com 和 data写到同一个 monitor 日志文件中
std::string file_path_m = mon_path.str() + "/" + mon_name.str() + ".log";
// 共用一个 appender 实例
SharedAppenderPtr monitor_appender(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2));
monitor_appender->setLayout(std::unique_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
2025-09-22 16:46:33 +08:00
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_DATA);
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
DIY_WARNLOG(monitor.monitor_id.c_str(), "【WARN】监测点:%s - id:%s监测点级日志初始化完毕", monitor.monitor_name.c_str(), monitor.logical_device_seq.c_str());
2025-06-24 17:55:34 +08:00
}
}
}
break; // 只匹配一个 terminal_id
}
}
//初始化台账日志
void init_loggers()
{
std::string base_dir = FRONT_PATH + "/" + subdir + "/processNo" + std::to_string(g_front_seg_index) + "/log";
// 遍历所有终端
for (size_t t = 0; t < terminal_devlist.size(); ++t) {
terminal_dev& term = terminal_devlist[t];
// 跳过无效终端
if (term.terminal_id.empty()) {
std::cout << "terminal_dev No." << t << " is null" << std::endl;
continue;
}
std::string ip_str = term.addr_str.empty() ? "unknown" : term.addr_str;
std::string device_dir = base_dir + "/" + ip_str;
2025-09-22 16:46:33 +08:00
std::string device_key = std::string("terminal.") + term.terminal_id;
2025-06-24 17:55:34 +08:00
// 所有终端日志com 和 data写到同一个 device 日志文件中
std::string file_path_t = device_dir + "/" + term.terminal_id + ".log";
// 共用一个 appender 实例
SharedAppenderPtr device_appender(new RollingFileAppender(file_path_t, 1 * 1024 * 1024, 2));
device_appender->setLayout(std::unique_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
2025-09-22 16:46:33 +08:00
Logger device_logger = init_logger(device_key, device_dir, term.terminal_id, device_appender);
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DATA);
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
DIY_WARNLOG(term.terminal_id.c_str(), "【WARN】终端id:%s终端级日志初始化完毕", term.terminal_id.c_str());
2025-06-24 17:55:34 +08:00
// 初始化监测点日志
for (size_t i = 0; i < term.line.size(); ++i) {
const ledger_monitor& monitor = term.line[i];
if (!monitor.monitor_id.empty()) {
2025-09-22 16:46:33 +08:00
std::ostringstream mon_key, mon_path, mon_name;
mon_key << "monitor." << monitor.monitor_id;
2025-06-24 17:55:34 +08:00
mon_path << device_dir << "/monitor" << i; // 用monitor+序号作为目录
mon_name << monitor.monitor_id;
std::string file_path_m = mon_path.str() + "/" + mon_name.str() + ".log";
// 共用一个 appender 实例
SharedAppenderPtr monitor_appender(new RollingFileAppender(file_path_m, 1 * 1024 * 1024, 2));
monitor_appender->setLayout(std::unique_ptr<Layout>(new PatternLayout("%D{%Y-%m-%d %H:%M:%S} [%p] [%c] %m%n")));
2025-09-22 16:46:33 +08:00
Logger mon_logger = init_logger(mon_key.str(), mon_path.str(), mon_name.str(), monitor_appender);
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
logger_map[mon_key.str()] = TypedLogger(mon_logger, LOGTYPE_DATA);
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
DIY_WARNLOG(mon_key.str().c_str(), "【WARN】监测点:%s - id:%s监测点级日志初始化完毕",
2025-08-08 11:16:38 +08:00
monitor.monitor_name.c_str(), monitor.logical_device_seq.c_str());
2025-06-24 17:55:34 +08:00
}
}
}
}
//单个终端的日志删除
void remove_loggers_by_terminal_id(const std::string& terminal_id) {
// 遍历所有终端
for (size_t t = 0; t < terminal_devlist.size(); ++t) {
terminal_dev& term = terminal_devlist[t];
if (term.terminal_id != terminal_id) continue;
// 删除终端日志 logger
2025-09-22 16:46:33 +08:00
std::string terminal_key = "terminal." + terminal_id;
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
if (logger_map.count(terminal_key)) {
logger_map[terminal_key].logger.removeAllAppenders();
logger_map.erase(terminal_key);
2025-06-24 17:55:34 +08:00
}
// 删除监测点日志 logger
for (size_t i = 0; i < term.line.size(); ++i) {
const ledger_monitor& monitor = term.line[i];
if (!monitor.monitor_id.empty()) {
std::string mon_prefix = "monitor." + monitor.monitor_id;
2025-09-22 16:46:33 +08:00
std::string mon_key = mon_prefix;
2025-06-24 17:55:34 +08:00
2025-09-22 16:46:33 +08:00
if (logger_map.count(mon_key)) {
logger_map[mon_key].logger.removeAllAppenders();
logger_map.erase(mon_key);
2025-06-24 17:55:34 +08:00
}
}
}
std::cout << "[LOG] Logger for terminal_id=" << terminal_id << " removed." << std::endl;
break; // 找到匹配终端后退出
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////封装函数C/C++通用
#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_queue_c(const char* guid, const char* step, const char* result) {
send_reply_to_queue(std::string(guid), std::string(step), std::string(result));
}
//标准化日志接口
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