mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-26 05:01:53 +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,
|
EnableColor: !fv.NoColor,
|
||||||
SlowOutput: false,
|
SlowOutput: false,
|
||||||
ShowProgress: !fv.DisableProgress,
|
ShowProgress: !fv.DisableProgress,
|
||||||
|
Silent: fv.Silent,
|
||||||
StartTime: GetGlobalState().GetStartTime(),
|
StartTime: GetGlobalState().GetStartTime(),
|
||||||
}
|
}
|
||||||
globalLogger = logging.NewLogger(config)
|
globalLogger = logging.NewLogger(config)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type LoggerConfig struct {
|
|||||||
EnableColor bool `json:"enable_color"`
|
EnableColor bool `json:"enable_color"`
|
||||||
SlowOutput bool `json:"slow_output"`
|
SlowOutput bool `json:"slow_output"`
|
||||||
ShowProgress bool `json:"show_progress"`
|
ShowProgress bool `json:"show_progress"`
|
||||||
|
Silent bool `json:"silent"`
|
||||||
StartTime time.Time `json:"start_time"`
|
StartTime time.Time `json:"start_time"`
|
||||||
LevelColors map[LogLevel]interface{} `json:"-"`
|
LevelColors map[LogLevel]interface{} `json:"-"`
|
||||||
}
|
}
|
||||||
@@ -111,6 +112,10 @@ func (l *Logger) log(level LogLevel, content string) {
|
|||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
|
if l.config.Silent {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !l.shouldLog(level) {
|
if !l.shouldLog(level) {
|
||||||
return
|
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 全局输出管理器
|
// ResultOutput 全局输出管理器
|
||||||
var ResultOutput *output.Manager
|
var ResultOutput *output.Manager
|
||||||
|
|
||||||
|
// StdoutWriter silent模式下的NDJSON stdout写入器
|
||||||
|
var StdoutWriter *output.StdoutNDJSONWriter
|
||||||
|
|
||||||
// InitOutput 初始化输出系统
|
// InitOutput 初始化输出系统
|
||||||
func InitOutput() error {
|
func InitOutput() error {
|
||||||
fv := GetFlagVars()
|
fv := GetFlagVars()
|
||||||
|
|
||||||
|
// silent模式:初始化NDJSON stdout写入器(独立于文件输出)
|
||||||
|
if fv.Silent {
|
||||||
|
StdoutWriter = output.NewStdoutNDJSONWriter()
|
||||||
|
}
|
||||||
|
|
||||||
// 用户通过-no flag禁用保存时,跳过文件初始化避免不必要的资源开销
|
// 用户通过-no flag禁用保存时,跳过文件初始化避免不必要的资源开销
|
||||||
if fv.DisableSave {
|
if fv.DisableSave {
|
||||||
return nil
|
return nil
|
||||||
@@ -59,6 +67,9 @@ func InitOutput() error {
|
|||||||
|
|
||||||
// CloseOutput 关闭输出系统
|
// CloseOutput 关闭输出系统
|
||||||
func CloseOutput() error {
|
func CloseOutput() error {
|
||||||
|
if StdoutWriter != nil {
|
||||||
|
_ = StdoutWriter.Close()
|
||||||
|
}
|
||||||
if ResultOutput == nil {
|
if ResultOutput == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -80,6 +91,11 @@ func SaveResult(result *output.ScanResult) error {
|
|||||||
"details": result.Details,
|
"details": result.Details,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// silent模式:NDJSON实时输出到stdout
|
||||||
|
if StdoutWriter != nil {
|
||||||
|
_ = StdoutWriter.WriteResult(result)
|
||||||
|
}
|
||||||
|
|
||||||
// 用户禁用保存或输出未初始化时,跳过文件保存
|
// 用户禁用保存或输出未初始化时,跳过文件保存
|
||||||
if GetGlobalConfig().Output.DisableSave || ResultOutput == nil {
|
if GetGlobalConfig().Output.DisableSave || ResultOutput == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -479,6 +479,10 @@ func (pm *ProgressManager) GetPercent() float64 {
|
|||||||
|
|
||||||
// LogWithProgress 在进度条活跃时协调日志输出
|
// LogWithProgress 在进度条活跃时协调日志输出
|
||||||
func LogWithProgress(message string) {
|
func LogWithProgress(message string) {
|
||||||
|
if cfg := GetGlobalConfig(); cfg != nil && cfg.Output.Silent {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
pm := GetProgressManager()
|
pm := GetProgressManager()
|
||||||
if !pm.IsActive() {
|
if !pm.IsActive() {
|
||||||
// 如果进度条不活跃,直接输出
|
// 如果进度条不活跃,直接输出
|
||||||
|
|||||||
Reference in New Issue
Block a user