refactor(logging): 重构日志级别为层级过滤设计

- LogLevel 从 string 改为 int 类型,支持层级比较
- 层级设计:Debug(0) < Base(1) < Info(2) < Success(3) < Vuln(4) < Error(5)
- 设置一个级别后,显示该级别及以上的日志
- Error 级别始终显示,不会被配置过滤掉
- 保留向后兼容别名(LevelAll, LevelInfoSuccess 等)
- 更新测试以匹配新的层级过滤行为
This commit is contained in:
ZacharyZcR
2026-01-20 13:12:11 +08:00
parent eec3941fba
commit 7c53a1c3a3
3 changed files with 51 additions and 79 deletions
+17 -13
View File
@@ -13,23 +13,27 @@ import (
)
// =============================================================================
// 日志级别常量 (从Types.go迁移)
// 日志级别常量 - 层级设计
// =============================================================================
// LogLevel 日志级别类型
type LogLevel string
// LogLevel 日志级别类型(数值越小越详细)
type LogLevel int
// 定义系统支持的日志级别常量
// 定义系统支持的日志级别常量(层级:Debug < Base < Info < Success < Vuln < Error
const (
LevelAll LogLevel = "ALL" // 显示所有级别日志
LevelError LogLevel = "ERROR" // 仅显示错误日志
LevelBase LogLevel = "BASE" // 仅显示基础信息日志
LevelInfo LogLevel = "INFO" // 仅显示信息日志
LevelSuccess LogLevel = "SUCCESS" // 仅显示成功日志(Web指纹等)
LevelVuln LogLevel = "VULN" // 漏洞和重要发现(密码成功、漏洞等
LevelDebug LogLevel = "DEBUG" // 仅显示调试日志
LevelInfoSuccess LogLevel = "INFO_SUCCESS" // 仅显示信息和成功日志
LevelBaseInfoSuccess LogLevel = "BASE_INFO_SUCCESS" // 显示基础、信息和成功日志
LevelDebug LogLevel = 0 // 调试信息(最详细)
LevelBase LogLevel = 1 // 基础信息(扫描进度等)
LevelInfo LogLevel = 2 // 一般信息(端口开放、服务识别等)
LevelSuccess LogLevel = 3 // 成功结果(Web指纹等)
LevelVuln LogLevel = 4 // 重要发现(弱密码、漏洞等)
LevelError LogLevel = 5 // 错误信息(始终显示
)
// 向后兼容的别名
const (
LevelAll LogLevel = LevelDebug // ALL 等同于 Debug(显示所有)
LevelInfoSuccess LogLevel = LevelInfo // 废弃,映射到 Info
LevelBaseInfoSuccess LogLevel = LevelBase // 废弃,映射到 Base
)
// =============================================================================
+5 -24
View File
@@ -128,33 +128,14 @@ func (l *Logger) log(level LogLevel, content string) {
}
// shouldLog 检查是否应该记录该级别的日志
// 层级过滤:消息级别 >= 配置级别 时显示,Error 始终显示
func (l *Logger) shouldLog(level LogLevel) bool {
switch l.config.Level {
case LevelAll:
// Error 级别始终显示
if level == LevelError {
return true
case LevelError:
return level == LevelError
case LevelVuln:
return level == LevelVuln
case LevelBase:
return level == LevelBase
case LevelInfo:
return level == LevelInfo
case LevelSuccess:
return level == LevelSuccess
case LevelDebug:
return level == LevelDebug
case LevelInfoSuccess:
return level == LevelInfo || level == LevelSuccess || level == LevelVuln
case LevelBaseInfoSuccess:
return level == LevelBase || level == LevelInfo || level == LevelSuccess || level == LevelVuln
default:
// 向后兼容:字符串"debug"显示所有
if string(l.config.Level) == "debug" {
return true
}
return level == LevelInfo || level == LevelSuccess || level == LevelVuln
}
// 层级过滤:消息级别 >= 配置级别
return level >= l.config.Level
}
// outputMessage 输出消息
+29 -42
View File
@@ -188,9 +188,9 @@ func TestLogger_AllLevels(t *testing.T) {
t.Errorf("输出缺少前缀: %s\n实际: %s", tt.wantPfx, msg)
}
// 验证时间格式
if !strings.HasPrefix(msg, "[") {
t.Errorf("输出应该以时间开头: %s", msg)
// 验证输出格式:前缀 + 空格 + 消息
if !strings.HasPrefix(msg, tt.wantPfx) {
t.Errorf("输出应该以前缀开头: %s\n实际: %s", tt.wantPfx, msg)
}
t.Logf("✓ %s 输出正确: %s", tt.name, msg)
@@ -249,7 +249,7 @@ func TestLogger_LevelFiltering(t *testing.T) {
},
wantOutput: map[string]bool{
"base": false, "info": true,
"success": true, "error": false,
"success": true, "error": true, // Error 始终显示(层级设计)
},
},
{
@@ -296,9 +296,9 @@ func TestLogger_LevelFiltering(t *testing.T) {
// Logger - 时间格式化测试
// =============================================================================
// TestLogger_TimeFormatting 测试时间格式化
// TestLogger_TimeFormatting 测试时间格式化函数
//
// 验证:不同时长格式化正确(毫秒、秒、分钟、小时)
// 验证:formatElapsedTime 对不同时长格式化正确(毫秒、秒、分钟、小时)
func TestLogger_TimeFormatting(t *testing.T) {
tests := []struct {
name string
@@ -362,30 +362,17 @@ func TestLogger_TimeFormatting(t *testing.T) {
},
}
// 直接测试 formatElapsedTime 函数
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
config := &LoggerConfig{
Level: LevelAll,
EnableColor: false,
StartTime: time.Now().Add(-tt.elapsed),
}
logger := NewLogger(config)
capture := &captureOutput{}
logger.SetCoordinatedOutput(capture.Write)
logger := NewLogger(nil)
result := logger.formatElapsedTime(tt.elapsed)
logger.Info("test")
output := capture.Get()
if len(output) != 1 {
t.Fatalf("期望1条输出,实际%d条", len(output))
if result != tt.wantStr {
t.Errorf("时间格式错误\n期望: %s\n实际: %s", tt.wantStr, result)
}
if !strings.Contains(output[0], tt.wantStr) {
t.Errorf("时间格式错误\n期望包含: %s\n实际输出: %s",
tt.wantStr, output[0])
}
t.Logf("✓ %s → %s", tt.name, tt.wantStr)
t.Logf("✓ %s → %s", tt.name, result)
})
}
}
@@ -441,9 +428,9 @@ func TestLogger_ConcurrentLogging(t *testing.T) {
totalLogs, len(output))
}
// 验证每条日志格式正确
// 验证每条日志格式正确(前缀可能是 "[" 或空格)
for i, line := range output {
if !strings.HasPrefix(line, "[") {
if !strings.HasPrefix(line, "[") && !strings.HasPrefix(line, " ") {
t.Errorf("第%d条日志格式错误: %s", i+1, line)
break
}
@@ -483,7 +470,7 @@ func TestLogger_NoCoordinatedOutput(t *testing.T) {
// TestLogger_SingleLevels 测试单独级别配置
//
// 验证:每个单独级别(LevelDebug, LevelBase等)只输出对应级别
// 验证:层级过滤 - 设置一个级别后,显示该级别及以上的日志,Error始终显示
func TestLogger_SingleLevels(t *testing.T) {
tests := []struct {
name string
@@ -492,7 +479,7 @@ func TestLogger_SingleLevels(t *testing.T) {
wantOutput map[string]bool
}{
{
name: "LevelDebug - 仅调试",
name: "LevelDebug - 显示所有",
configLevel: LevelDebug,
testLevels: map[string]func(*Logger, string){
"debug": (*Logger).Debug,
@@ -502,12 +489,12 @@ func TestLogger_SingleLevels(t *testing.T) {
"error": (*Logger).Error,
},
wantOutput: map[string]bool{
"debug": true, "base": false, "info": false,
"success": false, "error": false,
"debug": true, "base": true, "info": true,
"success": true, "error": true, // 层级过滤:Debug(0)及以上全显示
},
},
{
name: "LevelBase - 基础",
name: "LevelBase - 基础及以上",
configLevel: LevelBase,
testLevels: map[string]func(*Logger, string){
"debug": (*Logger).Debug,
@@ -515,29 +502,29 @@ func TestLogger_SingleLevels(t *testing.T) {
"info": (*Logger).Info,
},
wantOutput: map[string]bool{
"debug": false, "base": true, "info": false,
"debug": false, "base": true, "info": true, // 层级过滤:Base(1)及以上
},
},
{
name: "LevelInfo - 信息",
name: "LevelInfo - 信息及以上",
configLevel: LevelInfo,
testLevels: map[string]func(*Logger, string){
"base": (*Logger).Base,
"info": (*Logger).Info,
},
wantOutput: map[string]bool{
"base": false, "info": true,
"base": false, "info": true, // 层级过滤:Info(2)及以上
},
},
{
name: "LevelSuccess - 成功",
name: "LevelSuccess - 成功及以上",
configLevel: LevelSuccess,
testLevels: map[string]func(*Logger, string){
"info": (*Logger).Info,
"success": (*Logger).Success,
},
wantOutput: map[string]bool{
"info": false, "success": true,
"info": false, "success": true, // 层级过滤:Success(3)及以上
},
},
}
@@ -604,10 +591,10 @@ func TestLogger_ColorOutput(t *testing.T) {
// TestLogger_BackwardCompatibility 测试向后兼容性
//
// 验证:字符串"debug"作为级别时的行为
// 验证:LevelAll 等同于 LevelDebug,显示所有级别
func TestLogger_BackwardCompatibility(t *testing.T) {
config := &LoggerConfig{
Level: LogLevel("debug"), // 旧版本可能用字符串
Level: LevelAll, // LevelAll 是 LevelDebug 的别名
EnableColor: false,
ShowProgress: false,
StartTime: time.Now(),
@@ -617,17 +604,17 @@ func TestLogger_BackwardCompatibility(t *testing.T) {
capture := &captureOutput{}
logger.SetCoordinatedOutput(capture.Write)
// 字符串"debug"应该显示所有级别
// LevelAll 应该显示所有级别
logger.Debug("debug msg")
logger.Info("info msg")
logger.Error("error msg")
output := capture.Get()
if len(output) != 3 {
t.Errorf("字符串'debug'应该显示所有级别,期望3条,实际%d条", len(output))
t.Errorf("LevelAll应该显示所有级别,期望3条,实际%d条", len(output))
}
t.Logf("✓ 向后兼容测试通过(字符串'debug'显示所有级别)")
t.Logf("✓ 向后兼容测试通过(LevelAll显示所有级别)")
}
// TestLogger_Initialize 测试初始化标记