feat(core): confs2 合并入 core,内嵌 confs 配置服务 v0.1.0
- 新增 ConfsTask(ITask,与 HttpTask 同构):ConfigStore + INI/JSON 取值 + get/list/reload/health 四路由,协议与 Python confs 逐字节兼容 - 新增 ConfsLogC 异步日志客户端(沿用 confs2 LogC,服务名 confs) - main.cpp 新增 --confs <port> 选项与 CONFS_* 环境变量;不传则行为不变 - Makefile 链接 -lus_msg,版本 0.0.4 → 0.1.0 - _run.sh 启用 --confs 8010;_set.sh 自建 configs → ../confs/configs 链接 - init.txt 移除 confs2 自启动行(由 core 内嵌承担) - 黑盒对比测试 16/16 通过(tests/blackbox/test_core_confs.py)
This commit is contained in:
1 parent
1b282c6f1a
commit
59c5fe1feb
11 files changed
+947
-8
No files matched your search
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* ConfsLogC.hpp — core 内嵌 confs 服务的异步日志客户端
|
||||
*
|
||||
* 基于 libmsg AsyncQueue,异步批量推送日志到 db:8005 的 Logs 表。
|
||||
* - 内存队列 + 后台线程攒批(满 50 条或 2 秒 flush)
|
||||
* - fail-safe:db 不可用时静默丢弃
|
||||
* - API: info() / warning() / error()
|
||||
*
|
||||
* 改编自 confs2/inc/LogC.hpp,仅改 namespace(qing::confs),
|
||||
* 服务名保持 "confs" 与 Python/standalone confs2 日志连续。
|
||||
*/
|
||||
#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 qing {
|
||||
namespace confs {
|
||||
|
||||
struct ConfsLogEntry {
|
||||
std::string ts;
|
||||
std::string service;
|
||||
std::string level;
|
||||
std::string message;
|
||||
std::string trace_id;
|
||||
std::string extra;
|
||||
};
|
||||
|
||||
class ConfsLogC {
|
||||
public:
|
||||
static ConfsLogC& instance() {
|
||||
static ConfsLogC 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:
|
||||
ConfsLogC() {
|
||||
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<ConfsLogEntry>>(
|
||||
[this](std::vector<ConfsLogEntry>&& 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<ConfsLogEntry>&&) {
|
||||
std::cerr << "[core/confs-log] send failed: " << e.what() << std::endl;
|
||||
});
|
||||
queue_->start();
|
||||
}
|
||||
|
||||
~ConfsLogC() { 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);
|
||||
|
||||
ConfsLogEntry 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<ConfsLogEntry>&& 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 << "[core/confs-log] batch drop: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string db_host_;
|
||||
int db_port_;
|
||||
std::unique_ptr<pmc::net::AsyncQueue<ConfsLogEntry>> queue_;
|
||||
};
|
||||
|
||||
} // namespace confs
|
||||
} // namespace qing
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <us/Thread.hpp>
|
||||
#include <us/ITask.hpp>
|
||||
#include <us/RpcServer.hpp>
|
||||
|
||||
namespace qing {
|
||||
|
||||
/* 配置仓库前置声明(实现位于 ConfsTask.cpp,保持头文件干净) */
|
||||
class ConfigStore;
|
||||
|
||||
/* ----------------
|
||||
* 【内嵌 confs 配置服务任务】
|
||||
* ----------------
|
||||
* 将 confs2(C++20 统一配置服务,pymsg RpcServer)内嵌为 core 的一个 ITask,
|
||||
* 与 HttpTask / Tttask 并列,由 taskPool 统一管理生命周期。
|
||||
*
|
||||
* - 协议与 Python confs / standalone confs2 字节级兼容,消费方零改动
|
||||
* - 仅监听 127.0.0.1,路由 get/list/reload/health 不变
|
||||
* - 不传 --confs 参数时 core 不加载本任务,行为与原版完全一致
|
||||
*/
|
||||
class ConfsTask: public ITask {
|
||||
public:
|
||||
|
||||
/* 构造内嵌配置服务任务
|
||||
*
|
||||
* addr: 监听地址(默认 127.0.0.1)
|
||||
* port: 监听端口(默认 8010)
|
||||
* workers: RpcServer 工作线程数(默认 4)
|
||||
* config_dir: 配置文件目录(默认 ./configs) */
|
||||
ConfsTask(std::string addr, int port, int workers, std::string config_dir);
|
||||
|
||||
/* 析构需定义于 .cpp(unique_ptr 持有不完整类型 ConfigStore) */
|
||||
~ConfsTask();
|
||||
|
||||
/* 任务是否在运行中 */
|
||||
bool isRunning() override;
|
||||
|
||||
/* 启动任务(激活线程并等待 RpcServer 就绪) */
|
||||
void start() override;
|
||||
|
||||
/* 终止任务(停止 RpcServer 并等待线程关闭) */
|
||||
void stop() override;
|
||||
|
||||
private:
|
||||
std::string addr; /* 监听地址 */
|
||||
int port; /* 监听端口 */
|
||||
int workers; /* 工作线程数 */
|
||||
std::unique_ptr<ConfigStore> store; /* 配置仓库 */
|
||||
std::unique_ptr<Thread> th; /* 可控线程指针 */
|
||||
std::unique_ptr<pmc::net::RpcServer> server; /* RPC 服务器指针 */
|
||||
std::chrono::steady_clock::time_point started_at; /* 启动时间(health uptime) */
|
||||
|
||||
/* 初始化线程,定义线程的行为(RpcServer) */
|
||||
void init_thread();
|
||||
|
||||
/* 注册 pymsg 路由(get/list/reload/health) */
|
||||
void register_routes(pmc::net::RpcServer& rpc);
|
||||
};
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user