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): 保持现状,自成体系
This commit is contained in:
1 parent
01ae877673
commit
aa3741b861
32 files changed
+608
-2129
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
CXX := g++
|
||||
CXXFLAGS := -std=c++20 -Wall -Wextra -O2
|
||||
INCS := -I./inc
|
||||
LIBS := -lus_msg -lboost_json -lboost_program_options -lpthread
|
||||
LIBS := -lus_msg -lus_logc -lboost_json -lboost_program_options -lpthread
|
||||
|
||||
TARGET := confs2.bin
|
||||
SRC := src/main.cpp
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* LogC.hpp — confs2 异步日志客户端
|
||||
*
|
||||
* 基于 libmsg AsyncQueue,异步批量推送日志到 db:8005 的 Logs 表。
|
||||
* - 内存队列 + 后台线程攒批(满 50 条或 2 秒 flush)
|
||||
* - fail-safe:db 不可用时静默丢弃
|
||||
* - API: info() / warning() / error()
|
||||
*
|
||||
* 改编自 captcha2/inc/LogC.hpp,仅改 namespace 和服务名。
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <us/Msg.hpp>
|
||||
#include <us/AsyncQueue.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace confs2 {
|
||||
|
||||
struct LogEntry {
|
||||
std::string ts;
|
||||
std::string service;
|
||||
std::string level;
|
||||
std::string message;
|
||||
std::string trace_id;
|
||||
std::string extra;
|
||||
};
|
||||
|
||||
class LogC {
|
||||
public:
|
||||
static LogC& instance() {
|
||||
static LogC inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
void info(const std::string& svc, const std::string& msg) {
|
||||
push(svc, "INFO", msg);
|
||||
}
|
||||
void warning(const std::string& svc, const std::string& msg) {
|
||||
push(svc, "WARNING", msg);
|
||||
}
|
||||
void error(const std::string& svc, const std::string& msg) {
|
||||
push(svc, "ERROR", msg);
|
||||
}
|
||||
|
||||
private:
|
||||
LogC() {
|
||||
const char* host = std::getenv("DB_HOST");
|
||||
const char* port = std::getenv("DB_PORT");
|
||||
db_host_ = host ? host : "127.0.0.1";
|
||||
db_port_ = port ? std::atoi(port) : 8005;
|
||||
|
||||
queue_ = std::make_unique<pmc::net::AsyncQueue<LogEntry>>(
|
||||
[this](std::vector<LogEntry>&& batch) { send_batch(std::move(batch)); },
|
||||
pmc::net::AsyncQueueConfig{.batch_size = 50, .flush_interval = 2.0, .max_queue_size = 10000},
|
||||
[](const std::exception& e, std::vector<LogEntry>&&) {
|
||||
std::cerr << "[confs2/log] send failed: " << e.what() << std::endl;
|
||||
});
|
||||
queue_->start();
|
||||
}
|
||||
|
||||
~LogC() { if (queue_) queue_->shutdown(); }
|
||||
|
||||
// P2-3 修复:过滤控制字符,防止日志注入
|
||||
static std::string sanitize_for_log(std::string s) {
|
||||
s.erase(std::remove_if(s.begin(), s.end(), [](unsigned char c) {
|
||||
return c < 0x20 && c != '\t';
|
||||
}), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
void push(const std::string& svc, const std::string& level, const std::string& msg) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto t = std::chrono::system_clock::to_time_t(now);
|
||||
// 系统本地时间 + 时区偏移
|
||||
std::tm* lt = std::localtime(&t);
|
||||
char buf[40];
|
||||
std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", lt);
|
||||
char tz_raw[8];
|
||||
std::strftime(tz_raw, sizeof(tz_raw), "%z", lt);
|
||||
std::string tz = tz_raw;
|
||||
if (tz.length() >= 5)
|
||||
tz = tz.substr(0, 3) + ":" + tz.substr(3);
|
||||
|
||||
LogEntry e;
|
||||
e.ts = std::string(buf) + tz;
|
||||
e.service = sanitize_for_log(svc);
|
||||
e.level = level;
|
||||
e.message = sanitize_for_log(msg);
|
||||
queue_->push(std::move(e));
|
||||
}
|
||||
|
||||
void send_batch(std::vector<LogEntry>&& batch) {
|
||||
try {
|
||||
pmc::net::MsgClient client(db_host_, db_port_, "confs-log");
|
||||
for (auto& e : batch) {
|
||||
boost::json::object vals;
|
||||
vals["ts"] = e.ts;
|
||||
vals["service"] = e.service;
|
||||
vals["level"] = e.level;
|
||||
vals["message"] = e.message;
|
||||
vals["trace_id"] = e.trace_id.empty() ? nullptr : boost::json::value(e.trace_id);
|
||||
vals["extra"] = e.extra.empty() ? nullptr : boost::json::value(e.extra);
|
||||
|
||||
boost::json::object req;
|
||||
req["_route"] = "execute";
|
||||
req["action"] = "insert";
|
||||
req["table"] = "Logs";
|
||||
req["values"] = vals;
|
||||
|
||||
client.call(boost::json::serialize(req), 1.0);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[confs2/log] batch drop: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string db_host_;
|
||||
int db_port_;
|
||||
std::unique_ptr<pmc::net::AsyncQueue<LogEntry>> queue_;
|
||||
};
|
||||
|
||||
} // namespace confs2
|
||||
+3
-2
@@ -31,13 +31,13 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "LogC.hpp"
|
||||
#include <us/LogC.hpp>
|
||||
|
||||
namespace json = boost::json;
|
||||
namespace po = boost::program_options;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using confs2::LogC;
|
||||
using qing::LogC;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// 1. 常量与异常
|
||||
@@ -411,6 +411,7 @@ static void validate_config(const ConfsConfig& cfg) {
|
||||
static ConfsConfig g_config;
|
||||
static ConfigStore* g_store = nullptr;
|
||||
static auto& g_log = LogC::instance();
|
||||
static const auto _log_init = (LogC::instance().set_service_name("confs2-log"), true);
|
||||
static const auto g_start_time = std::chrono::steady_clock::now();
|
||||
|
||||
static std::int64_t uptime_seconds() {
|
||||
|
||||
Reference in new issue
Block a user