Files
blackbird/liblogc/inc/us/LogC.hpp
qing aa3741b861 feat(liblogc): 统一集中式日志客户端库 + 全量迁移
新增:
- liblogc/: C++ 统一日志库 (libus_logc.so),支持 set_endpoint/set_service_name/set_level
- pymsg/pymsg/logc.py: Python 统一日志模块,基于 AsyncRpcQueue

C++ 迁移 (删除 6 份本地 LogC.hpp 副本):
- ca, gateway: <us/LogC.hpp> + set_service_name
- captcha2, confs2, smtp3: using qing::LogC + set_service_name
- core: ConfsLogC → qing::LogC (ConfsTask.cpp)
- 所有 Makefile 添加 -lus_logc

Python 迁移 (6 个 log_client.py → 4 行薄包装):
- smtp, redirect80, confs, captcha, dnfs, static
- 全部委托到 pymsg.logc.LogClient

Go (waf): 保持现状,自成体系
2026-08-10 04:51:43 +00:00

145 lines
5.1 KiB
C++
Raw Permalink 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.
#pragma once
/** LogC — 统一集中式日志客户端(C++ 版)
*
* 通过 pymsg TCP 调用 dbv2 execute 路由,逐条 INSERT 到 Logs 表。
*
* 设计要点:
* - 异步非阻塞:基于 pmc::net::AsyncQueue 内存队列 + 后台线程批量推送
* - fail-safe:db 服务不可用时静默丢弃(仅 stderr 提示),绝不影响业务
* - 缓冲批量:队列满 50 条或超时 2 秒时自动 flush
* - 背压保护:队列上限 10000 条,满时丢最旧日志,防止内存无限增长
* - 级别过滤:set_level() 设定阈值,低于阈值的日志直接丢弃
* - 可配置 endpoint / service_name,各服务复用同一份库
*
* 用法:
* #include <us/LogC.hpp>
*
* // 启动时配置(默认 127.0.0.1:8005)
* qing::LogC::instance().set_endpoint("127.0.0.1", 8005);
* qing::LogC::instance().set_service_name("gateway-log");
* qing::LogC::instance().set_level("warn");
*
* qing::LogC::instance().info("gateway", "启动完成");
* qing::LogC::instance().error("gateway", "连接失败",
* std::nullopt, R"({"errno":111})");
*
* // 进程退出前调用(幂等)
* qing::LogC::instance().shutdown();
*/
#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
// forward-declare: 避免在头文件中拉入 AsyncQueue 模板
namespace pmc { namespace net {
template <typename T> class AsyncQueue;
}}
namespace qing {
class LogC {
public:
struct Entry {
std::string service;
std::string level;
std::string message;
std::optional<std::string> trace_id;
std::optional<std::string> extra; // 已序列化的 JSON 字符串(存 Logs.extra TEXT)
};
// 日志级别 → 数值(大写兼容)
static const std::unordered_map<std::string, int>& level_order() {
static const std::unordered_map<std::string, int> k = {
{"debug", 0}, {"info", 1}, {"warn", 2}, {"warning", 2},
{"error", 3}, {"critical", 3},
{"DEBUG", 0}, {"INFO", 1}, {"WARN", 2}, {"WARNING", 2},
{"ERROR", 3}, {"CRITICAL", 3},
};
return k;
}
static LogC& instance() {
static LogC inst;
return inst;
}
// ---- 配置(启动时调用,线程安全)-------------------------------------
void set_endpoint(const std::string& addr, int port);
void set_service_name(const std::string& name);
// 设置日志级别阈值(debug|info|warn|error,大小写不限)
void set_level(const std::string& level);
// ---- 推送接口(非阻塞,立即返回)-------------------------------------
void push(const std::string& service, const std::string& level,
const std::string& message,
std::optional<std::string> trace_id = std::nullopt,
std::optional<std::string> extra = std::nullopt);
void debug(const std::string& service, const std::string& message,
std::optional<std::string> trace_id = std::nullopt,
std::optional<std::string> extra = std::nullopt) {
push(service, "DEBUG", message, std::move(trace_id), std::move(extra));
}
void info(const std::string& service, const std::string& message,
std::optional<std::string> trace_id = std::nullopt,
std::optional<std::string> extra = std::nullopt) {
push(service, "INFO", message, std::move(trace_id), std::move(extra));
}
void warning(const std::string& service, const std::string& message,
std::optional<std::string> trace_id = std::nullopt,
std::optional<std::string> extra = std::nullopt) {
push(service, "WARNING", message, std::move(trace_id), std::move(extra));
}
void error(const std::string& service, const std::string& message,
std::optional<std::string> trace_id = std::nullopt,
std::optional<std::string> extra = std::nullopt) {
push(service, "ERROR", message, std::move(trace_id), std::move(extra));
}
void critical(const std::string& service, const std::string& message,
std::optional<std::string> trace_id = std::nullopt,
std::optional<std::string> extra = std::nullopt) {
push(service, "CRITICAL", message, std::move(trace_id), std::move(extra));
}
// ---- 生命周期 -------------------------------------------------------
void flush(); // 手动 flush 当前积攒批次,等待完成
void shutdown(); // 停止后台线程并 flush 剩余日志(幂等)
private:
LogC();
~LogC();
LogC(const LogC&) = delete;
LogC& operator=(const LogC&) = delete;
void _send_batch(std::vector<Entry>&& batch);
static std::string _utc_now_iso();
// ---- 配置项 ---------------------------------------------------------
std::string _db_addr = "127.0.0.1";
int _db_port = 8005;
std::string _service_name = "log";
std::mutex _endpoint_mtx; // 保护 _db_addr/_db_port/_service_name
std::atomic<int> _level_threshold{1}; // 默认 INFO(1)
static constexpr size_t _BATCH_SIZE = 50;
static constexpr double _FLUSH_INTERVAL = 2.0;
static constexpr size_t _MAX_QUEUE_SIZE = 10000;
static constexpr double _TIMEOUT = 5.0;
// ---- 核心队列(unique_ptr 延迟到 .cpp 中实例化,保持头文件轻量)----
std::unique_ptr<pmc::net::AsyncQueue<Entry>> _queue;
};
} // namespace qing