mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
feat: 重构进度条系统并优化日志级别控制
- 实现固定底部进度条显示,与正常输出分离 - 创建ProgressManager统一管理进度条状态和渲染 - 优化日志级别过滤,默认只显示BASE、INFO、SUCCESS级别 - 修复进度条与并发日志输出的冲突问题 - 重构日志系统以支持动态级别配置和进度条协调 - 改进用户体验,提供清晰、专业的扫描进度反馈 主要改进: * 新增ProgressManager.go实现固定底部进度条 * 修复日志初始化时机,确保级别配置正确生效 * 实现日志输出与进度条的互斥显示机制 * 优化默认日志级别,过滤干扰性调试和错误信息 * 保持向后兼容性,支持用户自定义日志级别
This commit is contained in:
parent
a00e7a735a
commit
4101ccc91a
169
Common/Bridge.go
169
Common/Bridge.go
@ -62,7 +62,7 @@ var (
|
|||||||
Num, End int64
|
Num, End int64
|
||||||
StartTime = time.Now()
|
StartTime = time.Now()
|
||||||
globalLogger *logging.Logger
|
globalLogger *logging.Logger
|
||||||
loggerOnce sync.Once
|
loggerMutex sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// 日志级别常量
|
// 日志级别常量
|
||||||
@ -83,9 +83,15 @@ type ScanStatus = logging.ScanStatus
|
|||||||
type ProgressDisplay = logging.ProgressDisplay
|
type ProgressDisplay = logging.ProgressDisplay
|
||||||
|
|
||||||
func getGlobalLogger() *logging.Logger {
|
func getGlobalLogger() *logging.Logger {
|
||||||
loggerOnce.Do(func() {
|
loggerMutex.Lock()
|
||||||
|
defer loggerMutex.Unlock()
|
||||||
|
|
||||||
|
if globalLogger == nil {
|
||||||
|
// 获取正确的日志级别 - 动态获取确保使用最新值
|
||||||
|
level := getLogLevelFromString(LogLevel)
|
||||||
|
|
||||||
config := &logging.LoggerConfig{
|
config := &logging.LoggerConfig{
|
||||||
Level: logging.LevelBaseInfoSuccess, EnableColor: !NoColor, SlowOutput: SlowLogOutput,
|
Level: level, EnableColor: !NoColor, SlowOutput: SlowLogOutput,
|
||||||
ShowProgress: true, StartTime: StartTime,
|
ShowProgress: true, StartTime: StartTime,
|
||||||
LevelColors: map[logging.LogLevel]color.Attribute{
|
LevelColors: map[logging.LogLevel]color.Attribute{
|
||||||
logging.LevelError: color.FgBlue, logging.LevelBase: color.FgYellow,
|
logging.LevelError: color.FgBlue, logging.LevelBase: color.FgYellow,
|
||||||
@ -98,19 +104,162 @@ func getGlobalLogger() *logging.Logger {
|
|||||||
}
|
}
|
||||||
globalLogger.SetOutputMutex(&OutputMutex)
|
globalLogger.SetOutputMutex(&OutputMutex)
|
||||||
status = globalLogger.GetScanStatus()
|
status = globalLogger.GetScanStatus()
|
||||||
})
|
}
|
||||||
return globalLogger
|
return globalLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shouldLogLevel 检查是否应该记录该级别的日志
|
||||||
|
func shouldLogLevel(logger *logging.Logger, level logging.LogLevel) bool {
|
||||||
|
// 直接基于当前的LogLevel配置判断
|
||||||
|
switch LogLevel {
|
||||||
|
case "all", "ALL":
|
||||||
|
return true
|
||||||
|
case "error", "ERROR":
|
||||||
|
return level == logging.LevelError
|
||||||
|
case "base", "BASE":
|
||||||
|
return level == logging.LevelBase
|
||||||
|
case "info", "INFO":
|
||||||
|
return level == logging.LevelInfo
|
||||||
|
case "success", "SUCCESS":
|
||||||
|
return level == logging.LevelSuccess
|
||||||
|
case "debug", "DEBUG":
|
||||||
|
return level == logging.LevelDebug || level == logging.LevelError
|
||||||
|
case "info,success", "INFO_SUCCESS":
|
||||||
|
return level == logging.LevelInfo || level == logging.LevelSuccess
|
||||||
|
case "base,info,success", "BASE_INFO_SUCCESS":
|
||||||
|
return level == logging.LevelBase || level == logging.LevelInfo || level == logging.LevelSuccess
|
||||||
|
default:
|
||||||
|
// 默认使用BaseInfoSuccess级别,不包含DEBUG和ERROR级别
|
||||||
|
return level == logging.LevelBase || level == logging.LevelInfo || level == logging.LevelSuccess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLogLevelFromString 根据字符串获取日志级别
|
||||||
|
func getLogLevelFromString(levelStr string) logging.LogLevel {
|
||||||
|
switch levelStr {
|
||||||
|
case "all", "ALL":
|
||||||
|
return logging.LevelAll
|
||||||
|
case "error", "ERROR":
|
||||||
|
return logging.LevelError
|
||||||
|
case "base", "BASE":
|
||||||
|
return logging.LevelBase
|
||||||
|
case "info", "INFO":
|
||||||
|
return logging.LevelInfo
|
||||||
|
case "success", "SUCCESS":
|
||||||
|
return logging.LevelSuccess
|
||||||
|
case "debug", "DEBUG":
|
||||||
|
return logging.LevelDebug
|
||||||
|
case "info,success":
|
||||||
|
return logging.LevelInfoSuccess
|
||||||
|
case "base,info,success", "BASE_INFO_SUCCESS":
|
||||||
|
return logging.LevelBaseInfoSuccess
|
||||||
|
default:
|
||||||
|
// 默认使用InfoSuccess级别,只显示Info和Success消息
|
||||||
|
return logging.LevelInfoSuccess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 日志相关函数
|
// 日志相关函数
|
||||||
func InitLogger() { log.SetOutput(io.Discard); getGlobalLogger().Initialize() }
|
func InitLogger() {
|
||||||
func LogDebug(msg string) { getGlobalLogger().Debug(msg) }
|
// 重置全局logger以确保使用最新的LogLevel配置
|
||||||
func LogBase(msg string) { getGlobalLogger().Base(msg) }
|
loggerMutex.Lock()
|
||||||
func LogInfo(msg string) { getGlobalLogger().Info(msg) }
|
globalLogger = nil
|
||||||
func LogSuccess(result string) { getGlobalLogger().Success(result) }
|
loggerMutex.Unlock()
|
||||||
func LogError(errMsg string) { getGlobalLogger().Error(errMsg) }
|
|
||||||
|
log.SetOutput(io.Discard)
|
||||||
|
getGlobalLogger().Initialize()
|
||||||
|
}
|
||||||
|
func LogDebug(msg string) { logWithProgressCoordination(msg, "debug") }
|
||||||
|
func LogBase(msg string) { logWithProgressCoordination(msg, "base") }
|
||||||
|
func LogInfo(msg string) { logWithProgressCoordination(msg, "info") }
|
||||||
|
func LogSuccess(result string) { logWithProgressCoordination(result, "success") }
|
||||||
|
func LogError(errMsg string) { logWithProgressCoordination(errMsg, "error") }
|
||||||
func CheckErrs(err error) error { return logging.CheckErrs(err) }
|
func CheckErrs(err error) error { return logging.CheckErrs(err) }
|
||||||
|
|
||||||
|
// logWithProgressCoordination 协调日志输出与进度条的冲突
|
||||||
|
func logWithProgressCoordination(msg, level string) {
|
||||||
|
logger := getGlobalLogger()
|
||||||
|
|
||||||
|
// 首先检查是否应该记录这个级别的日志
|
||||||
|
var logLevel logging.LogLevel
|
||||||
|
switch level {
|
||||||
|
case "debug":
|
||||||
|
logLevel = logging.LevelDebug
|
||||||
|
case "base":
|
||||||
|
logLevel = logging.LevelBase
|
||||||
|
case "info":
|
||||||
|
logLevel = logging.LevelInfo
|
||||||
|
case "success":
|
||||||
|
logLevel = logging.LevelSuccess
|
||||||
|
case "error":
|
||||||
|
logLevel = logging.LevelError
|
||||||
|
default:
|
||||||
|
logLevel = logging.LevelDebug
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果当前日志级别不应该显示这条消息,直接返回
|
||||||
|
if !shouldLogLevel(logger, logLevel) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果进度条活跃,使用协调输出
|
||||||
|
if IsProgressActive() {
|
||||||
|
// 简单格式化消息,保持时间戳格式一致
|
||||||
|
elapsed := time.Since(StartTime)
|
||||||
|
var prefix, colorCode, resetCode string
|
||||||
|
|
||||||
|
if !NoColor {
|
||||||
|
resetCode = "\033[0m"
|
||||||
|
switch level {
|
||||||
|
case "debug":
|
||||||
|
colorCode = "\033[37m" // 白色
|
||||||
|
prefix = " "
|
||||||
|
case "base":
|
||||||
|
colorCode = "\033[33m" // 黄色
|
||||||
|
prefix = " "
|
||||||
|
case "info":
|
||||||
|
colorCode = "\033[32m" // 绿色
|
||||||
|
prefix = " [*] "
|
||||||
|
case "success":
|
||||||
|
colorCode = "\033[31m" // 红色
|
||||||
|
prefix = " [+] "
|
||||||
|
case "error":
|
||||||
|
colorCode = "\033[34m" // 蓝色
|
||||||
|
prefix = " [-] "
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch level {
|
||||||
|
case "info":
|
||||||
|
prefix = " [*] "
|
||||||
|
case "success":
|
||||||
|
prefix = " [+] "
|
||||||
|
case "error":
|
||||||
|
prefix = " [-] "
|
||||||
|
default:
|
||||||
|
prefix = " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedMsg := fmt.Sprintf("[%.1fs]%s%s%s%s",
|
||||||
|
elapsed.Seconds(), prefix, colorCode, msg, resetCode)
|
||||||
|
LogWithProgress(formattedMsg)
|
||||||
|
} else {
|
||||||
|
// 如果进度条不活跃,使用原始日志方法
|
||||||
|
switch level {
|
||||||
|
case "debug":
|
||||||
|
logger.Debug(msg)
|
||||||
|
case "base":
|
||||||
|
logger.Base(msg)
|
||||||
|
case "info":
|
||||||
|
logger.Info(msg)
|
||||||
|
case "success":
|
||||||
|
logger.Success(msg)
|
||||||
|
case "error":
|
||||||
|
logger.Error(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// 输出桥接 (Output.go 功能)
|
// 输出桥接 (Output.go 功能)
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
@ -328,7 +328,17 @@ func RegisterPlugin(name string, plugin ScanPlugin) error {
|
|||||||
Enabled: true,
|
Enabled: true,
|
||||||
ScanFunc: plugin.ScanFunc,
|
ScanFunc: plugin.ScanFunc,
|
||||||
}
|
}
|
||||||
return globalPluginManager.RegisterPlugin(newPlugin)
|
|
||||||
|
// 注册到新的插件管理器
|
||||||
|
err := globalPluginManager.RegisterPlugin(newPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同时更新Legacy管理器以保持向后兼容
|
||||||
|
LegacyPluginManager[name] = plugin
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGlobalPluginManager 获取全局插件管理器
|
// GetGlobalPluginManager 获取全局插件管理器
|
||||||
|
@ -212,7 +212,9 @@ func Flag(Info *HostInfo) {
|
|||||||
flag.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode"))
|
flag.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode"))
|
||||||
flag.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color"))
|
flag.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color"))
|
||||||
flag.StringVar(&LogLevel, "log", LogLevelBaseInfoSuccess, GetText("flag_log_level"))
|
flag.StringVar(&LogLevel, "log", LogLevelBaseInfoSuccess, GetText("flag_log_level"))
|
||||||
flag.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress"))
|
flag.BoolVar(&ShowProgress, "pg", true, GetText("flag_show_progress"))
|
||||||
|
var noProgress bool
|
||||||
|
flag.BoolVar(&noProgress, "np-bar", false, GetText("flag_no_progress"))
|
||||||
flag.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan"))
|
flag.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan"))
|
||||||
flag.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output"))
|
flag.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output"))
|
||||||
|
|
||||||
@ -232,6 +234,11 @@ func Flag(Info *HostInfo) {
|
|||||||
// 设置语言
|
// 设置语言
|
||||||
SetLanguage(Language)
|
SetLanguage(Language)
|
||||||
|
|
||||||
|
// 处理进度条禁用逻辑
|
||||||
|
if noProgress {
|
||||||
|
ShowProgress = false
|
||||||
|
}
|
||||||
|
|
||||||
// 如果显示帮助或者没有提供目标,显示帮助信息并退出
|
// 如果显示帮助或者没有提供目标,显示帮助信息并退出
|
||||||
if showHelp || shouldShowHelp(Info) {
|
if showHelp || shouldShowHelp(Info) {
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
|
337
Common/ProgressManager.go
Normal file
337
Common/ProgressManager.go
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
package Common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ProgressManager.go - 固定底部进度条管理器
|
||||||
|
|
||||||
|
提供固定在终端底部的进度条显示,与正常输出内容分离。
|
||||||
|
使用终端控制码实现位置固定和内容保护。
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ProgressManager 进度条管理器
|
||||||
|
type ProgressManager struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
enabled bool
|
||||||
|
total int64
|
||||||
|
current int64
|
||||||
|
description string
|
||||||
|
startTime time.Time
|
||||||
|
isActive bool
|
||||||
|
terminalHeight int
|
||||||
|
reservedLines int // 为进度条保留的行数
|
||||||
|
lastContentLine int // 最后一行内容的位置
|
||||||
|
|
||||||
|
// 输出缓冲相关
|
||||||
|
outputMutex sync.Mutex
|
||||||
|
pendingOutputs []string
|
||||||
|
needProgressRedraw bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalProgressManager *ProgressManager
|
||||||
|
progressMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetProgressManager 获取全局进度条管理器
|
||||||
|
func GetProgressManager() *ProgressManager {
|
||||||
|
progressMutex.Lock()
|
||||||
|
defer progressMutex.Unlock()
|
||||||
|
|
||||||
|
if globalProgressManager == nil {
|
||||||
|
globalProgressManager = &ProgressManager{
|
||||||
|
enabled: true,
|
||||||
|
reservedLines: 2, // 保留2行:进度条 + 空行
|
||||||
|
terminalHeight: getTerminalHeight(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return globalProgressManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitProgress 初始化进度条
|
||||||
|
func (pm *ProgressManager) InitProgress(total int64, description string) {
|
||||||
|
if !ShowProgress || Silent {
|
||||||
|
pm.enabled = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
|
pm.total = total
|
||||||
|
pm.current = 0
|
||||||
|
pm.description = description
|
||||||
|
pm.startTime = time.Now()
|
||||||
|
pm.isActive = true
|
||||||
|
pm.enabled = true
|
||||||
|
|
||||||
|
// 初始显示进度条
|
||||||
|
pm.renderProgress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateProgress 更新进度
|
||||||
|
func (pm *ProgressManager) UpdateProgress(increment int64) {
|
||||||
|
if !pm.enabled || !pm.isActive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
|
pm.current += increment
|
||||||
|
if pm.current > pm.total {
|
||||||
|
pm.current = pm.total
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.renderProgress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetProgress 设置当前进度
|
||||||
|
func (pm *ProgressManager) SetProgress(current int64) {
|
||||||
|
if !pm.enabled || !pm.isActive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
|
pm.current = current
|
||||||
|
if pm.current > pm.total {
|
||||||
|
pm.current = pm.total
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.renderProgress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FinishProgress 完成进度条
|
||||||
|
func (pm *ProgressManager) FinishProgress() {
|
||||||
|
if !pm.enabled || !pm.isActive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
|
pm.current = pm.total
|
||||||
|
pm.renderProgress()
|
||||||
|
|
||||||
|
// 显示完成信息
|
||||||
|
pm.showCompletionInfo()
|
||||||
|
|
||||||
|
// 清理进度条区域,恢复正常输出
|
||||||
|
pm.clearProgressArea()
|
||||||
|
pm.isActive = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserveProgressSpace 为进度条保留底部空间
|
||||||
|
func (pm *ProgressManager) reserveProgressSpace() {
|
||||||
|
if pm.terminalHeight <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动到底部保留区域上方
|
||||||
|
targetLine := pm.terminalHeight - pm.reservedLines
|
||||||
|
fmt.Printf("\033[%d;1H", targetLine)
|
||||||
|
|
||||||
|
// 在底部创建空行
|
||||||
|
for i := 0; i < pm.reservedLines; i++ {
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回到内容输出位置
|
||||||
|
fmt.Printf("\033[%d;1H", targetLine)
|
||||||
|
pm.lastContentLine = targetLine - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderProgress 渲染进度条(使用锁避免输出冲突)
|
||||||
|
func (pm *ProgressManager) renderProgress() {
|
||||||
|
pm.outputMutex.Lock()
|
||||||
|
defer pm.outputMutex.Unlock()
|
||||||
|
|
||||||
|
// 清除当前行并回到行首
|
||||||
|
fmt.Print("\033[2K\r")
|
||||||
|
|
||||||
|
pm.renderProgressUnsafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateProgressBar 生成进度条字符串
|
||||||
|
func (pm *ProgressManager) generateProgressBar() string {
|
||||||
|
if pm.total == 0 {
|
||||||
|
return fmt.Sprintf("%s: 等待中...", pm.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
percentage := float64(pm.current) / float64(pm.total) * 100
|
||||||
|
elapsed := time.Since(pm.startTime)
|
||||||
|
|
||||||
|
// 计算预估剩余时间
|
||||||
|
var eta string
|
||||||
|
if pm.current > 0 {
|
||||||
|
totalTime := elapsed * time.Duration(pm.total) / time.Duration(pm.current)
|
||||||
|
remaining := totalTime - elapsed
|
||||||
|
if remaining > 0 {
|
||||||
|
eta = fmt.Sprintf(" ETA:%s", formatDuration(remaining))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算速度
|
||||||
|
speed := float64(pm.current) / elapsed.Seconds()
|
||||||
|
speedStr := ""
|
||||||
|
if speed > 0 {
|
||||||
|
speedStr = fmt.Sprintf(" (%.1f/s)", speed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成进度条
|
||||||
|
barWidth := 30
|
||||||
|
filled := int(percentage * float64(barWidth) / 100)
|
||||||
|
bar := ""
|
||||||
|
|
||||||
|
if NoColor {
|
||||||
|
// 无颜色版本
|
||||||
|
bar = "[" +
|
||||||
|
fmt.Sprintf("%s%s",
|
||||||
|
string(make([]rune, filled)),
|
||||||
|
string(make([]rune, barWidth-filled))) +
|
||||||
|
"]"
|
||||||
|
for i := 0; i < filled; i++ {
|
||||||
|
bar = bar[:i+1] + "=" + bar[i+2:]
|
||||||
|
}
|
||||||
|
for i := filled; i < barWidth; i++ {
|
||||||
|
bar = bar[:i+1] + "-" + bar[i+2:]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 彩色版本
|
||||||
|
bar = "│"
|
||||||
|
for i := 0; i < barWidth; i++ {
|
||||||
|
if i < filled {
|
||||||
|
bar += "█"
|
||||||
|
} else {
|
||||||
|
bar += "░"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bar += "│"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s %6.1f%% %s (%d/%d)%s%s",
|
||||||
|
pm.description, percentage, bar, pm.current, pm.total, speedStr, eta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// showCompletionInfo 显示完成信息
|
||||||
|
func (pm *ProgressManager) showCompletionInfo() {
|
||||||
|
elapsed := time.Since(pm.startTime)
|
||||||
|
|
||||||
|
// 换行并显示完成信息
|
||||||
|
fmt.Print("\n")
|
||||||
|
|
||||||
|
completionMsg := GetText("progress_scan_completed")
|
||||||
|
if NoColor {
|
||||||
|
fmt.Printf("✓ %s %d/%d (耗时: %s)\n",
|
||||||
|
completionMsg, pm.total, pm.total, formatDuration(elapsed))
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\033[32m✓ %s %d/%d\033[0m \033[90m(耗时: %s)\033[0m\n",
|
||||||
|
completionMsg, pm.total, pm.total, formatDuration(elapsed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearProgressArea 清理进度条区域
|
||||||
|
func (pm *ProgressManager) clearProgressArea() {
|
||||||
|
// 简单清除当前行
|
||||||
|
fmt.Print("\033[2K\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsActive 检查进度条是否活跃
|
||||||
|
func (pm *ProgressManager) IsActive() bool {
|
||||||
|
pm.mu.RLock()
|
||||||
|
defer pm.mu.RUnlock()
|
||||||
|
return pm.isActive && pm.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTerminalHeight 获取终端高度
|
||||||
|
func getTerminalHeight() int {
|
||||||
|
// 对于固定底部进度条,我们暂时禁用终端高度检测
|
||||||
|
// 因为在不同终端环境中可能会有问题
|
||||||
|
// 改为使用相对定位方式
|
||||||
|
return 0 // 返回0表示使用简化模式
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatDuration 格式化时间间隔
|
||||||
|
func formatDuration(d time.Duration) string {
|
||||||
|
if d < time.Minute {
|
||||||
|
return fmt.Sprintf("%.1fs", d.Seconds())
|
||||||
|
} else if d < time.Hour {
|
||||||
|
return fmt.Sprintf("%.1fm", d.Minutes())
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%.1fh", d.Hours())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全局函数,方便其他模块调用
|
||||||
|
func InitProgressBar(total int64, description string) {
|
||||||
|
GetProgressManager().InitProgress(total, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateProgressBar(increment int64) {
|
||||||
|
GetProgressManager().UpdateProgress(increment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetProgressBar(current int64) {
|
||||||
|
GetProgressManager().SetProgress(current)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FinishProgressBar() {
|
||||||
|
GetProgressManager().FinishProgress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsProgressActive() bool {
|
||||||
|
return GetProgressManager().IsActive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 日志输出协调功能
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// LogWithProgress 在进度条活跃时协调日志输出
|
||||||
|
func LogWithProgress(message string) {
|
||||||
|
pm := GetProgressManager()
|
||||||
|
if !pm.IsActive() {
|
||||||
|
// 如果进度条不活跃,直接输出
|
||||||
|
fmt.Println(message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pm.outputMutex.Lock()
|
||||||
|
defer pm.outputMutex.Unlock()
|
||||||
|
|
||||||
|
// 清除当前进度条
|
||||||
|
fmt.Print("\033[2K\r")
|
||||||
|
|
||||||
|
// 输出消息
|
||||||
|
fmt.Println(message)
|
||||||
|
|
||||||
|
// 重新绘制进度条
|
||||||
|
pm.renderProgressUnsafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderProgressUnsafe 不加锁的进度条渲染(内部使用)
|
||||||
|
func (pm *ProgressManager) renderProgressUnsafe() {
|
||||||
|
if !pm.enabled || !pm.isActive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成进度条内容
|
||||||
|
progressBar := pm.generateProgressBar()
|
||||||
|
|
||||||
|
// 输出进度条(带颜色,如果启用)
|
||||||
|
if NoColor {
|
||||||
|
fmt.Print(progressBar)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\033[36m%s\033[0m", progressBar) // 青色
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新输出
|
||||||
|
os.Stdout.Sync()
|
||||||
|
}
|
@ -37,7 +37,10 @@ const (
|
|||||||
|
|
||||||
// RegisterPlugin 注册插件到全局管理器 - 保持向后兼容
|
// RegisterPlugin 注册插件到全局管理器 - 保持向后兼容
|
||||||
func RegisterPlugin(name string, plugin ScanPlugin) {
|
func RegisterPlugin(name string, plugin ScanPlugin) {
|
||||||
Core.RegisterPlugin(name, plugin)
|
if err := Core.RegisterPlugin(name, plugin); err != nil {
|
||||||
|
// 注册失败时记录错误,但不中断程序
|
||||||
|
LogError("Failed to register plugin " + name + ": " + err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGlobalPluginManager 获取全局插件管理器
|
// GetGlobalPluginManager 获取全局插件管理器
|
||||||
|
@ -709,8 +709,12 @@ var coreMessages = map[string]map[string]string{
|
|||||||
LangEN: "Log level",
|
LangEN: "Log level",
|
||||||
},
|
},
|
||||||
"flag_show_progress": {
|
"flag_show_progress": {
|
||||||
LangZH: "显示进度",
|
LangZH: "显示进度条 (默认启用)",
|
||||||
LangEN: "Show progress",
|
LangEN: "Show progress bar (enabled by default)",
|
||||||
|
},
|
||||||
|
"flag_no_progress": {
|
||||||
|
LangZH: "禁用进度条",
|
||||||
|
LangEN: "Disable progress bar",
|
||||||
},
|
},
|
||||||
"flag_show_scan_plan": {
|
"flag_show_scan_plan": {
|
||||||
LangZH: "显示扫描计划",
|
LangZH: "显示扫描计划",
|
||||||
@ -736,6 +740,28 @@ var coreMessages = map[string]map[string]string{
|
|||||||
LangZH: "显示版本信息",
|
LangZH: "显示版本信息",
|
||||||
LangEN: "Show version information",
|
LangEN: "Show version information",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ========================= 进度条消息 =========================
|
||||||
|
"progress_scanning_description": {
|
||||||
|
LangZH: "扫描进度",
|
||||||
|
LangEN: "Scanning Progress",
|
||||||
|
},
|
||||||
|
"progress_port_scanning": {
|
||||||
|
LangZH: "端口扫描",
|
||||||
|
LangEN: "Port Scanning",
|
||||||
|
},
|
||||||
|
"progress_scan_completed": {
|
||||||
|
LangZH: "✅ 扫描完成:",
|
||||||
|
LangEN: "✅ Scan Completed:",
|
||||||
|
},
|
||||||
|
"progress_port_scan_completed": {
|
||||||
|
LangZH: "🔍 端口扫描完成:",
|
||||||
|
LangEN: "🔍 Port Scan Completed:",
|
||||||
|
},
|
||||||
|
"progress_open_ports": {
|
||||||
|
LangZH: "开放端口",
|
||||||
|
LangEN: "Open Ports",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
@ -27,6 +27,22 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
|
|||||||
exclude[p] = struct{}{}
|
exclude[p] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算总扫描数量
|
||||||
|
totalTasks := 0
|
||||||
|
for range hosts {
|
||||||
|
for _, port := range portList {
|
||||||
|
if _, excluded := exclude[port]; !excluded {
|
||||||
|
totalTasks++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化端口扫描进度条
|
||||||
|
if totalTasks > 0 && Common.ShowProgress {
|
||||||
|
description := Common.GetText("progress_port_scanning")
|
||||||
|
Common.InitProgressBar(int64(totalTasks), description)
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化并发控制
|
// 初始化并发控制
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -51,7 +67,11 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
defer sem.Release(1)
|
defer func() {
|
||||||
|
sem.Release(1)
|
||||||
|
// 更新端口扫描进度
|
||||||
|
Common.UpdateProgressBar(1)
|
||||||
|
}()
|
||||||
|
|
||||||
// 连接测试 - 支持SOCKS5代理
|
// 连接测试 - 支持SOCKS5代理
|
||||||
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, to)
|
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, to)
|
||||||
@ -146,6 +166,12 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 完成端口扫描进度条
|
||||||
|
if Common.IsProgressActive() {
|
||||||
|
Common.FinishProgressBar()
|
||||||
|
}
|
||||||
|
|
||||||
Common.LogBase(Common.GetText("scan_complete_ports_found", count))
|
Common.LogBase(Common.GetText("scan_complete_ports_found", count))
|
||||||
return aliveAddrs
|
return aliveAddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package Core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/schollz/progressbar/v3"
|
|
||||||
"github.com/shadow1ng/fscan/Common"
|
"github.com/shadow1ng/fscan/Common"
|
||||||
"github.com/shadow1ng/fscan/WebScan/lib"
|
"github.com/shadow1ng/fscan/WebScan/lib"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -82,10 +81,12 @@ func (s *Scanner) Scan(info Common.HostInfo) {
|
|||||||
|
|
||||||
// finishScan 完成扫描并输出结果
|
// finishScan 完成扫描并输出结果
|
||||||
func (s *Scanner) finishScan() {
|
func (s *Scanner) finishScan() {
|
||||||
if Common.ProgressBar != nil {
|
// 确保进度条正确完成
|
||||||
Common.ProgressBar.Finish()
|
if Common.IsProgressActive() {
|
||||||
fmt.Println()
|
Common.FinishProgressBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 输出扫描完成信息
|
||||||
Common.LogBase(Common.GetText("scan_task_complete", Common.End, Common.Num))
|
Common.LogBase(Common.GetText("scan_task_complete", Common.End, Common.Num))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +105,8 @@ func ExecuteScanTasks(targets []Common.HostInfo, strategy ScanStrategy, ch *chan
|
|||||||
|
|
||||||
// 初始化进度条
|
// 初始化进度条
|
||||||
if len(tasks) > 0 && Common.ShowProgress {
|
if len(tasks) > 0 && Common.ShowProgress {
|
||||||
initProgressBar(len(tasks))
|
description := Common.GetText("progress_scanning_description")
|
||||||
|
Common.InitProgressBar(int64(len(tasks)), description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行所有任务
|
// 执行所有任务
|
||||||
@ -161,26 +163,6 @@ func logScanPlan(tasks []ScanTask) {
|
|||||||
Common.LogBase(planInfo.String())
|
Common.LogBase(planInfo.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化进度条
|
|
||||||
func initProgressBar(totalTasks int) {
|
|
||||||
Common.ProgressBar = progressbar.NewOptions(totalTasks,
|
|
||||||
progressbar.OptionEnableColorCodes(true),
|
|
||||||
progressbar.OptionShowCount(),
|
|
||||||
progressbar.OptionSetWidth(15),
|
|
||||||
progressbar.OptionSetDescription("[cyan]扫描进度:[reset]"),
|
|
||||||
progressbar.OptionSetTheme(progressbar.Theme{
|
|
||||||
Saucer: "[green]=[reset]",
|
|
||||||
SaucerHead: "[green]>[reset]",
|
|
||||||
SaucerPadding: " ",
|
|
||||||
BarStart: "[",
|
|
||||||
BarEnd: "]",
|
|
||||||
}),
|
|
||||||
progressbar.OptionThrottle(65*time.Millisecond),
|
|
||||||
progressbar.OptionUseANSICodes(true),
|
|
||||||
progressbar.OptionSetRenderBlankState(true),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调度单个扫描任务
|
// 调度单个扫描任务
|
||||||
func scheduleScanTask(pluginName string, target Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
|
func scheduleScanTask(pluginName string, target Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
@ -209,7 +191,7 @@ func scheduleScanTask(pluginName string, target Common.HostInfo, ch *chan struct
|
|||||||
|
|
||||||
atomic.AddInt64(&Common.Num, 1)
|
atomic.AddInt64(&Common.Num, 1)
|
||||||
executeSingleScan(pluginName, target)
|
executeSingleScan(pluginName, target)
|
||||||
updateProgress()
|
Common.UpdateProgressBar(1)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,18 +208,6 @@ func executeSingleScan(pluginName string, info Common.HostInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新扫描进度
|
|
||||||
func updateProgress() {
|
|
||||||
Common.OutputMutex.Lock()
|
|
||||||
defer Common.OutputMutex.Unlock()
|
|
||||||
|
|
||||||
atomic.AddInt64(&Common.End, 1)
|
|
||||||
|
|
||||||
if Common.ProgressBar != nil {
|
|
||||||
fmt.Print("\033[2K\r")
|
|
||||||
Common.ProgressBar.Add(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 入口函数,向后兼容旧的调用方式
|
// 入口函数,向后兼容旧的调用方式
|
||||||
func Scan(info Common.HostInfo) {
|
func Scan(info Common.HostInfo) {
|
||||||
|
5
main.go
5
main.go
@ -9,11 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
Common.InitLogger()
|
|
||||||
|
|
||||||
var Info Common.HostInfo
|
var Info Common.HostInfo
|
||||||
Common.Flag(&Info)
|
Common.Flag(&Info)
|
||||||
|
|
||||||
|
// 在flag解析后初始化logger,确保LogLevel参数生效
|
||||||
|
Common.InitLogger()
|
||||||
|
|
||||||
// 解析 CLI 参数
|
// 解析 CLI 参数
|
||||||
if err := Common.Parse(&Info); err != nil {
|
if err := Common.Parse(&Info); err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
Loading…
Reference in New Issue
Block a user