Compare commits
7
Commits
01e67648e0
...
9fc3542771
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fc3542771 | ||
|
|
d0ee948339 | ||
|
|
29eac70f24 | ||
|
|
8eb14cad4a | ||
|
|
4df510712e | ||
|
|
4275f002e0 | ||
|
|
01c6d0cb1f |
No files matched your search
@@ -165,9 +165,39 @@ def rag_list():
|
||||
return jsonify({"total": len(entries), "entries": entries}), 200
|
||||
|
||||
|
||||
def _parse_args():
|
||||
"""解析命令行参数(标准化:CLI > ENV > 默认值)。"""
|
||||
import argparse
|
||||
p = argparse.ArgumentParser(
|
||||
prog="ai_review",
|
||||
description="AI Review Service — 基于 DeepSeek + RAG 的内容审核微服务",
|
||||
)
|
||||
p.add_argument("--host", default=None,
|
||||
help="绑定地址(默认读取 $AI_REVIEW_HOST,否则 127.0.0.1)")
|
||||
p.add_argument("--port", type=int, default=None,
|
||||
help="监听端口(默认读取 $AI_REVIEW_PORT,否则 8007)")
|
||||
p.add_argument("--log-level", default="info",
|
||||
choices=["debug", "info", "warn", "error"],
|
||||
help="日志级别(默认 info)")
|
||||
p.add_argument("--instance", default="ai_review",
|
||||
help="实例名(注册中心用,默认 ai_review)")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
# ── 配置默认值(ENV 覆盖,CLI 最高优先级)───────────────────────────
|
||||
ADDR = os.getenv("AI_REVIEW_HOST", "127.0.0.1")
|
||||
PORT = int(os.getenv("AI_REVIEW_PORT", "8007"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = _parse_args()
|
||||
|
||||
# CLI 覆盖(最高优先级)
|
||||
if args.host is not None:
|
||||
ADDR = args.host
|
||||
if args.port is not None:
|
||||
PORT = args.port
|
||||
|
||||
print(f'AI Review Service listening at http://{ADDR}:{PORT}')
|
||||
print(f'[ai_review] instance={args.instance} log-level={args.log_level}')
|
||||
print(f'[ai_review] 配置来源: CLI > ENV > 默认值')
|
||||
serve(app, host=ADDR, port=PORT)
|
||||
@@ -18,6 +18,9 @@
|
||||
*
|
||||
* // 进程退出前:
|
||||
* qing::LogC::instance().shutdown(); // 停止后台线程并 flush 剩余日志(幂等)
|
||||
*
|
||||
* // 设置日志级别(main 启动时调用,低于此级别的日志直接丢弃)
|
||||
* qing::LogC::instance().set_level("warn"); // 只输出 warn/error
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
@@ -27,6 +30,7 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
@@ -48,6 +52,17 @@ public:
|
||||
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;
|
||||
@@ -55,11 +70,24 @@ public:
|
||||
|
||||
// ---- 公开接口 --------------------------------------------------------
|
||||
|
||||
// 设置日志级别阈值(debug|info|warn|error,大小写不限)
|
||||
// 低于此级别的日志在 push 时直接丢弃
|
||||
void set_level(const std::string& level) {
|
||||
auto it = level_order().find(level);
|
||||
if (it != level_order().end()) {
|
||||
_level_threshold.store(it->second, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
// 推送一条日志(非阻塞,立即返回)
|
||||
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) {
|
||||
// 日志级别过滤:低于阈值直接丢弃
|
||||
auto it = level_order().find(level);
|
||||
int lvl = (it != level_order().end()) ? it->second : 1;
|
||||
if (lvl < _level_threshold.load(std::memory_order_relaxed)) return;
|
||||
_queue.push(Entry{service, level, message,
|
||||
std::move(trace_id), std::move(extra)});
|
||||
}
|
||||
@@ -193,6 +221,7 @@ private:
|
||||
std::string _db_addr = "127.0.0.1";
|
||||
int _db_port = 8005;
|
||||
std::mutex _endpoint_mtx; // 保护 _db_addr/_db_port 读写(set_endpoint 与后台线程并发)
|
||||
std::atomic<int> _level_threshold{1}; // 日志级别阈值,默认 INFO(1)
|
||||
|
||||
static constexpr size_t _BATCH_SIZE = 50; // 队列满 50 条时 flush
|
||||
static constexpr double _FLUSH_INTERVAL = 2.0; // 超时 2 秒强制 flush
|
||||
|
||||
+44
-5
@@ -406,6 +406,9 @@ static std::unordered_map<std::string, std::string> _WS_PROXY_ROUTES;
|
||||
/* instance items */
|
||||
static std::unique_ptr<HttpServer> _SERVE;
|
||||
static std::unique_ptr<MyRSA::Generator> _KEYGEN;
|
||||
/* CLI 标准化参数 */
|
||||
static std::string _INSTANCE = "ca"; // --instance,注册中心用
|
||||
static std::string _LOG_LEVEL = "info"; // --log-level,debug|info|warn|error
|
||||
//static std::unique_ptr<Sqlite3> _SQLDB;
|
||||
static RateLimiter _LOGIN_RATE;
|
||||
static RateLimiter _REGISTER_RATE;
|
||||
@@ -769,6 +772,11 @@ int main(int argc, char **argv) {
|
||||
desc.add_options()
|
||||
("help,h", "output help message")
|
||||
("version,v", "output version message")
|
||||
("host", po::value<std::string>(), "绑定地址(默认 127.0.0.1),覆盖 [Server] address")
|
||||
("port", po::value<int>(), "监听端口(默认 8443),覆盖 [Server] port")
|
||||
("config", po::value<std::string>(), "confs 拉取的配置名(默认 ca.ini)")
|
||||
("log-level", po::value<std::string>(), "日志级别 debug|info|warn|error(默认 info)")
|
||||
("instance", po::value<std::string>(), "实例名(注册中心用,默认 ca)")
|
||||
;
|
||||
|
||||
|
||||
@@ -815,15 +823,21 @@ int main(int argc, char **argv) {
|
||||
_CONFS_HOST = ConfIni::env("CONFS_HOST", "127.0.0.1");
|
||||
_CONFS_PORT = ConfIni::env_int("CONFS_PORT", 8010);
|
||||
|
||||
/* --config 覆盖 confs 拉取的配置名(在 confs 拉取前读取) */
|
||||
std::string confs_config_name = "ca.ini";
|
||||
if (vm.count("config")) {
|
||||
confs_config_name = vm["config"].as<std::string>();
|
||||
}
|
||||
|
||||
ConfIni cf;
|
||||
try {
|
||||
/* 从 confs 拉取 ca.ini */
|
||||
/* 从 confs 拉取配置 */
|
||||
std::cout << "[ca] 从 confs (" << _CONFS_HOST << ":" << _CONFS_PORT
|
||||
<< ") 拉取 ca.ini ..." << std::endl;
|
||||
<< ") 拉取 " << confs_config_name << " ..." << std::endl;
|
||||
pmc::net::MsgClient confs_client(_CONFS_HOST, _CONFS_PORT, "ca");
|
||||
json::object req;
|
||||
req["_route"] = "get";
|
||||
req["name"] = "ca.ini";
|
||||
req["name"] = confs_config_name;
|
||||
std::string resp_str = confs_client.call(
|
||||
json::serialize(req), /*timeout=*/5.0,
|
||||
/*retry_on_refused=*/true);
|
||||
@@ -917,7 +931,28 @@ int main(int argc, char **argv) {
|
||||
int env_smtp_port = ConfIni::env_int("SMTP_PORT", -1);
|
||||
if (env_smtp_port > 0) _SMTP_PORT = env_smtp_port;
|
||||
|
||||
/* 4. 配置完整性校验 */
|
||||
/* 4. CLI 参数覆盖(最高优先级) */
|
||||
if (vm.count("host")) _ADDR = vm["host"].as<std::string>();
|
||||
if (vm.count("port")) _PORT = vm["port"].as<int>();
|
||||
if (vm.count("instance")) _INSTANCE = vm["instance"].as<std::string>();
|
||||
if (vm.count("log-level")) {
|
||||
_LOG_LEVEL = vm["log-level"].as<std::string>();
|
||||
// 归一化 warning → warn
|
||||
if (_LOG_LEVEL == "warning") _LOG_LEVEL = "warn";
|
||||
// 合法性校验
|
||||
static const char* valid[] = {"debug", "info", "warn", "error"};
|
||||
bool ok = false;
|
||||
for (const char* v : valid) {
|
||||
if (_LOG_LEVEL == v) { ok = true; break; }
|
||||
}
|
||||
if (!ok) {
|
||||
std::cerr << "[ca] 致命错误: 非法 --log-level: " << _LOG_LEVEL
|
||||
<< "(合法值: debug|info|warn|error)" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5. 配置完整性校验 */
|
||||
if (_ADDR.empty() || _PORT == -1) {
|
||||
std::cerr << "[ca] 致命错误: 配置项 [Server] address/port 缺失"
|
||||
"(ini 中未配置且无 LISTEN_ADDR/LISTEN_PORT 环境变量覆盖)" << std::endl;
|
||||
@@ -937,8 +972,12 @@ int main(int argc, char **argv) {
|
||||
DbC::init(_DB_HOST, _DB_PORT);
|
||||
logs().set_endpoint(_DB_HOST, _DB_PORT);
|
||||
|
||||
/* 设置日志级别过滤 */
|
||||
logs().set_level(_LOG_LEVEL);
|
||||
|
||||
std::cout << "[ca] 监听: " << _ADDR << ":" << _PORT
|
||||
<< " (TLS: " << (_TLS ? "on" : "off") << ")" << std::endl;
|
||||
std::cout << "[ca] 实例: " << _INSTANCE << " 日志级别: " << _LOG_LEVEL << std::endl;
|
||||
std::cout << "[ca] 下游服务: db=" << _DB_HOST << ":" << _DB_PORT
|
||||
<< ", captcha=" << _CAPTCHA_HOST << ":" << _CAPTCHA_PORT
|
||||
<< ", smtp=" << _SMTP_HOST << ":" << _SMTP_PORT << std::endl;
|
||||
@@ -984,7 +1023,7 @@ int main(int argc, char **argv) {
|
||||
// 等待服务器启动完成(简单延迟,确保started日志打印后再输出提示)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
std::cout << "CA " << _VER << " started" << std::endl;
|
||||
logs().info("ca", std::string("CA ") + _VER + " started");
|
||||
logs().info("ca", std::string("CA ") + _VER + " started (instance=" + _INSTANCE + ")");
|
||||
|
||||
/* 主线程:等待关闭信号,带超时轮询 */
|
||||
while (!_stop_requested && !server_exited) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <atomic>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace captcha2 {
|
||||
|
||||
@@ -30,11 +32,31 @@ struct LogEntry {
|
||||
|
||||
class LogC {
|
||||
public:
|
||||
// 日志级别 → 数值(大写兼容)
|
||||
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;
|
||||
}
|
||||
|
||||
// 设置日志级别阈值(debug|info|warn|error,大小写不限)
|
||||
// 低于此级别的日志在 push 时直接丢弃
|
||||
void set_level(const std::string& level) {
|
||||
auto it = level_order().find(level);
|
||||
if (it != level_order().end()) {
|
||||
level_threshold_.store(it->second, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void info(const std::string& svc, const std::string& msg) {
|
||||
push(svc, "INFO", msg);
|
||||
}
|
||||
@@ -64,6 +86,11 @@ private:
|
||||
~LogC() { if (queue_) queue_->shutdown(); }
|
||||
|
||||
void push(const std::string& svc, const std::string& level, const std::string& msg) {
|
||||
// 日志级别过滤:低于阈值直接丢弃
|
||||
auto it = level_order().find(level);
|
||||
int lvl = (it != level_order().end()) ? it->second : 1;
|
||||
if (lvl < level_threshold_.load(std::memory_order_relaxed)) return;
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto t = std::chrono::system_clock::to_time_t(now);
|
||||
char buf[32];
|
||||
@@ -105,6 +132,7 @@ private:
|
||||
|
||||
std::string db_host_;
|
||||
int db_port_;
|
||||
std::atomic<int> level_threshold_{1}; // 日志级别阈值,默认 INFO(1)
|
||||
std::unique_ptr<pmc::net::AsyncQueue<LogEntry>> queue_;
|
||||
};
|
||||
|
||||
|
||||
+25
-7
@@ -70,12 +70,13 @@ struct CaptchaConfig {
|
||||
int rate_limit_per_min = 600; // P1-1: 全局限流仅作最后防线,主要限流由 gateway 按 IP 负责
|
||||
};
|
||||
|
||||
// 从 confs 拉取 captcha.ini 全文,阻塞等待 confs 就绪
|
||||
static std::string fetch_config_from_confs(const std::string& confs_host, int confs_port) {
|
||||
// 从 confs 拉取指定名称的配置文件全文,阻塞等待 confs 就绪
|
||||
static std::string fetch_config_from_confs(const std::string& confs_host, int confs_port,
|
||||
const std::string& config_name) {
|
||||
pmc::net::RpcClient client(confs_host, confs_port, "captcha2",
|
||||
/*timeout=*/5.0, /*retry_on_refused=*/true);
|
||||
try {
|
||||
auto data = client.call("get", {{"name", "captcha.ini"}});
|
||||
auto data = client.call("get", {{"name", config_name}});
|
||||
auto* content = data.as_object().if_contains("content");
|
||||
if (!content || !content->is_string()) {
|
||||
std::cerr << "[captcha2] confs 返回的 captcha.ini 无 content 字段" << std::endl;
|
||||
@@ -531,6 +532,9 @@ int main(int argc, char* argv[]) {
|
||||
// ── 命令行参数 ──
|
||||
std::string confs_host = "127.0.0.1";
|
||||
int confs_port = 8010;
|
||||
std::string confs_config_name = "captcha.ini"; // --config 覆盖
|
||||
std::string instance = "captcha2"; // --instance 覆盖
|
||||
std::string log_level = "info"; // --log-level 覆盖
|
||||
{
|
||||
const char* env;
|
||||
if ((env = std::getenv("CONFS_HOST"))) confs_host = env;
|
||||
@@ -540,9 +544,13 @@ int main(int argc, char* argv[]) {
|
||||
po::options_description desc("captcha2 — pymsg 验证码服务");
|
||||
desc.add_options()
|
||||
("help,h", "显示帮助")
|
||||
("addr,a", po::value<std::string>(), "监听地址(覆盖 confs)")
|
||||
("addr,a", po::value<std::string>(), "监听地址(覆盖 confs,别名 --host)")
|
||||
("host", po::value<std::string>(), "监听地址(覆盖 confs,别名 --addr/-a)")
|
||||
("port,p", po::value<int>(), "监听端口(覆盖 confs)")
|
||||
("workers,w", po::value<int>(), "工作线程数(覆盖 confs)");
|
||||
("workers,w", po::value<int>(), "工作线程数(覆盖 confs)")
|
||||
("config", po::value<std::string>(), "confs 拉取的配置名(默认 captcha.ini)")
|
||||
("log-level", po::value<std::string>(), "日志级别 debug|info|warn|error,默认 info")
|
||||
("instance", po::value<std::string>(), "实例名(注册中心用,默认 captcha2)");
|
||||
po::variables_map vm;
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
po::notify(vm);
|
||||
@@ -552,20 +560,30 @@ int main(int argc, char* argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 预扫描 --config / --instance / --log-level / --host(confs 拉取前处理)
|
||||
if (vm.count("config")) confs_config_name = vm["config"].as<std::string>();
|
||||
if (vm.count("instance")) instance = vm["instance"].as<std::string>();
|
||||
if (vm.count("log-level")) log_level = vm["log-level"].as<std::string>();
|
||||
|
||||
// ── 配置加载:confs → env → 命令行 ──
|
||||
std::string ini_text = fetch_config_from_confs(confs_host, confs_port);
|
||||
std::string ini_text = fetch_config_from_confs(confs_host, confs_port, confs_config_name);
|
||||
merge_ini(g_config, ini_text);
|
||||
|
||||
apply_env_overrides(g_config);
|
||||
|
||||
if (vm.count("addr")) g_config.host = vm["addr"].as<std::string>();
|
||||
if (vm.count("host")) g_config.host = vm["host"].as<std::string>();
|
||||
if (vm.count("port")) g_config.port = vm["port"].as<int>();
|
||||
if (vm.count("workers")) g_config.workers = vm["workers"].as<int>();
|
||||
|
||||
validate_config(g_config);
|
||||
|
||||
std::cerr << "[captcha2] 配置来源: confs captcha.ini @ "
|
||||
g_log.set_level(log_level);
|
||||
|
||||
std::cerr << "[captcha2] 配置来源: confs " << confs_config_name << " @ "
|
||||
<< confs_host << ":" << confs_port << std::endl;
|
||||
std::cerr << "[captcha2] instance=" << instance
|
||||
<< " log-level=" << log_level << std::endl;
|
||||
std::cerr << "[captcha2] captcha_len=" << g_config.len
|
||||
<< " ttl=" << g_config.ttl
|
||||
<< " max_store=" << g_config.max_store
|
||||
|
||||
+7
-7
@@ -21,7 +21,7 @@ services:
|
||||
# --- AI 审核 ---
|
||||
- name: ai_review
|
||||
cmd: bash /opt/micro_servers/ai_review/_run.sh
|
||||
args: []
|
||||
args: ["--instance", "ai_review", "--log-level", "info"]
|
||||
depends_on: [db2]
|
||||
sleep_before: 2
|
||||
|
||||
@@ -57,7 +57,7 @@ services:
|
||||
# --- 邮件服务 ---
|
||||
- name: smtp
|
||||
cmd: bash /opt/micro_servers/smtp/_run.sh
|
||||
args: []
|
||||
args: ["--instance", "smtp", "--log-level", "info"]
|
||||
depends_on: []
|
||||
sleep_before: 2
|
||||
|
||||
@@ -71,26 +71,26 @@ services:
|
||||
# --- 安全链路 ---
|
||||
- name: waf
|
||||
cmd: bash /opt/micro_servers/waf/_run.sh
|
||||
args: [] # -c config.yaml 保留在 _run.sh 作为默认值
|
||||
args: ["--instance", "waf", "--log-level", "info"]
|
||||
depends_on: [ca, gateway]
|
||||
sleep_before: 0
|
||||
|
||||
- name: ca
|
||||
cmd: bash /opt/micro_servers/ca/_run.sh
|
||||
args: []
|
||||
args: ["--instance", "ca", "--log-level", "info"]
|
||||
depends_on: [db2]
|
||||
sleep_before: 2
|
||||
|
||||
- name: gateway
|
||||
cmd: bash /opt/micro_servers/gateway/_run.sh
|
||||
args: []
|
||||
args: ["--instance", "gateway", "--log-level", "info"]
|
||||
depends_on: [db2]
|
||||
sleep_before: 2
|
||||
|
||||
# --- 验证码 ---
|
||||
- name: captcha2
|
||||
cmd: bash /opt/micro_servers/captcha2/_run.sh
|
||||
args: []
|
||||
args: ["--instance", "captcha2", "--log-level", "info"]
|
||||
depends_on: []
|
||||
sleep_before: 1
|
||||
|
||||
@@ -104,6 +104,6 @@ services:
|
||||
# --- 端口重定向(最后启动) ---
|
||||
- name: redirect80
|
||||
cmd: bash /opt/micro_servers/redirect80/_run.sh
|
||||
args: []
|
||||
args: ["--instance", "redirect80", "--log-level", "info"]
|
||||
depends_on: [waf]
|
||||
sleep_before: 0
|
||||
@@ -18,6 +18,9 @@
|
||||
*
|
||||
* // 进程退出前:
|
||||
* qing::LogC::instance().shutdown(); // 停止后台线程并 flush 剩余日志(幂等)
|
||||
*
|
||||
* // 设置日志级别(main 启动时调用,低于此级别的日志直接丢弃)
|
||||
* qing::LogC::instance().set_level("warn"); // 只输出 warn/error
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
@@ -27,6 +30,7 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
@@ -55,11 +59,35 @@ public:
|
||||
|
||||
// ---- 公开接口 --------------------------------------------------------
|
||||
|
||||
// 日志级别 → 数值(大写兼容)
|
||||
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;
|
||||
}
|
||||
|
||||
// 设置日志级别阈值(debug|info|warn|error,大小写不限)
|
||||
// 低于此级别的日志在 push 时直接丢弃
|
||||
void set_level(const std::string& level) {
|
||||
auto it = level_order().find(level);
|
||||
if (it != level_order().end()) {
|
||||
_level_threshold.store(it->second, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
// 推送一条日志(非阻塞,立即返回)
|
||||
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) {
|
||||
// 日志级别过滤:低于阈值直接丢弃
|
||||
auto it = level_order().find(level);
|
||||
int lvl = (it != level_order().end()) ? it->second : 1;
|
||||
if (lvl < _level_threshold.load(std::memory_order_relaxed)) return;
|
||||
_queue.push(Entry{service, level, message,
|
||||
std::move(trace_id), std::move(extra)});
|
||||
}
|
||||
@@ -193,6 +221,7 @@ private:
|
||||
std::string _db_addr = "127.0.0.1";
|
||||
int _db_port = 8005;
|
||||
std::mutex _endpoint_mtx; // 保护 _db_addr/_db_port 读写(set_endpoint 与后台线程并发)
|
||||
std::atomic<int> _level_threshold{1}; // 日志级别阈值,默认 INFO(1)
|
||||
|
||||
static constexpr size_t _BATCH_SIZE = 50; // 队列满 50 条时 flush
|
||||
static constexpr double _FLUSH_INTERVAL = 2.0; // 超时 2 秒强制 flush
|
||||
|
||||
+29
-2
@@ -109,6 +109,11 @@ static int _DB_PORT = 8005;
|
||||
/* confs 引导配置(不依赖 confs 本身,环境变量 CONFS_HOST/CONFS_PORT 覆盖,默认 127.0.0.1:8010) */
|
||||
static std::string _CONFS_HOST = "127.0.0.1";
|
||||
static int _CONFS_PORT = 8010;
|
||||
/* confs 配置名(--config 覆盖,默认 gateway.ini) */
|
||||
static std::string _CONFS_CONFIG_NAME = "gateway.ini";
|
||||
/* CLI 标准化参数(注册中心 / 日志级别) */
|
||||
static std::string _INSTANCE = "gateway";
|
||||
static std::string _LOG_LEVEL = "info";
|
||||
|
||||
/* instance items */
|
||||
static std::unique_ptr<HttpServer> _SERVE;
|
||||
@@ -164,6 +169,11 @@ int main(int argc, char **argv) {
|
||||
desc.add_options()
|
||||
("help,h", "output help message")
|
||||
("version,v", "output version message")
|
||||
("host", po::value<std::string>(), "绑定地址(覆盖 [Server] address,默认 127.0.0.1)")
|
||||
("port", po::value<int>(), "监听端口(覆盖 [Server] port,默认 8445)")
|
||||
("config", po::value<std::string>(), "confs 拉取的配置名(默认 gateway.ini)")
|
||||
("log-level", po::value<std::string>(), "日志级别 debug|info|warn|error,默认 info")
|
||||
("instance", po::value<std::string>(), "实例名(注册中心用,默认 gateway)")
|
||||
;
|
||||
|
||||
|
||||
@@ -195,6 +205,10 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CLI 预扫描:--config 必须在 confs 拉取前处理(其余 CLI 覆盖在 ENV 之后接入) */
|
||||
if (vm.count("config"))
|
||||
_CONFS_CONFIG_NAME = vm["config"].as<std::string>();
|
||||
|
||||
/* ==================== 配置加载(两层:confs 强依赖 → 环境变量覆盖) ====================
|
||||
* 与 Python 服务(dnfs/db/static)保持一致的加载模式:
|
||||
* 1. 引导配置:confs 地址从 CONFS_HOST/CONFS_PORT 环境变量读取,缺省 127.0.0.1:8010
|
||||
@@ -214,11 +228,11 @@ int main(int argc, char **argv) {
|
||||
try {
|
||||
/* 从 confs 拉取 gateway.ini */
|
||||
std::cout << "[gateway] 从 confs (" << _CONFS_HOST << ":" << _CONFS_PORT
|
||||
<< ") 拉取 gateway.ini ..." << std::endl;
|
||||
<< ") 拉取 " << _CONFS_CONFIG_NAME << " ..." << std::endl;
|
||||
pmc::net::MsgClient confs_client(_CONFS_HOST, _CONFS_PORT, "gateway");
|
||||
json::object req;
|
||||
req["_route"] = "get";
|
||||
req["name"] = "gateway.ini";
|
||||
req["name"] = _CONFS_CONFIG_NAME;
|
||||
std::string resp_str = confs_client.call(
|
||||
json::serialize(req), /*timeout=*/5.0,
|
||||
/*retry_on_refused=*/true);
|
||||
@@ -268,6 +282,16 @@ int main(int argc, char **argv) {
|
||||
int env_db_port = ConfIni::env_int("DB_PORT", -1);
|
||||
if (env_db_port > 0) _DB_PORT = env_db_port;
|
||||
|
||||
/* 3b. CLI 覆盖(最高优先级):--host / --port / --instance / --log-level */
|
||||
if (vm.count("host"))
|
||||
_ADDR = vm["host"].as<std::string>();
|
||||
if (vm.count("port"))
|
||||
_PORT = vm["port"].as<int>();
|
||||
if (vm.count("instance"))
|
||||
_INSTANCE = vm["instance"].as<std::string>();
|
||||
if (vm.count("log-level"))
|
||||
_LOG_LEVEL = vm["log-level"].as<std::string>();
|
||||
|
||||
/* 4. 配置完整性校验 */
|
||||
if (_ADDR.empty() || _PORT == -1) {
|
||||
std::cerr << "[gateway] 致命错误: 配置项 [Server] address/port 缺失"
|
||||
@@ -295,9 +319,12 @@ int main(int argc, char **argv) {
|
||||
|
||||
/* 初始化日志推送端点 */
|
||||
logs().set_endpoint(_DB_HOST, _DB_PORT);
|
||||
logs().set_level(_LOG_LEVEL);
|
||||
|
||||
std::cout << "[gateway] 监听: " << _ADDR << ":" << _PORT
|
||||
<< " (TLS: " << (_TLS ? "on" : "off") << ")" << std::endl;
|
||||
std::cout << "[gateway] instance=" << _INSTANCE
|
||||
<< " log-level=" << _LOG_LEVEL << std::endl;
|
||||
|
||||
// 根据 Server.tls 选择 HTTPS(带证书)或 HTTP(无证书)构造函数
|
||||
// HTTP 模式用于 ca 前置后的内部转发链路
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
- 缓冲批量:队列满 50 条或超时 2 秒时自动 flush
|
||||
- 时间戳:日志入队时即记录 ts(UTC ISO 格式),反映事件真实发生时间
|
||||
- 底层复用 pymsg.AsyncRpcQueue 通用队列基础设施
|
||||
- 日志级别过滤:set_level() 设定阈值,低于阈值的日志直接丢弃
|
||||
|
||||
用法(与旧版完全兼容):
|
||||
from log_client import logs
|
||||
@@ -16,6 +17,10 @@
|
||||
logs.error("redirect80", "端口绑定失败", extra={"errno": 98})
|
||||
logs.push("redirect80", "WARNING", "confs 不可用,使用本地配置",
|
||||
trace_id="startup", extra={"fallback": "redirect80.ini"})
|
||||
|
||||
# 设置日志级别(main 启动时调用)
|
||||
from log_client import set_log_level
|
||||
set_log_level("warn") # 只输出 warn/error
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -37,6 +42,19 @@ _FLUSH_INTERVAL = 2.0
|
||||
_TIMEOUT = 5
|
||||
_QUEUE_MAXSIZE = 10000
|
||||
|
||||
# ── 日志级别过滤 ────────────────────────────────────────────────────
|
||||
_LEVEL_ORDER = {"DEBUG": 0, "INFO": 1, "WARNING": 2, "WARN": 2, "ERROR": 3, "CRITICAL": 3}
|
||||
_level_threshold = 1 # 默认 INFO
|
||||
|
||||
|
||||
def set_log_level(level: str):
|
||||
"""设置全局日志级别阈值。低于此级别的日志直接丢弃。"""
|
||||
global _level_threshold
|
||||
lv = level.upper()
|
||||
if lv == "WARNING":
|
||||
lv = "WARN"
|
||||
_level_threshold = _LEVEL_ORDER.get(lv, 1)
|
||||
|
||||
|
||||
def _safe_json(obj):
|
||||
"""安全序列化 extra 字段,不可序列化对象转为 repr 字符串。"""
|
||||
@@ -79,8 +97,11 @@ class LogClient:
|
||||
trace_id: str = None, extra=None):
|
||||
"""推送一条日志(非阻塞,立即返回)。
|
||||
|
||||
低于设定级别的日志直接丢弃。
|
||||
队列满时静默丢弃最旧条目再放入新条目,保证不阻塞业务线程、不导致 OOM。
|
||||
"""
|
||||
if _LEVEL_ORDER.get(level.upper(), 1) < _level_threshold:
|
||||
return
|
||||
entry = {
|
||||
'ts': _now_iso(),
|
||||
'service': service,
|
||||
|
||||
+128
-52
@@ -7,12 +7,14 @@ redirect80 — HTTP 80 端口重定向服务。
|
||||
技术选型:Python 内置 http.server + 多线程混合模式,无外部 Web 框架依赖。
|
||||
日志通过 log_client 异步推送到 db 的 Logs 表(fail-safe)。
|
||||
|
||||
配置(两层加载):
|
||||
配置(三层加载):
|
||||
1. confs 强依赖拉取 redirect80.ini(retry_on_refused 阻塞等待)
|
||||
2. 环境变量覆盖(Docker/k8s 兼容)
|
||||
confs 不可用时直接退出,不做本地回退。
|
||||
3. 命令行参数(最高优先级)
|
||||
配置不可用时直接退出,不做本地回退。
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import os
|
||||
import sys as _sys
|
||||
@@ -22,57 +24,124 @@ from socketserver import ThreadingMixIn
|
||||
# ── 路径设置 ──────────────────────────────────────────────────────────
|
||||
_here = os.path.dirname(os.path.abspath(__file__))
|
||||
_sys.path.insert(0, os.path.join(_here, ".."))
|
||||
from log_client import logs # noqa: E402
|
||||
from log_client import logs, set_log_level # noqa: E402
|
||||
|
||||
# ── 配置加载(confs 强依赖 → 环境变量覆盖)────────────────────────
|
||||
_config = {
|
||||
"host": "0.0.0.0",
|
||||
"port": 80,
|
||||
"redirect_port": 443,
|
||||
"redirect_code": 301,
|
||||
}
|
||||
|
||||
# 1. confs 强依赖拉取配置(阻塞等待 confs 就绪,失败则退出)
|
||||
_sys.path.insert(0, os.path.join(_here, "..", "pymsg"))
|
||||
from pymsg import RpcClient # noqa: E402
|
||||
|
||||
_confs_host = os.environ.get("CONFS_HOST", "127.0.0.1")
|
||||
_confs_port = int(os.environ.get("CONFS_PORT", "8010"))
|
||||
_confs_client = RpcClient(
|
||||
_confs_host, _confs_port, "redirect80",
|
||||
retry_on_refused=True, timeout=5,
|
||||
)
|
||||
_data = _confs_client.call("get", {"name": "redirect80.ini"})
|
||||
_ini_text = _data.get("content", "")
|
||||
_ini = configparser.ConfigParser(interpolation=None)
|
||||
_ini.read_string(_ini_text)
|
||||
|
||||
_merge_map = {
|
||||
("server", "host"): "host",
|
||||
("server", "port"): "port",
|
||||
("server", "redirect_port"): "redirect_port",
|
||||
("server", "redirect_code"): "redirect_code",
|
||||
}
|
||||
for (sec, key), var in _merge_map.items():
|
||||
if _ini.has_option(sec, key):
|
||||
val = _ini.get(sec, key)
|
||||
_config[var] = type(_config[var])(val)
|
||||
logs.info("redirect80", "配置已从 confs 加载")
|
||||
# ── 配置加载 ─────────────────────────────────────────────────────────
|
||||
|
||||
def load_config(argv):
|
||||
"""三层加载: confs → ENV → CLI(最高优先级)。"""
|
||||
|
||||
# argparse 先行:--help 在任何网络调用前处理
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="redirect80",
|
||||
description="HTTP 80 端口重定向服务",
|
||||
add_help=False,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="配置优先级: CLI > 环境变量 > confs > 默认值",
|
||||
)
|
||||
parser.add_argument("--host", default=None,
|
||||
help="绑定地址,默认 0.0.0.0")
|
||||
parser.add_argument("--port", type=int, default=None,
|
||||
help="监听端口,默认 80")
|
||||
parser.add_argument("--redirect-port", type=int, default=None,
|
||||
help="重定向目标端口,默认 443")
|
||||
parser.add_argument("--redirect-code", type=int, default=None,
|
||||
help="GET/HEAD 重定向状态码,默认 301")
|
||||
parser.add_argument("--log-level", default=None,
|
||||
help="debug|info|warn|error,默认 info")
|
||||
parser.add_argument("--instance", default=None,
|
||||
help="实例名(注册中心用),默认 redirect80")
|
||||
parser.add_argument("--help", "-h", action="help",
|
||||
help="显示此帮助并退出")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
# 默认值
|
||||
config = {
|
||||
"host": "0.0.0.0",
|
||||
"port": 80,
|
||||
"redirect_port": 443,
|
||||
"redirect_code": 301,
|
||||
"log_level": "info",
|
||||
"instance": "redirect80",
|
||||
}
|
||||
|
||||
# 1. confs 强依赖拉取配置(阻塞等待 confs 就绪,失败则退出)
|
||||
confs_host = os.environ.get("CONFS_HOST", "127.0.0.1")
|
||||
confs_port = int(os.environ.get("CONFS_PORT", "8010"))
|
||||
try:
|
||||
client = RpcClient(
|
||||
confs_host, confs_port, "redirect80",
|
||||
retry_on_refused=True, timeout=5,
|
||||
)
|
||||
data = client.call("get", {"name": "redirect80.ini"})
|
||||
ini_text = data.get("content", "")
|
||||
ini = configparser.ConfigParser(interpolation=None)
|
||||
ini.read_string(ini_text)
|
||||
|
||||
_merge = {
|
||||
("server", "host"): "host",
|
||||
("server", "port"): "port",
|
||||
("server", "redirect_port"): "redirect_port",
|
||||
("server", "redirect_code"): "redirect_code",
|
||||
}
|
||||
for (sec, key), var in _merge.items():
|
||||
if ini.has_option(sec, key):
|
||||
val = ini.get(sec, key)
|
||||
config[var] = type(config[var])(val)
|
||||
logs.info("redirect80", "配置已从 confs 加载")
|
||||
except Exception as e:
|
||||
print(f"[redirect80] confs 获取失败: {e}", file=_sys.stderr)
|
||||
_sys.exit(1)
|
||||
|
||||
# 2. 环境变量覆盖
|
||||
_env_map = {
|
||||
"REDIRECT80_HOST": "host",
|
||||
"REDIRECT80_PORT": ("port", int),
|
||||
"REDIRECT80_REDIRECT_PORT": ("redirect_port", int),
|
||||
"REDIRECT80_REDIRECT_CODE": ("redirect_code", int),
|
||||
}
|
||||
for env_var, mapping in _env_map.items():
|
||||
val = os.environ.get(env_var)
|
||||
if val is not None:
|
||||
if isinstance(mapping, tuple):
|
||||
config[mapping[0]] = mapping[1](val)
|
||||
else:
|
||||
config[mapping] = val
|
||||
|
||||
# 3. CLI 参数(最高优先级)
|
||||
if args.host is not None:
|
||||
config["host"] = args.host
|
||||
if args.port is not None:
|
||||
config["port"] = args.port
|
||||
if args.redirect_port is not None:
|
||||
config["redirect_port"] = args.redirect_port
|
||||
if args.redirect_code is not None:
|
||||
config["redirect_code"] = args.redirect_code
|
||||
if args.log_level is not None:
|
||||
config["log_level"] = args.log_level
|
||||
if args.instance is not None:
|
||||
config["instance"] = args.instance
|
||||
|
||||
# log_level 合法性校验
|
||||
lv = config["log_level"].lower()
|
||||
if lv == "warning":
|
||||
lv = "warn"
|
||||
if lv not in ("debug", "info", "warn", "error"):
|
||||
print(f"[redirect80] invalid log_level: {config['log_level']} "
|
||||
f"(must be debug|info|warn|error)", file=_sys.stderr)
|
||||
_sys.exit(1)
|
||||
config["log_level"] = lv
|
||||
|
||||
return config
|
||||
|
||||
|
||||
# ── 模块级配置(main() 中赋值)──────────────────────────────────────
|
||||
_config = {}
|
||||
|
||||
# 2. 环境变量最高优先级覆盖
|
||||
_env_map = {
|
||||
"REDIRECT80_HOST": "host",
|
||||
"REDIRECT80_PORT": ("port", int),
|
||||
"REDIRECT80_REDIRECT_PORT": ("redirect_port", int),
|
||||
"REDIRECT80_REDIRECT_CODE": ("redirect_code", int),
|
||||
}
|
||||
for env_var, mapping in _env_map.items():
|
||||
val = os.environ.get(env_var)
|
||||
if val is not None:
|
||||
if isinstance(mapping, tuple):
|
||||
_config[mapping[0]] = mapping[1](val)
|
||||
else:
|
||||
_config[mapping] = val
|
||||
|
||||
# ── 请求处理器 ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -110,10 +179,10 @@ class RedirectHandler(BaseHTTPRequestHandler):
|
||||
f"{code} {self.command} {host} → {location}")
|
||||
|
||||
def do_GET(self):
|
||||
self._redirect(301)
|
||||
self._redirect(_config["redirect_code"])
|
||||
|
||||
def do_HEAD(self):
|
||||
self._redirect(301)
|
||||
self._redirect(_config["redirect_code"])
|
||||
|
||||
def do_POST(self):
|
||||
self._redirect(308)
|
||||
@@ -158,17 +227,24 @@ class ThreadingRedirectServer(ThreadingMixIn, HTTPServer):
|
||||
|
||||
|
||||
def main():
|
||||
global _config
|
||||
_config = load_config(_sys.argv[1:])
|
||||
|
||||
# 设置日志级别过滤
|
||||
set_log_level(_config["log_level"])
|
||||
|
||||
host = _config["host"]
|
||||
port = int(_config["port"])
|
||||
|
||||
server = ThreadingRedirectServer((host, port), RedirectHandler)
|
||||
|
||||
logs.info("redirect80",
|
||||
f"HTTP→HTTPS 重定向服务启动 "
|
||||
f"服务启动 instance={_config['instance']} "
|
||||
f"http://{host}:{port} → https://*:{_config['redirect_port']}")
|
||||
|
||||
print(f"[redirect80] 监听 http://{host}:{port} "
|
||||
f"→ 重定向到 :{_config['redirect_port']}", file=_sys.stderr)
|
||||
print(f"[redirect80] instance={_config['instance']} log-level={_config['log_level']} "
|
||||
f"监听 http://{host}:{port} → 重定向到 :{_config['redirect_port']}",
|
||||
file=_sys.stderr)
|
||||
|
||||
try:
|
||||
server.serve_forever()
|
||||
|
||||
@@ -38,6 +38,29 @@ _FLUSH_INTERVAL = 2.0 # 超时 2 秒强制 flush
|
||||
_TIMEOUT = 5 # socket 超时(秒)
|
||||
_QUEUE_MAXSIZE = 10000 # 队列上限,防止后台线程异常时内存无限增长;满则丢弃最旧条目
|
||||
|
||||
# ── 日志级别过滤 ──
|
||||
_LOG_LEVELS = {
|
||||
"debug": 0, "info": 1, "warn": 2, "warning": 2,
|
||||
"error": 3, "critical": 4,
|
||||
}
|
||||
_LOG_LEVEL_THRESHOLD = _LOG_LEVELS["info"] # 默认 info
|
||||
|
||||
|
||||
def _normalize_level(level):
|
||||
"""归一化级别名:大写转小写,warning→warn。"""
|
||||
lv = (level or "info").lower()
|
||||
if lv == "warning":
|
||||
lv = "warn"
|
||||
return lv
|
||||
|
||||
|
||||
def set_log_level(level):
|
||||
"""设置日志过滤阈值(低于该级别的日志将被丢弃)。"""
|
||||
global _LOG_LEVEL_THRESHOLD
|
||||
lv = _normalize_level(level)
|
||||
if lv in _LOG_LEVELS:
|
||||
_LOG_LEVEL_THRESHOLD = _LOG_LEVELS[lv]
|
||||
|
||||
|
||||
def _safe_json(obj):
|
||||
"""安全序列化 extra 字段,不可序列化对象转为 repr 字符串,保证不抛异常。"""
|
||||
@@ -77,6 +100,9 @@ class LogClient:
|
||||
|
||||
队列满时静默丢弃最旧条目再放入新条目,保证不阻塞业务线程、不导致 OOM。
|
||||
"""
|
||||
# 级别过滤:低于当前阈值的日志直接丢弃(不进队列)
|
||||
if _LOG_LEVELS.get(_normalize_level(level), 0) < _LOG_LEVEL_THRESHOLD:
|
||||
return
|
||||
entry = {
|
||||
'ts': _now_iso(), # 入队时即生成时间戳,反映真实事件时间
|
||||
'service': service,
|
||||
|
||||
+12
-1
@@ -303,12 +303,23 @@ if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="smtp — pymsg 邮箱验证码服务")
|
||||
parser.add_argument("--addr", default="127.0.0.1", help="监听地址")
|
||||
parser.add_argument("--addr", "--host", dest="addr", default="127.0.0.1",
|
||||
help="监听地址(--host 与 --addr 等价)")
|
||||
parser.add_argument("--port", type=int, default=8008, help="监听端口")
|
||||
parser.add_argument("--workers", type=int, default=4,
|
||||
help="回调并发线程数(0=同步串行模式)")
|
||||
parser.add_argument("--log-level", default="info",
|
||||
choices=["debug", "info", "warn", "error"],
|
||||
help="日志级别(默认 info)")
|
||||
parser.add_argument("--instance", default="smtp",
|
||||
help="实例名(注册中心用,默认 smtp)")
|
||||
args = parser.parse_args()
|
||||
|
||||
# 应用日志级别过滤(低于阈值的日志不进队列)
|
||||
logs.set_log_level(args.log_level)
|
||||
|
||||
print(f"[smtp] instance={args.instance} log-level={args.log_level}")
|
||||
print(f"[smtp] 配置来源: .env / ENV > 默认值(SMTP 账号不通过 CLI 传递)")
|
||||
server = SmtpServer(args.addr, args.port, workers=args.workers)
|
||||
try:
|
||||
server.start()
|
||||
|
||||
@@ -30,13 +30,47 @@ const (
|
||||
maxMsgSize = 64 * 1024 * 1024
|
||||
)
|
||||
|
||||
// 日志级别数值(越小越详细)。
|
||||
const (
|
||||
levelDebug = 0
|
||||
levelInfo = 1
|
||||
levelWarn = 2
|
||||
levelError = 3
|
||||
)
|
||||
|
||||
var levelMap = map[string]int{
|
||||
"debug": levelDebug,
|
||||
"info": levelInfo,
|
||||
"warn": levelWarn,
|
||||
"warning": levelWarn,
|
||||
"error": levelError,
|
||||
"DEBUG": levelDebug,
|
||||
"INFO": levelInfo,
|
||||
"WARN": levelWarn,
|
||||
"WARNING": levelWarn,
|
||||
"ERROR": levelError,
|
||||
}
|
||||
|
||||
// dbAddr 通过环境变量 LOG_DB_ADDR 配置(默认 127.0.0.1:8005),支持非标准部署场景。
|
||||
var dbAddr = defaultDBAddr
|
||||
|
||||
// minLevel 控制最低输出级别,默认 info(debug 被过滤)。
|
||||
var minLevel = levelInfo
|
||||
|
||||
func init() {
|
||||
if v := os.Getenv("LOG_DB_ADDR"); v != "" {
|
||||
dbAddr = v
|
||||
}
|
||||
if v := os.Getenv("LOG_LEVEL"); v != "" {
|
||||
SetLevel(v)
|
||||
}
|
||||
}
|
||||
|
||||
// SetLevel 设置最低日志级别(debug|info|warn|error,大小写不敏感)。
|
||||
func SetLevel(level string) {
|
||||
if n, ok := levelMap[level]; ok {
|
||||
minLevel = n
|
||||
}
|
||||
}
|
||||
|
||||
// Entry 单条日志。
|
||||
@@ -93,10 +127,15 @@ func New() *Client {
|
||||
}
|
||||
|
||||
// Push 推送一条日志(非阻塞,队列满时丢弃)。
|
||||
// 低于 SetLevel 设定的最低级别的日志将被静默丢弃。
|
||||
func Push(service, level, message string, extra map[string]any) {
|
||||
if global == nil {
|
||||
return
|
||||
}
|
||||
n, ok := levelMap[level]
|
||||
if !ok || n < minLevel {
|
||||
return
|
||||
}
|
||||
global.push(Entry{
|
||||
Service: service,
|
||||
Level: level,
|
||||
@@ -105,6 +144,11 @@ func Push(service, level, message string, extra map[string]any) {
|
||||
})
|
||||
}
|
||||
|
||||
// Debug 推送 DEBUG 级别日志。
|
||||
func Debug(service, message string, extra map[string]any) {
|
||||
Push(service, "DEBUG", message, extra)
|
||||
}
|
||||
|
||||
// Info 推送 INFO 级别日志。
|
||||
func Info(service, message string, extra map[string]any) {
|
||||
Push(service, "INFO", message, extra)
|
||||
|
||||
+57
-6
@@ -42,15 +42,66 @@ type Config struct {
|
||||
|
||||
func main() {
|
||||
var (
|
||||
configFile = flag.String("c", "config.yaml", "配置文件路径")
|
||||
doList = flag.Bool("list", false, "列出所有规则后退出")
|
||||
doTest = flag.String("test", "", "校验单个规则文件语法后退出")
|
||||
doCheck = flag.String("check", "", "用当前规则测试输入字符串(URI/path)后退出")
|
||||
configFile = flag.String("c", "config.yaml", "配置文件路径")
|
||||
doList = flag.Bool("list", false, "列出所有规则后退出")
|
||||
doTest = flag.String("test", "", "校验单个规则文件语法后退出")
|
||||
doCheck = flag.String("check", "", "用当前规则测试输入字符串(URI/path)后退出")
|
||||
// CLI 标准化参数(覆盖 config.yaml)
|
||||
cliListen = flag.String("listen", "", "监听地址(别名: --host),覆盖 config.yaml")
|
||||
cliHost = flag.String("host", "", "监听地址(--listen 别名)")
|
||||
cliUpstream = flag.String("upstream", "", "gateway 地址,覆盖 config.yaml")
|
||||
cliTLSCert = flag.String("tls-cert", "", "TLS 证书文件,覆盖 config.yaml")
|
||||
cliTLSKey = flag.String("tls-key", "", "TLS 私钥文件,覆盖 config.yaml")
|
||||
cliRulesDir = flag.String("rules-dir", "", "规则根目录,覆盖 config.yaml")
|
||||
logLevel = flag.String("log-level", "info", "日志级别: debug|info|warn|error")
|
||||
instance = flag.String("instance", "waf", "实例名(注册中心用)")
|
||||
)
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
"waf — WAF 入口网关\n\n"+
|
||||
"用法: waf [选项]\n\n"+
|
||||
"选项:\n"+
|
||||
" -c <file> 配置文件路径,默认 config.yaml\n"+
|
||||
" --listen <addr> 监听地址(别名: --host),覆盖 config.yaml\n"+
|
||||
" --upstream <url> gateway 地址,覆盖 config.yaml\n"+
|
||||
" --tls-cert <file> TLS 证书文件,覆盖 config.yaml\n"+
|
||||
" --tls-key <file> TLS 私钥文件,覆盖 config.yaml\n"+
|
||||
" --rules-dir <dir> 规则根目录,覆盖 config.yaml\n"+
|
||||
" --log-level <lv> debug|info|warn|error,默认 info\n"+
|
||||
" --instance <name> 实例名,默认 waf\n"+
|
||||
" -list 列出所有规则后退出\n"+
|
||||
" -test <file> 校验单个规则文件语法后退出\n"+
|
||||
" -check <input> 用当前规则测试输入字符串后退出\n"+
|
||||
" -h, --help 显示此帮助并退出\n\n"+
|
||||
"配置优先级: CLI > config.yaml\n")
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
// 日志级别
|
||||
logclient.SetLevel(*logLevel)
|
||||
|
||||
cfg := loadConfig(*configFile)
|
||||
|
||||
// CLI 覆盖 config.yaml
|
||||
if *cliListen != "" {
|
||||
cfg.Listen = *cliListen
|
||||
}
|
||||
if *cliHost != "" {
|
||||
cfg.Listen = *cliHost
|
||||
}
|
||||
if *cliUpstream != "" {
|
||||
cfg.Upstream = *cliUpstream
|
||||
}
|
||||
if *cliTLSCert != "" {
|
||||
cfg.TLSCert = *cliTLSCert
|
||||
}
|
||||
if *cliTLSKey != "" {
|
||||
cfg.TLSKey = *cliTLSKey
|
||||
}
|
||||
if *cliRulesDir != "" {
|
||||
cfg.RulesDir = *cliRulesDir
|
||||
}
|
||||
|
||||
// 子命令:极客 CLI 工具
|
||||
if *doTest != "" {
|
||||
if err := engine.TestFile(*doTest); err != nil {
|
||||
@@ -146,8 +197,8 @@ func main() {
|
||||
termCh := make(chan os.Signal, 1)
|
||||
signal.Notify(termCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
logclient.Info("waf", fmt.Sprintf("服务启动 listen=%s upstream=%s rules=%s pid=%d",
|
||||
cfg.Listen, cfg.Upstream, cfg.RulesDir, os.Getpid()), nil)
|
||||
logclient.Info("waf", fmt.Sprintf("服务启动 instance=%s listen=%s upstream=%s rules=%s pid=%d",
|
||||
*instance, cfg.Listen, cfg.Upstream, cfg.RulesDir, os.Getpid()), nil)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
|
||||
Reference in new issue
Block a user