//////////////////////////////////////////////////////////////////////////////////////////////////////////
#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 <unordered_map>
#include <chrono>
#include <memory>
//////////////////////////////////////////////////////////////////////////////////////////////////////////
#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"
#include "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;
////////////////////////////////////////////////////////////////////////////////////////////////////
const int LOGTYPE_DEFAULT = LOG_CODE_OTHER;

static const int LOGTYPE_WILDCARD = 999;   // logtype 通配号码
static const char* ID_WILDCARD = "all";     // id 通配字段

std::map<std::string, TypedLogger> logger_map;
DebugSwitch g_debug_switch;
/////////////////////////////////////////////////


// 原子指针：append 线程只读它，不加锁
std::shared_ptr<LogLevelCache> g_level_cache_sp;
///////////////////////////////////////////////////////////////
//用来控制日志上送的结构
struct LOGEntry {
    std::string id; //测点和装置需要的id
    std::string level; // terminal / measurepoint /process
    int logtype; // 日志类型
    int min_grade;  // DEBUG / INFO / WARN / ERROR
    int countdown;  // 倒计时，单位秒
};

//日志上送map管理
std::map<std::string, LOGEntry> g_log_entries;
pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER;

/////////////////////////////////////////////////////////////////////
std::string build_debug_key(const std::string& id, const std::string& level, int logtype);

//////////////////////////////////////////////////////////

#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";
    }
}

const char* loglevel_to_str(int lv) {
    switch (lv) {
        case DEBUG_LOG_LEVEL: return "DEBUG";
        case INFO_LOG_LEVEL:  return "NORMAL";
        case WARN_LOG_LEVEL:  return "WARN";
        case ERROR_LOG_LEVEL: return "ERROR";
        default:              return "UNKNOWN";
    }
}

static int str_to_loglevel(const std::string& s, int default_level = WARN_LOG_LEVEL)
{
    if (s == "DEBUG")  return DEBUG_LOG_LEVEL;
    if (s == "NORMAL") return INFO_LOG_LEVEL;   // NORMAL 当 INFO
    if (s == "INFO")   return INFO_LOG_LEVEL;
    if (s == "WARN")   return WARN_LOG_LEVEL;
    if (s == "ERROR")  return ERROR_LOG_LEVEL;
    return default_level;                       // 空/非法都兜底
}
//////////////////////////////////////////////////////////////////////
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;
}
////////////////////////////////////////////////////////////////////////////////////
static LogLevelCache* build_cache_unlocked()
{
    LogLevelCache* nc = new LogLevelCache;
    nc->term_min.reserve(terminal_devlist.size());

    for (const terminal_dev& t : terminal_devlist) {
        const int t_lv = str_to_loglevel(t.DevLogLevel, WARN_LOG_LEVEL);

        if (!t.terminal_id.empty())
            nc->term_min[t.terminal_id] = t_lv;

        for (const ledger_monitor& m : t.line) {
            if (m.monitor_id.empty()) continue;

            // 监测点优先；空/非法自动兜底到终端 t_lv
            const int m_lv = str_to_loglevel(m.LineLogLevel, t_lv);
            nc->mp_min[m.monitor_id] = m_lv;
        }
    }
    return nc;
}

void refresh_log_level_cache_locked()
{
    std::shared_ptr<LogLevelCache> nc(build_cache_unlocked());
    std::atomic_store_explicit(&g_level_cache_sp, nc, std::memory_order_release);
}
//////////////////////////////////////////////////////////////////////////////////

class SendAppender : public Appender {
private:
    struct RateState {
        uint64_t hit_count = 0;  // 同一条日志累计命中次数
        std::chrono::steady_clock::time_point last_emit =
            std::chrono::steady_clock::time_point::min();
    };

    static std::unordered_map<std::string, RateState> s_rate_map;   //频率map
    static std::mutex s_rate_mutex;

    // 定义“同一条日志”的规则：logger + level + code + msg   //原来只区分了日志登记名和等级，现在具体到每一条日志
    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();
    }

    // 前 3 次：1 秒一次；第 3 次起：300 秒一次，一小时恢复
    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.last_emit != steady_clock::time_point::min()) {
            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.last_emit == steady_clock::time_point::min()) {
            st.last_emit = now;
            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;
    }

    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 logtype,
                                 int level_val) {//告警等级
        pthread_mutex_lock(&g_log_mutex);

        // 1) 精确匹配：id + level + logtype    //这个id指定日志种类指定级别的日志
        if (find_entry_allow(build_debug_key(id, level_str, logtype), level_val)) {
            pthread_mutex_unlock(&g_log_mutex);
            return true;
        }

        // 2) logtype 通配：id + level + -1  //这个id指定日志级别的所有日志
        if (find_entry_allow(build_debug_key(id, level_str, LOGTYPE_WILDCARD), level_val)) {
            pthread_mutex_unlock(&g_log_mutex);
            return true;
        }

        // 3) id 通配：* + level + logtype  //这个id的指定级别的日志
        if (find_entry_allow(build_debug_key(ID_WILDCARD, level_str, logtype), level_val)) {
            pthread_mutex_unlock(&g_log_mutex);
            return true;
        }

        // 4) 双通配：* + level + -1    //所有指定级别的日志，即根据台账的等级来上送所有日志
        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);
        if (id.empty()) return DEFAULT_LEVEL;

        std::shared_ptr<LogLevelCache> c =
            std::atomic_load_explicit(&g_level_cache_sp, std::memory_order_acquire);
        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;
    }

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
            level_str = "terminal";

        // TLS code
        int code = g_log_code_tls;

        const int safe_logtype = code; // 使用 code 作为 logtype

        bool allow_send = false;

        int min_send_level = get_min_send_level_cached(level_str, logger_name);

        std::cout << "[LOG] logger: " << logger_name
                  << ", level_str: " << level_str
                  << ", level: " << level
                  << ", min_send_level: " << min_send_level << std::endl;

        // ① 高于“台账阈值”的日志：直接上送
        if (level >= min_send_level) {
            allow_send = true;
        } else {
            //  NORMAL/DEBUG 默认不上送，必须命令打开
            std::string ctrl_level = level_str; // "process" / "terminal" / "measurepoint"
        
            std::string ctrl_id;
            if (ctrl_level == "process") {
                ctrl_id = "process";            // process 用固定 id
            } else {
                ctrl_id = extract_logger_id(logger_name); // terminal.<id> / monitor.<id>
                if (ctrl_id.empty()) {
                    // 没解析出 id，就不给低等级上送（避免误发）
                    allow_send = false;
                }
            }
        
            if (!ctrl_id.empty()) {
                allow_send = allow_low_level_send(ctrl_id, ctrl_level, safe_logtype, level);
            }
        }

        if (!allow_send) {
            return;
        }

        // ★新增：限频判断（同一条日志前 5 次 1 秒一次；之后 300 秒一次）
        const std::string key = make_key(logger_name, level, code, msg);
        if (!should_emit(key)) {
            return;
        }

        std::ostringstream oss;
        oss << "{\"processNo\":\"" << std::to_string(g_front_seg_index)
            << "\",\"nodeId\":\"" << FRONT_INST
            << "\",\"businessId\":\"" << extract_logger_id(logger_name)
            << "\",\"level\":\"" << level_str
            << "\",\"time\":\"" << now_yyyy_mm_dd_hh_mm_ss()
            << "\",\"grade\":\"" << get_level_str(level)
            // ★建议：code 用数字（不是字符串）
            << "\",\"code\":" << code
            << ",\"log\":\"" << escape_json(msg) << "\"}";

        queue_data_t connect_info;
        connect_info.strTopic = G_LOG_TOPIC;
        connect_info.strText = oss.str();
        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 (size_t 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();
    }

    void close() override {
        // 可空实现
    }

public:
    SendAppender() {}
    virtual ~SendAppender() {
        destructorImpl();
    }
};

//用来控制日志上送的静态变量定义
std::unordered_map<std::string, SendAppender::RateState> SendAppender::s_rate_map;
std::mutex SendAppender::s_rate_mutex;

// 生成唯一 key
std::string build_debug_key(const std::string& id, const std::string& level, int logtype) {
    std::ostringstream oss;                                                                  
    oss << id << "|" << level << "|" << logtype;                                             
    return oss.str();                                                                        
}

// 外部线程中调用：每秒更新所有倒计时，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 logtype) {
    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, logtype);

    pthread_mutex_lock(&g_log_mutex);

    LOGEntry& entry = g_log_entries[key];  // 会自动 insert 或取已有
    entry.id = id;
    entry.level = level;
    entry.logtype = logtype;
    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_DEFAULT);
    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()) {

            // 所有终端日志写到同一个 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_DEFAULT);

            
        }

        // 初始化监测点日志，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_DEFAULT);

                    
                }
            }
        }

        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_DEFAULT);

        

        // 初始化监测点日志
        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_DEFAULT);

                
            }
        }
    }
}

//单个终端的日志删除
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()) {
            std::cout << "[LOG][MISS] logger not found, key=" 
                      << (key ? key : "NULL") << std::endl;
            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); }

    //标准化日志接口
    // #define LOGMSG_WITH_TS  // 需要时间时再打开

    //已在头文件添加编译校验
    void format_log_msg(char* buf, size_t buf_size, const char* fmt, ...) {
        if (!buf || buf_size == 0) return;
        buf[0] = '\0';
        if (!fmt) return;

        va_list args;
        va_start(args, fmt);
    #ifdef LOGMSG_WITH_TS
        // 写入时间
        time_t now = time(NULL);
        struct tm tm_info;
        localtime_r(&now, &tm_info);
        size_t n = strftime(buf, buf_size, "%Y-%m-%d %H:%M:%S ", &tm_info);
        if (n < buf_size) {
            vsnprintf(buf + n, buf_size - n, fmt, args);
        }
    #else
        vsnprintf(buf, buf_size, fmt, args);
    #endif
        va_end(args);
    }
    
#ifdef __cplusplus
}
#endif
