Files
front_linux/LFtid1056/cloudfront/code/log4.cpp
2025-09-22 16:46:33 +08:00

506 lines
19 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 <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/log4.h"
#include "log4cplus/spi/loggingevent.h"
#include "rocketmq.h"
#include "interface.h"
#include "log4cplus/log4.h"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//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;
//////////////////////////////////////////////////////////
/* log4.cpp 顶部 */
#if __cplusplus >= 201103L
thread_local int g_log_code_tls = 0;
#else
__thread int g_log_code_tls = 0;
#endif
////////////////////////////////////////////////////////辅助函数
// 递归创建目录
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
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
<< "\",\"log\":\"" << escape_json(msg) << "\"}";
std::string jsonString = oss.str();
queue_data_t connect_info;
connect_info.strTopic = G_LOG_TOPIC;
connect_info.strText = jsonString;
connect_info.tag = G_LOG_TAG;
connect_info.key = G_LOG_KEY;
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;
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(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")));
Logger device_logger = init_logger(device_key, device_dir, dev_id, device_appender);
logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DATA);
DIY_WARNLOG(dev_id.c_str(), "【WARN】终端id:%s终端级日志初始化完毕", term.terminal_id.c_str());
}
// 初始化监测点日志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()) {
std::ostringstream mon_key, mon_path, mon_name;
mon_key << "monitor." << monitor.monitor_id;
mon_path << device_dir << "/monitor" << j;
mon_name << monitor.monitor_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(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")));
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);
DIY_WARNLOG(monitor.monitor_id.c_str(), "【WARN】监测点:%s - id:%s监测点级日志初始化完毕", monitor.monitor_name.c_str(), monitor.logical_device_seq.c_str());
}
}
}
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;
std::string device_key = std::string("terminal.") + term.terminal_id;
// 所有终端日志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")));
Logger device_logger = init_logger(device_key, device_dir, term.terminal_id, device_appender);
logger_map[device_key] = TypedLogger(device_logger, LOGTYPE_DATA);
DIY_WARNLOG(term.terminal_id.c_str(), "【WARN】终端id:%s终端级日志初始化完毕", term.terminal_id.c_str());
// 初始化监测点日志
for (size_t i = 0; i < term.line.size(); ++i) {
const ledger_monitor& monitor = term.line[i];
if (!monitor.monitor_id.empty()) {
std::ostringstream mon_key, mon_path, mon_name;
mon_key << "monitor." << monitor.monitor_id;
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")));
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);
DIY_WARNLOG(mon_key.str().c_str(), "【WARN】监测点:%s - id:%s监测点级日志初始化完毕",
monitor.monitor_name.c_str(), monitor.logical_device_seq.c_str());
}
}
}
}
//单个终端的日志删除
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
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 (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;
std::string mon_key = mon_prefix;
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." << 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