Skip to content

Commit 5457695

Browse files
committed
```
feat(core): 添加日志等级配置和配置项校验功能 - 新增 log_level 配置项,默认值为 info,支持 debug/info/warn/error - 在 build.ps1 中添加日志等级配置示例和文档说明 - 实现配置项校验机制,对无效的 proxy.type、proxy.host、proxy.port 进行防御性修正 - 对 timeout 配置项进行有效性验证,确保超时值为正数 - 统一配置项大小写处理,避免大小写导致的配置失效 - 添加代理规则中 dns_mode 和 ipv6_mode 的枚举值校验 - 改进 Logger 类,使用原子操作管理日志等级,支持动态调整日志粒度 - 实现多进程环境下的日志文件写入互斥机制,避免日志交错和损坏 ```
1 parent 0c649f6 commit 5457695

File tree

7 files changed

+630
-95
lines changed

7 files changed

+630
-95
lines changed

build.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ $configJson = @{
251251
"config" = $Config
252252
"arch" = $Arch
253253
}
254+
# 日志等级:默认 info(更克制,减少刷屏/IO 开销);需要更细粒度排障时可改为 debug
255+
log_level = "info"
254256
proxy = @{
255257
host = "127.0.0.1"
256258
port = 7890
@@ -311,6 +313,7 @@ Antigravity-Proxy 是一个基于 MinHook 的 Windows DLL 代理注入工具。
311313
"port": 7890, // 代理服务器端口
312314
"type": "socks5" // 代理类型: socks5 或 http
313315
},
316+
"log_level": "info", // 日志等级: debug/info/warn/error (默认 info)
314317
"fake_ip": {
315318
"enabled": true, // 是否启用 FakeIP 系统 (拦截 DNS 解析)
316319
"cidr": "198.18.0.0/15" // FakeIP 分配的虚拟 IP 地址范围 (默认为基准测试保留网段)
@@ -361,6 +364,7 @@ Test-NetConnection -ComputerName 127.0.0.1 -Port 7890
361364
362365
| 配置项 | 说明 | 默认值 |
363366
|--------|------|--------|
367+
| log_level | 日志等级 (debug/info/warn/error) | info |
364368
| proxy.host | 代理服务器地址 | 127.0.0.1 |
365369
| proxy.port | 代理服务器端口 | 7890 |
366370
| proxy.type | 代理类型 (socks5/http) | socks5 |

src/core/Config.hpp

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,18 @@ namespace Core {
160160
}
161161
return false;
162162
}
163+
nlohmann::json j = nlohmann::json::parse(f);
164+
165+
// 日志等级:默认 info(更克制),允许通过配置切到 debug 以获得更细粒度排障信息
166+
// 设计意图:默认减少刷屏/IO 开销,现场需要时可提升日志粒度。
167+
const std::string logLevelStr = j.value("log_level", "info");
168+
if (!Logger::SetLevelFromString(logLevelStr)) {
169+
Logger::SetLevel(LogLevel::Info);
170+
Logger::Warn("配置: log_level 无效(" + logLevelStr + "),已回退为 info (可选: debug/info/warn/error)");
171+
}
163172
if (!resolvedPath.empty()) {
164173
Logger::Info("使用配置文件路径: " + resolvedPath);
165174
}
166-
nlohmann::json j = nlohmann::json::parse(f);
167175

168176
if (j.contains("proxy")) {
169177
auto& p = j["proxy"];
@@ -172,6 +180,23 @@ namespace Core {
172180
proxy.type = p.value("type", "socks5");
173181
}
174182

183+
// 配置校验:统一 proxy.type 大小写,并对关键字段做防御性修正,避免运行期异常
184+
std::transform(proxy.type.begin(), proxy.type.end(), proxy.type.begin(),
185+
[](unsigned char c) { return (char)std::tolower(c); });
186+
if (proxy.type.empty()) proxy.type = "socks5";
187+
if (proxy.type != "socks5" && proxy.type != "http") {
188+
Logger::Warn("配置: proxy.type 无效(" + proxy.type + "),已回退为 socks5 (可选: socks5/http)");
189+
proxy.type = "socks5";
190+
}
191+
if (proxy.host.empty()) {
192+
Logger::Warn("配置: proxy.host 为空,已回退为 127.0.0.1");
193+
proxy.host = "127.0.0.1";
194+
}
195+
if (proxy.port < 0 || proxy.port > 65535) {
196+
Logger::Warn("配置: proxy.port 超出范围(" + std::to_string(proxy.port) + "),已回退为 7890");
197+
proxy.port = 7890;
198+
}
199+
175200
if (j.contains("fake_ip")) {
176201
auto& fip = j["fake_ip"];
177202
fakeIp.enabled = fip.value("enabled", true);
@@ -186,6 +211,20 @@ namespace Core {
186211
timeout.recv_ms = t.value("recv", 5000);
187212
}
188213

214+
// 配置校验:超时必须为正数,避免 select/WaitConnect 异常行为
215+
if (timeout.connect_ms <= 0) {
216+
Logger::Warn("配置: timeout.connect 非法(" + std::to_string(timeout.connect_ms) + "),已回退为 5000");
217+
timeout.connect_ms = 5000;
218+
}
219+
if (timeout.send_ms <= 0) {
220+
Logger::Warn("配置: timeout.send 非法(" + std::to_string(timeout.send_ms) + "),已回退为 5000");
221+
timeout.send_ms = 5000;
222+
}
223+
if (timeout.recv_ms <= 0) {
224+
Logger::Warn("配置: timeout.recv 非法(" + std::to_string(timeout.recv_ms) + "),已回退为 5000");
225+
timeout.recv_ms = 5000;
226+
}
227+
189228
// ============= 代理路由规则解析 =============
190229
bool hasProxyRules = false;
191230
if (j.contains("proxy_rules")) {
@@ -202,13 +241,26 @@ namespace Core {
202241
}
203242
// 解析 DNS 策略
204243
rules.dns_mode = pr.value("dns_mode", "direct");
244+
std::transform(rules.dns_mode.begin(), rules.dns_mode.end(),
245+
rules.dns_mode.begin(),
246+
[](unsigned char c) { return (char)std::tolower(c); });
247+
if (rules.dns_mode.empty()) rules.dns_mode = "direct";
205248
// 解析 IPv6 策略,统一为小写,避免大小写导致配置失效
206249
rules.ipv6_mode = pr.value("ipv6_mode", "proxy");
207250
std::transform(rules.ipv6_mode.begin(), rules.ipv6_mode.end(),
208251
rules.ipv6_mode.begin(),
209252
[](unsigned char c) { return (char)std::tolower(c); });
210253
if (rules.ipv6_mode.empty()) rules.ipv6_mode = "proxy";
211254
}
255+
// 配置校验:限制策略枚举取值,避免拼写错误导致绕过预期逻辑
256+
if (rules.dns_mode != "direct" && rules.dns_mode != "proxy") {
257+
Logger::Warn("配置: proxy_rules.dns_mode 无效(" + rules.dns_mode + "),已回退为 direct (可选: direct/proxy)");
258+
rules.dns_mode = "direct";
259+
}
260+
if (rules.ipv6_mode != "proxy" && rules.ipv6_mode != "direct" && rules.ipv6_mode != "block") {
261+
Logger::Warn("配置: proxy_rules.ipv6_mode 无效(" + rules.ipv6_mode + "),已回退为 proxy (可选: proxy/direct/block)");
262+
rules.ipv6_mode = "proxy";
263+
}
212264
Logger::Info("路由规则: allowed_ports=" + std::to_string(rules.allowed_ports.size()) +
213265
" 项, dns_mode=" + rules.dns_mode + ", ipv6_mode=" + rules.ipv6_mode +
214266
(hasProxyRules ? "" : " (默认)"));

src/core/Logger.hpp

Lines changed: 131 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#pragma once
22
#include <fstream>
3+
#include <atomic>
34
#include <mutex>
45
#include <string>
6+
#include <algorithm>
7+
#include <cctype>
58
#include <iostream>
69

710
#ifndef WIN32_LEAN_AND_MEAN
@@ -14,8 +17,65 @@
1417
#include <sstream>
1518

1619
namespace Core {
20+
// 日志等级(用于控制输出粒度:默认 Info;需要更细粒度排障时可切到 Debug)
21+
enum class LogLevel : int {
22+
Debug = 0,
23+
Info = 1,
24+
Warn = 2,
25+
Error = 3,
26+
};
27+
1728
class Logger {
1829
private:
30+
// ========== 日志等级控制 ==========
31+
// 设计意图:默认 Info(更克制),现场需要时可切到 Debug;同时提供可配置降级能力以降低性能开销。
32+
static std::atomic<int>& LevelStorage() {
33+
static std::atomic<int> s_level{static_cast<int>(LogLevel::Info)};
34+
return s_level;
35+
}
36+
37+
static bool TryParseLevelFromString(const std::string& input, LogLevel* out) {
38+
if (!out) return false;
39+
40+
// 去掉首尾空白,并统一转小写(配置中常用 debug/info/warn/error)
41+
std::string s = input;
42+
auto notSpace = [](unsigned char c) { return !std::isspace(c); };
43+
s.erase(s.begin(), std::find_if(s.begin(), s.end(), notSpace));
44+
s.erase(std::find_if(s.rbegin(), s.rend(), notSpace).base(), s.end());
45+
std::transform(s.begin(), s.end(), s.begin(),
46+
[](unsigned char c) { return (char)std::tolower(c); });
47+
48+
if (s == "debug" || s == "d" || s == "trace" || s == "verbose" || s == "调试") {
49+
*out = LogLevel::Debug;
50+
return true;
51+
}
52+
if (s == "info" || s == "i" || s == "信息") {
53+
*out = LogLevel::Info;
54+
return true;
55+
}
56+
if (s == "warn" || s == "warning" || s == "w" || s == "警告") {
57+
*out = LogLevel::Warn;
58+
return true;
59+
}
60+
if (s == "error" || s == "err" || s == "e" || s == "错误") {
61+
*out = LogLevel::Error;
62+
return true;
63+
}
64+
return false;
65+
}
66+
67+
// ========== 跨进程互斥(用于多进程注入场景下的日志一致性) ==========
68+
// 设计意图:序列化“检查大小→必要时截断→写入”这一段,避免多进程互相截断或写入交错。
69+
static HANDLE GetCrossProcessLogMutex() {
70+
static HANDLE s_mutex = NULL;
71+
static std::once_flag s_once;
72+
std::call_once(s_once, []() {
73+
// 使用 Local\ 命名空间:对普通桌面进程权限更稳
74+
s_mutex = CreateMutexA(NULL, FALSE, "Local\\AntigravityProxy_LogFileMutex");
75+
});
76+
return s_mutex;
77+
}
78+
1979
// ========== 日志目录相关函数 ==========
2080

2181
// 获取 DLL 所在目录(用于定位日志目录)
@@ -138,6 +198,14 @@ namespace Core {
138198
return size >= maxBytes;
139199
}
140200

201+
static ULONGLONG GetFileSizeBytes(const std::string& path) {
202+
WIN32_FILE_ATTRIBUTE_DATA data{};
203+
if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &data)) {
204+
return 0;
205+
}
206+
return (static_cast<ULONGLONG>(data.nFileSizeHigh) << 32) | data.nFileSizeLow;
207+
}
208+
141209
// 清理旧日志文件,只保留当天的日志
142210
static void CleanupOldLogs(const std::string& todayLog) {
143211
std::string logDir = GetLogDirectory();
@@ -175,57 +243,100 @@ namespace Core {
175243
}
176244

177245
static void WriteToFile(const std::string& message) {
178-
static std::mutex mtx;
179-
std::lock_guard<std::mutex> lock(mtx);
180246
// 按日期写日志并清理旧文件,避免历史日志堆积
181247
static std::string s_todayLog;
182-
static ULONGLONG s_lastCheckTick = 0;
183-
static bool s_dropForToday = false;
184-
static const ULONGLONG kMaxLogBytes = 100ull * 1024 * 1024; // 100MB
185-
static const ULONGLONG kCheckIntervalMs = 60ull * 60 * 1000; // 1 小时
248+
// 需求:单文件 10MB 达到即覆盖写入(不轮转、不备份)
249+
static const ULONGLONG kMaxLogBytes = 10ull * 1024 * 1024; // 10MB
250+
251+
// 多进程注入场景:使用跨进程互斥量保证“检查+截断+写入”的原子性
252+
HANDLE hMutex = GetCrossProcessLogMutex();
253+
DWORD waitRc = WAIT_FAILED;
254+
if (hMutex) {
255+
waitRc = WaitForSingleObject(hMutex, INFINITE);
256+
}
257+
const bool locked = (hMutex != NULL) && (waitRc == WAIT_OBJECT_0 || waitRc == WAIT_ABANDONED);
258+
259+
// 如果跨进程互斥不可用,退化为进程内互斥,保证不崩溃(但多进程一致性会弱一些)
260+
static std::mutex s_fallbackMtx;
261+
std::unique_lock<std::mutex> fallbackLock;
262+
if (!locked) {
263+
fallbackLock = std::unique_lock<std::mutex>(s_fallbackMtx);
264+
}
265+
186266
std::string todayLog = GetTodayLogName();
187267
if (s_todayLog != todayLog) {
188268
s_todayLog = todayLog;
189-
s_lastCheckTick = 0;
190-
s_dropForToday = false;
191269
CleanupOldLogs(s_todayLog);
192270
}
193-
if (s_dropForToday) {
194-
return;
195-
}
196-
ULONGLONG nowTick = GetTickCount64();
197-
if (s_lastCheckTick == 0 || nowTick - s_lastCheckTick >= kCheckIntervalMs) {
198-
s_lastCheckTick = nowTick;
199-
if (IsLogOverLimit(s_todayLog, kMaxLogBytes)) {
200-
// 当天日志超过上限后直接丢弃,次日恢复
201-
s_dropForToday = true;
202-
return;
203-
}
271+
272+
// 判断本次写入是否会超过上限;超过则直接截断覆盖写入
273+
const ULONGLONG currentSize = GetFileSizeBytes(s_todayLog);
274+
const ULONGLONG appendBytes = static_cast<ULONGLONG>(message.size() + 1); // + '\n'
275+
const bool needTruncate = (currentSize > 0 && (currentSize + appendBytes) > kMaxLogBytes);
276+
277+
std::ofstream logFile;
278+
if (needTruncate) {
279+
logFile.open(s_todayLog, std::ios::out | std::ios::trunc);
280+
} else {
281+
logFile.open(s_todayLog, std::ios::out | std::ios::app);
204282
}
205-
std::ofstream logFile(s_todayLog, std::ios::app);
206283
if (logFile.is_open()) {
207284
logFile << message << "\n";
208285
}
286+
287+
if (locked) {
288+
ReleaseMutex(hMutex);
289+
}
209290
}
210291

211292
public:
293+
// 判断某个等级的日志是否会输出(用于调用方做“懒构造字符串”,减少性能开销)
294+
static bool IsEnabled(LogLevel level) {
295+
const int threshold = LevelStorage().load(std::memory_order_relaxed);
296+
return static_cast<int>(level) >= threshold;
297+
}
298+
299+
static LogLevel GetLevel() {
300+
return static_cast<LogLevel>(LevelStorage().load(std::memory_order_relaxed));
301+
}
302+
303+
static void SetLevel(LogLevel level) {
304+
LevelStorage().store(static_cast<int>(level), std::memory_order_relaxed);
305+
}
306+
307+
// 从字符串设置日志等级;返回是否识别成功(不识别则不修改当前等级)
308+
static bool SetLevelFromString(const std::string& levelStr) {
309+
LogLevel parsed;
310+
if (!TryParseLevelFromString(levelStr, &parsed)) {
311+
return false;
312+
}
313+
SetLevel(parsed);
314+
return true;
315+
}
316+
212317
static void Log(const std::string& message) {
318+
// 将无等级的 Log 视为 Info 级别,确保可被 log_level 控制
319+
if (!IsEnabled(LogLevel::Info)) return;
213320
WriteToFile("[" + GetTimestamp() + "] " + GetPidTidPrefix() + " " + message);
214321
}
215322

216323
static void Error(const std::string& message) {
324+
if (!IsEnabled(LogLevel::Error)) return;
217325
WriteToFile("[" + GetTimestamp() + "] " + GetPidTidPrefix() + " [错误] " + message);
218326
}
219327

220328
static void Info(const std::string& message) {
329+
if (!IsEnabled(LogLevel::Info)) return;
221330
WriteToFile("[" + GetTimestamp() + "] " + GetPidTidPrefix() + " [信息] " + message);
222331
}
223332

224333
static void Warn(const std::string& message) {
334+
if (!IsEnabled(LogLevel::Warn)) return;
225335
WriteToFile("[" + GetTimestamp() + "] " + GetPidTidPrefix() + " [警告] " + message);
226336
}
227337

228338
static void Debug(const std::string& message) {
339+
if (!IsEnabled(LogLevel::Debug)) return;
229340
WriteToFile("[" + GetTimestamp() + "] " + GetPidTidPrefix() + " [调试] " + message);
230341
}
231342
};

0 commit comments

Comments
 (0)