From 22039d3d21206225820b0b1a3bccadd4df40e167 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Thu, 7 May 2026 06:39:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20-silent=20=E6=A8=A1=E5=BC=8F=E8=BE=93?= =?UTF-8?q?=E5=87=BA=20NDJSON=20=E5=88=B0=20stdout=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20AI=20agent=20=E7=AE=A1=E9=81=93=E6=B6=88=E8=B4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 StdoutNDJSONWriter,silent 模式下每条扫描结果实时输出一行 JSON - LogWithProgress 层拦截人类可读日志,绕过 logger sync.Once 初始化时序问题 - 支持 fscan -h xxx -silent | jq 管道用法 --- common/logger.go | 1 + common/logging/logger.go | 5 +++++ common/output/stdout_writer.go | 40 ++++++++++++++++++++++++++++++++++ common/output_api.go | 16 ++++++++++++++ common/progress_manager.go | 4 ++++ 5 files changed, 66 insertions(+) create mode 100644 common/output/stdout_writer.go diff --git a/common/logger.go b/common/logger.go index 0bd0307..7f8568d 100644 --- a/common/logger.go +++ b/common/logger.go @@ -27,6 +27,7 @@ func getGlobalLogger() *logging.Logger { EnableColor: !fv.NoColor, SlowOutput: false, ShowProgress: !fv.DisableProgress, + Silent: fv.Silent, StartTime: GetGlobalState().GetStartTime(), } globalLogger = logging.NewLogger(config) diff --git a/common/logging/logger.go b/common/logging/logger.go index c0a5dc0..24611ed 100644 --- a/common/logging/logger.go +++ b/common/logging/logger.go @@ -24,6 +24,7 @@ type LoggerConfig struct { EnableColor bool `json:"enable_color"` SlowOutput bool `json:"slow_output"` ShowProgress bool `json:"show_progress"` + Silent bool `json:"silent"` StartTime time.Time `json:"start_time"` LevelColors map[LogLevel]interface{} `json:"-"` } @@ -111,6 +112,10 @@ func (l *Logger) log(level LogLevel, content string) { l.mu.Lock() defer l.mu.Unlock() + if l.config.Silent { + return + } + if !l.shouldLog(level) { return } diff --git a/common/output/stdout_writer.go b/common/output/stdout_writer.go new file mode 100644 index 0000000..a48c235 --- /dev/null +++ b/common/output/stdout_writer.go @@ -0,0 +1,40 @@ +package output + +import ( + "bufio" + "encoding/json" + "os" + "sync" +) + +type StdoutNDJSONWriter struct { + mu sync.Mutex + writer *bufio.Writer +} + +func NewStdoutNDJSONWriter() *StdoutNDJSONWriter { + return &StdoutNDJSONWriter{ + writer: bufio.NewWriter(os.Stdout), + } +} + +func (w *StdoutNDJSONWriter) WriteResult(result *ScanResult) error { + w.mu.Lock() + defer w.mu.Unlock() + + data, err := json.Marshal(result) + if err != nil { + return err + } + data = append(data, '\n') + if _, err := w.writer.Write(data); err != nil { + return err + } + return w.writer.Flush() +} + +func (w *StdoutNDJSONWriter) Close() error { + w.mu.Lock() + defer w.mu.Unlock() + return w.writer.Flush() +} diff --git a/common/output_api.go b/common/output_api.go index b3424c6..a523904 100644 --- a/common/output_api.go +++ b/common/output_api.go @@ -15,10 +15,18 @@ import ( // ResultOutput 全局输出管理器 var ResultOutput *output.Manager +// StdoutWriter silent模式下的NDJSON stdout写入器 +var StdoutWriter *output.StdoutNDJSONWriter + // InitOutput 初始化输出系统 func InitOutput() error { fv := GetFlagVars() + // silent模式:初始化NDJSON stdout写入器(独立于文件输出) + if fv.Silent { + StdoutWriter = output.NewStdoutNDJSONWriter() + } + // 用户通过-no flag禁用保存时,跳过文件初始化避免不必要的资源开销 if fv.DisableSave { return nil @@ -59,6 +67,9 @@ func InitOutput() error { // CloseOutput 关闭输出系统 func CloseOutput() error { + if StdoutWriter != nil { + _ = StdoutWriter.Close() + } if ResultOutput == nil { return nil } @@ -80,6 +91,11 @@ func SaveResult(result *output.ScanResult) error { "details": result.Details, }) + // silent模式:NDJSON实时输出到stdout + if StdoutWriter != nil { + _ = StdoutWriter.WriteResult(result) + } + // 用户禁用保存或输出未初始化时,跳过文件保存 if GetGlobalConfig().Output.DisableSave || ResultOutput == nil { return nil diff --git a/common/progress_manager.go b/common/progress_manager.go index 0009db8..1df00f1 100644 --- a/common/progress_manager.go +++ b/common/progress_manager.go @@ -479,6 +479,10 @@ func (pm *ProgressManager) GetPercent() float64 { // LogWithProgress 在进度条活跃时协调日志输出 func LogWithProgress(message string) { + if cfg := GetGlobalConfig(); cfg != nil && cfg.Output.Silent { + return + } + pm := GetProgressManager() if !pm.IsActive() { // 如果进度条不活跃,直接输出