mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
feat: -silent 模式输出 NDJSON 到 stdout,支持 AI agent 管道消费
- 新增 StdoutNDJSONWriter,silent 模式下每条扫描结果实时输出一行 JSON - LogWithProgress 层拦截人类可读日志,绕过 logger sync.Once 初始化时序问题 - 支持 fscan -h xxx -silent | jq 管道用法
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
// 如果进度条不活跃,直接输出
|
||||
|
||||
Reference in New Issue
Block a user