mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-02-09 02:09:17 +08:00
feat: 分离结果输出和日志
This commit is contained in:
@@ -847,7 +847,10 @@ var PortMap = map[int][]string{
|
||||
|
||||
var Passwords = []string{"123456", "admin", "admin123", "root", "", "pass123", "pass@123", "password", "Password", "P@ssword123", "123123", "654321", "111111", "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", "1q2w3e", "Charge123", "Aa123456789", "elastic123"}
|
||||
|
||||
var Outputfile = "result.txt"
|
||||
var (
|
||||
Outputfile string // 输出文件路径
|
||||
OutputFormat string // 输出格式
|
||||
)
|
||||
|
||||
// 添加一个全局的进度条变量
|
||||
var ProgressBar *progressbar.ProgressBar
|
||||
|
||||
@@ -160,6 +160,7 @@ func Flag(Info *HostInfo) {
|
||||
|
||||
// 输出配置
|
||||
flag.StringVar(&Outputfile, "o", "result.txt", "指定结果输出文件名")
|
||||
flag.StringVar(&OutputFormat, "f", "txt", "指定输出格式 (txt/json/csv)")
|
||||
flag.BoolVar(&DisableSave, "no", false, "禁止保存扫描结果")
|
||||
flag.BoolVar(&Silent, "silent", false, "启用静默扫描模式(减少屏幕输出)")
|
||||
flag.BoolVar(&NoColor, "nocolor", false, "禁用彩色输出显示")
|
||||
|
||||
276
Common/Log.go
276
Common/Log.go
@@ -1,12 +1,9 @@
|
||||
package Common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -16,118 +13,123 @@ import (
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// 全局变量定义
|
||||
var (
|
||||
// 全局变量
|
||||
// 扫描状态管理器,记录最近一次成功和错误的时间
|
||||
status = &ScanStatus{lastSuccess: time.Now(), lastError: time.Now()}
|
||||
|
||||
// 扫描计数
|
||||
Num int64 // 总任务数
|
||||
End int64 // 已完成任务数
|
||||
|
||||
// 文件写入器
|
||||
fileWriter *bufferedFileWriter
|
||||
// Num 表示待处理的总任务数量
|
||||
Num int64
|
||||
// End 表示已经完成的任务数量
|
||||
End int64
|
||||
)
|
||||
|
||||
// ScanStatus 记录扫描状态
|
||||
// ScanStatus 用于记录和管理扫描状态的结构体
|
||||
type ScanStatus struct {
|
||||
mu sync.RWMutex
|
||||
total int64
|
||||
completed int64
|
||||
lastSuccess time.Time
|
||||
lastError time.Time
|
||||
mu sync.RWMutex // 读写互斥锁,用于保护并发访问
|
||||
total int64 // 总任务数
|
||||
completed int64 // 已完成任务数
|
||||
lastSuccess time.Time // 最近一次成功的时间
|
||||
lastError time.Time // 最近一次错误的时间
|
||||
}
|
||||
|
||||
// LogEntry 日志条目
|
||||
// LogEntry 定义单条日志的结构
|
||||
type LogEntry struct {
|
||||
Level string // "ERROR", "INFO", "SUCCESS", "DEBUG"
|
||||
Time time.Time
|
||||
Content string
|
||||
Level string // 日志级别: ERROR/INFO/SUCCESS/DEBUG
|
||||
Time time.Time // 日志时间
|
||||
Content string // 日志内容
|
||||
}
|
||||
|
||||
// LogLevel 定义日志等级常量
|
||||
// 定义系统支持的日志级别常量
|
||||
const (
|
||||
LogLevelAll = "ALL" // 输出所有日志
|
||||
LogLevelError = "ERROR" // 错误日志
|
||||
LogLevelInfo = "INFO" // 信息日志
|
||||
LogLevelSuccess = "SUCCESS" // 成功日志
|
||||
LogLevelDebug = "DEBUG" // 调试日志
|
||||
LogLevelAll = "ALL" // 显示所有级别日志
|
||||
LogLevelError = "ERROR" // 仅显示错误日志
|
||||
LogLevelInfo = "INFO" // 仅显示信息日志
|
||||
LogLevelSuccess = "SUCCESS" // 仅显示成功日志
|
||||
LogLevelDebug = "DEBUG" // 仅显示调试日志
|
||||
)
|
||||
|
||||
// 定义日志颜色映射
|
||||
// 日志级别对应的显示颜色映射
|
||||
var logColors = map[string]color.Attribute{
|
||||
LogLevelError: color.FgRed,
|
||||
LogLevelInfo: color.FgYellow,
|
||||
LogLevelSuccess: color.FgGreen,
|
||||
LogLevelDebug: color.FgBlue,
|
||||
}
|
||||
|
||||
// JsonOutput JSON输出的结构体
|
||||
type JsonOutput struct {
|
||||
Level string `json:"level"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Message string `json:"message"`
|
||||
LogLevelError: color.FgRed, // 错误日志显示红色
|
||||
LogLevelInfo: color.FgYellow, // 信息日志显示黄色
|
||||
LogLevelSuccess: color.FgGreen, // 成功日志显示绿色
|
||||
LogLevelDebug: color.FgBlue, // 调试日志显示蓝色
|
||||
}
|
||||
|
||||
// InitLogger 初始化日志系统
|
||||
func InitLogger() {
|
||||
// 禁用标准日志输出
|
||||
log.SetOutput(io.Discard)
|
||||
if !DisableSave {
|
||||
fileWriter = newBufferedFileWriter()
|
||||
}
|
||||
}
|
||||
|
||||
// formatLogMessage 格式化日志消息
|
||||
// formatLogMessage 格式化日志消息为标准格式
|
||||
// 返回格式:[时间] [级别] 内容
|
||||
func formatLogMessage(entry *LogEntry) string {
|
||||
timeStr := entry.Time.Format("2006-01-02 15:04:05")
|
||||
return fmt.Sprintf("[%s] [%s] %s", timeStr, entry.Level, entry.Content)
|
||||
}
|
||||
|
||||
// 修改 printLog 函数
|
||||
// printLog 根据日志级别打印日志
|
||||
func printLog(entry *LogEntry) {
|
||||
// 默认情况(LogLevelInfo)下打印 INFO、SUCCESS、ERROR
|
||||
if LogLevel == LogLevelInfo {
|
||||
// 根据当前设置的日志级别过滤日志
|
||||
switch LogLevel {
|
||||
case LogLevelInfo:
|
||||
// INFO模式下只打印 INFO、SUCCESS、ERROR 级别的日志
|
||||
if entry.Level != LogLevelInfo &&
|
||||
entry.Level != LogLevelSuccess &&
|
||||
entry.Level != LogLevelError {
|
||||
return
|
||||
}
|
||||
} else if LogLevel == LogLevelDebug || LogLevel == LogLevelAll {
|
||||
// Debug或ALL模式打印所有日志
|
||||
} else if entry.Level != LogLevel {
|
||||
// 其他情况只打印指定等级的日志
|
||||
return
|
||||
case LogLevelDebug, LogLevelAll:
|
||||
// Debug或ALL模式下打印所有日志
|
||||
default:
|
||||
// 其他模式下只打印指定级别的日志
|
||||
if entry.Level != LogLevel {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
OutputMutex.Lock()
|
||||
defer OutputMutex.Unlock()
|
||||
|
||||
// 确保清除当前进度条
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.Clear()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// 处理进度条
|
||||
clearAndWaitProgress()
|
||||
|
||||
// 打印日志
|
||||
// 打印日志消息
|
||||
logMsg := formatLogMessage(entry)
|
||||
if !NoColor {
|
||||
// 使用彩色输出
|
||||
if colorAttr, ok := logColors[entry.Level]; ok {
|
||||
color.New(colorAttr).Println(logMsg)
|
||||
} else {
|
||||
fmt.Println(logMsg)
|
||||
}
|
||||
} else {
|
||||
// 普通输出
|
||||
fmt.Println(logMsg)
|
||||
}
|
||||
|
||||
// 确保日志完全输出
|
||||
// 等待日志输出完成
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// 重新渲染进度条
|
||||
// 重新显示进度条
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.RenderBlank()
|
||||
}
|
||||
}
|
||||
|
||||
// clearAndWaitProgress 清除进度条并等待
|
||||
func clearAndWaitProgress() {
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.Clear()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// LogError 记录错误日志,自动包含文件名和行号信息
|
||||
func LogError(errMsg string) {
|
||||
// 获取调用者的文件名和行号
|
||||
_, file, line, ok := runtime.Caller(1)
|
||||
if !ok {
|
||||
file = "unknown"
|
||||
@@ -137,182 +139,60 @@ func LogError(errMsg string) {
|
||||
|
||||
errorMsg := fmt.Sprintf("%s:%d - %s", file, line, errMsg)
|
||||
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.Clear()
|
||||
}
|
||||
|
||||
entry := &LogEntry{
|
||||
Level: LogLevelError,
|
||||
Time: time.Now(),
|
||||
Content: errorMsg,
|
||||
}
|
||||
|
||||
printLog(entry)
|
||||
if fileWriter != nil {
|
||||
fileWriter.write(entry)
|
||||
handleLog(entry)
|
||||
}
|
||||
|
||||
// handleLog 统一处理日志的输出
|
||||
func handleLog(entry *LogEntry) {
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.Clear()
|
||||
}
|
||||
|
||||
printLog(entry)
|
||||
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.RenderBlank()
|
||||
}
|
||||
}
|
||||
|
||||
// LogInfo 记录信息日志
|
||||
func LogInfo(msg string) {
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.Clear()
|
||||
}
|
||||
|
||||
entry := &LogEntry{
|
||||
handleLog(&LogEntry{
|
||||
Level: LogLevelInfo,
|
||||
Time: time.Now(),
|
||||
Content: msg,
|
||||
}
|
||||
|
||||
printLog(entry)
|
||||
if fileWriter != nil {
|
||||
fileWriter.write(entry)
|
||||
}
|
||||
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.RenderBlank()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// LogSuccess 记录成功日志,并更新最后成功时间
|
||||
func LogSuccess(result string) {
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.Clear()
|
||||
}
|
||||
|
||||
entry := &LogEntry{
|
||||
Level: LogLevelSuccess,
|
||||
Time: time.Now(),
|
||||
Content: result,
|
||||
}
|
||||
|
||||
printLog(entry)
|
||||
if fileWriter != nil {
|
||||
fileWriter.write(entry)
|
||||
}
|
||||
handleLog(entry)
|
||||
|
||||
// 更新最后成功时间
|
||||
status.mu.Lock()
|
||||
status.lastSuccess = time.Now()
|
||||
status.mu.Unlock()
|
||||
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.RenderBlank()
|
||||
}
|
||||
}
|
||||
|
||||
// LogDebug 记录调试日志
|
||||
func LogDebug(msg string) {
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.Clear()
|
||||
}
|
||||
|
||||
entry := &LogEntry{
|
||||
handleLog(&LogEntry{
|
||||
Level: LogLevelDebug,
|
||||
Time: time.Now(),
|
||||
Content: msg,
|
||||
}
|
||||
|
||||
printLog(entry)
|
||||
if fileWriter != nil {
|
||||
fileWriter.write(entry)
|
||||
}
|
||||
|
||||
if ProgressBar != nil {
|
||||
ProgressBar.RenderBlank()
|
||||
}
|
||||
}
|
||||
|
||||
func newBufferedFileWriter() *bufferedFileWriter {
|
||||
file, err := os.OpenFile(Outputfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] 打开输出文件失败 %s: %v\n", Outputfile, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
writer := bufio.NewWriter(file)
|
||||
return &bufferedFileWriter{
|
||||
file: file,
|
||||
writer: writer,
|
||||
jsonEnc: json.NewEncoder(writer),
|
||||
}
|
||||
}
|
||||
|
||||
type bufferedFileWriter struct {
|
||||
file *os.File
|
||||
writer *bufio.Writer
|
||||
jsonEnc *json.Encoder
|
||||
mu sync.Mutex // 添加互斥锁保护写入
|
||||
}
|
||||
|
||||
func (w *bufferedFileWriter) write(entry *LogEntry) {
|
||||
if w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
var err error
|
||||
if JsonFormat {
|
||||
output := JsonOutput{
|
||||
Level: entry.Level,
|
||||
Timestamp: entry.Time,
|
||||
Message: entry.Content,
|
||||
}
|
||||
err = w.jsonEnc.Encode(output)
|
||||
} else {
|
||||
logMsg := formatLogMessage(entry) + "\n"
|
||||
_, err = w.writer.WriteString(logMsg)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] 写入日志失败: %v\n", err)
|
||||
// 尝试重新打开文件
|
||||
if err := w.reopen(); err != nil {
|
||||
fmt.Printf("[ERROR] 重新打开文件失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 每隔一定数量的写入才进行一次Flush
|
||||
if err := w.writer.Flush(); err != nil {
|
||||
fmt.Printf("[ERROR] 刷新缓冲区失败: %v\n", err)
|
||||
if err := w.reopen(); err != nil {
|
||||
fmt.Printf("[ERROR] 重新打开文件失败: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *bufferedFileWriter) reopen() error {
|
||||
if w.file != nil {
|
||||
w.file.Close()
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(Outputfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.file = file
|
||||
w.writer = bufio.NewWriter(file)
|
||||
w.jsonEnc = json.NewEncoder(w.writer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *bufferedFileWriter) close() {
|
||||
if w != nil {
|
||||
w.writer.Flush()
|
||||
w.file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func CloseLogger() {
|
||||
if fileWriter != nil {
|
||||
fileWriter.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// CheckErrs 检查是否为需要重试的错误
|
||||
|
||||
247
Common/Output.go
Normal file
247
Common/Output.go
Normal file
@@ -0,0 +1,247 @@
|
||||
// output.go
|
||||
|
||||
package Common
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 全局输出管理器
|
||||
var ResultOutput *OutputManager
|
||||
|
||||
// OutputManager 输出管理器结构体
|
||||
type OutputManager struct {
|
||||
mu sync.Mutex
|
||||
outputPath string
|
||||
outputFormat string
|
||||
file *os.File
|
||||
csvWriter *csv.Writer
|
||||
jsonEncoder *json.Encoder
|
||||
isInitialized bool
|
||||
}
|
||||
|
||||
// ResultType 定义结果类型
|
||||
type ResultType string
|
||||
|
||||
const (
|
||||
HOST ResultType = "HOST" // 主机存活
|
||||
PORT ResultType = "PORT" // 端口开放
|
||||
SERVICE ResultType = "SERVICE" // 服务识别
|
||||
VULN ResultType = "VULN" // 漏洞发现
|
||||
)
|
||||
|
||||
// ScanResult 扫描结果结构
|
||||
type ScanResult struct {
|
||||
Time time.Time `json:"time"` // 发现时间
|
||||
Type ResultType `json:"type"` // 结果类型
|
||||
Target string `json:"target"` // 目标(IP/域名/URL)
|
||||
Status string `json:"status"` // 状态描述
|
||||
Details map[string]interface{} `json:"details"` // 详细信息
|
||||
}
|
||||
|
||||
// InitOutput 初始化输出系统
|
||||
func InitOutput() error {
|
||||
LogDebug("开始初始化输出系统")
|
||||
|
||||
// 验证输出格式
|
||||
switch OutputFormat {
|
||||
case "txt", "json", "csv":
|
||||
// 有效的格式
|
||||
default:
|
||||
return fmt.Errorf("不支持的输出格式: %s", OutputFormat)
|
||||
}
|
||||
|
||||
// 验证输出路径
|
||||
if Outputfile == "" {
|
||||
return fmt.Errorf("输出文件路径不能为空")
|
||||
}
|
||||
|
||||
dir := filepath.Dir(Outputfile)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
LogDebug(fmt.Sprintf("创建输出目录失败: %v", err))
|
||||
return fmt.Errorf("创建输出目录失败: %v", err)
|
||||
}
|
||||
|
||||
manager := &OutputManager{
|
||||
outputPath: Outputfile,
|
||||
outputFormat: OutputFormat,
|
||||
}
|
||||
|
||||
if err := manager.initialize(); err != nil {
|
||||
LogDebug(fmt.Sprintf("初始化输出管理器失败: %v", err))
|
||||
return fmt.Errorf("初始化输出管理器失败: %v", err)
|
||||
}
|
||||
|
||||
ResultOutput = manager
|
||||
LogDebug("输出系统初始化完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (om *OutputManager) initialize() error {
|
||||
om.mu.Lock()
|
||||
defer om.mu.Unlock()
|
||||
|
||||
if om.isInitialized {
|
||||
LogDebug("输出管理器已经初始化,跳过")
|
||||
return nil
|
||||
}
|
||||
|
||||
LogDebug(fmt.Sprintf("正在打开输出文件: %s", om.outputPath))
|
||||
file, err := os.OpenFile(om.outputPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
LogDebug(fmt.Sprintf("打开输出文件失败: %v", err))
|
||||
return fmt.Errorf("打开输出文件失败: %v", err)
|
||||
}
|
||||
om.file = file
|
||||
|
||||
switch om.outputFormat {
|
||||
case "csv":
|
||||
LogDebug("初始化CSV写入器")
|
||||
om.csvWriter = csv.NewWriter(file)
|
||||
headers := []string{"Time", "Type", "Target", "Status", "Details"}
|
||||
if err := om.csvWriter.Write(headers); err != nil {
|
||||
LogDebug(fmt.Sprintf("写入CSV头失败: %v", err))
|
||||
file.Close()
|
||||
return fmt.Errorf("写入CSV头失败: %v", err)
|
||||
}
|
||||
om.csvWriter.Flush()
|
||||
case "json":
|
||||
LogDebug("初始化JSON编码器")
|
||||
om.jsonEncoder = json.NewEncoder(file)
|
||||
om.jsonEncoder.SetIndent("", " ")
|
||||
case "txt":
|
||||
LogDebug("初始化文本输出")
|
||||
default:
|
||||
LogDebug(fmt.Sprintf("不支持的输出格式: %s", om.outputFormat))
|
||||
}
|
||||
|
||||
om.isInitialized = true
|
||||
LogDebug("输出管理器初始化完成")
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveResult 保存扫描结果
|
||||
func SaveResult(result *ScanResult) error {
|
||||
if ResultOutput == nil {
|
||||
LogDebug("输出系统未初始化")
|
||||
return fmt.Errorf("输出系统未初始化")
|
||||
}
|
||||
|
||||
LogDebug(fmt.Sprintf("正在保存结果 - 类型: %s, 目标: %s", result.Type, result.Target))
|
||||
return ResultOutput.saveResult(result)
|
||||
}
|
||||
|
||||
func (om *OutputManager) saveResult(result *ScanResult) error {
|
||||
om.mu.Lock()
|
||||
defer om.mu.Unlock()
|
||||
|
||||
if !om.isInitialized {
|
||||
LogDebug("输出管理器未初始化")
|
||||
return fmt.Errorf("输出管理器未初始化")
|
||||
}
|
||||
|
||||
var err error
|
||||
switch om.outputFormat {
|
||||
case "txt":
|
||||
err = om.writeTxt(result)
|
||||
case "json":
|
||||
err = om.writeJson(result)
|
||||
case "csv":
|
||||
err = om.writeCsv(result)
|
||||
default:
|
||||
LogDebug(fmt.Sprintf("不支持的输出格式: %s", om.outputFormat))
|
||||
return fmt.Errorf("不支持的输出格式: %s", om.outputFormat)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
LogDebug(fmt.Sprintf("保存结果失败: %v", err))
|
||||
} else {
|
||||
LogDebug(fmt.Sprintf("成功保存结果 - 类型: %s, 目标: %s", result.Type, result.Target))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (om *OutputManager) writeTxt(result *ScanResult) error {
|
||||
// 格式化 Details 为键值对字符串
|
||||
var details string
|
||||
if len(result.Details) > 0 {
|
||||
pairs := make([]string, 0, len(result.Details))
|
||||
for k, v := range result.Details {
|
||||
pairs = append(pairs, fmt.Sprintf("%s=%v", k, v))
|
||||
}
|
||||
details = strings.Join(pairs, ", ")
|
||||
}
|
||||
|
||||
txt := fmt.Sprintf("[%s] [%s] Target: %s, Status: %s, Details: {%s}\n",
|
||||
result.Time.Format("2006-01-02 15:04:05"),
|
||||
result.Type,
|
||||
result.Target,
|
||||
result.Status,
|
||||
details,
|
||||
)
|
||||
_, err := om.file.WriteString(txt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (om *OutputManager) writeJson(result *ScanResult) error {
|
||||
return om.jsonEncoder.Encode(result)
|
||||
}
|
||||
|
||||
func (om *OutputManager) writeCsv(result *ScanResult) error {
|
||||
details, err := json.Marshal(result.Details)
|
||||
if err != nil {
|
||||
details = []byte("{}")
|
||||
}
|
||||
|
||||
record := []string{
|
||||
result.Time.Format("2006-01-02 15:04:05"),
|
||||
string(result.Type),
|
||||
result.Target,
|
||||
result.Status,
|
||||
string(details),
|
||||
}
|
||||
|
||||
if err := om.csvWriter.Write(record); err != nil {
|
||||
return err
|
||||
}
|
||||
om.csvWriter.Flush()
|
||||
return om.csvWriter.Error()
|
||||
}
|
||||
|
||||
// CloseOutput 关闭输出系统
|
||||
func CloseOutput() error {
|
||||
if ResultOutput == nil {
|
||||
LogDebug("输出系统未初始化,无需关闭")
|
||||
return nil
|
||||
}
|
||||
|
||||
LogDebug("正在关闭输出系统")
|
||||
ResultOutput.mu.Lock()
|
||||
defer ResultOutput.mu.Unlock()
|
||||
|
||||
if !ResultOutput.isInitialized {
|
||||
LogDebug("输出管理器未初始化,无需关闭")
|
||||
return nil
|
||||
}
|
||||
|
||||
if ResultOutput.csvWriter != nil {
|
||||
LogDebug("刷新CSV写入器缓冲区")
|
||||
ResultOutput.csvWriter.Flush()
|
||||
}
|
||||
|
||||
if err := ResultOutput.file.Close(); err != nil {
|
||||
LogDebug(fmt.Sprintf("关闭文件失败: %v", err))
|
||||
return fmt.Errorf("关闭文件失败: %v", err)
|
||||
}
|
||||
|
||||
ResultOutput.isInitialized = false
|
||||
LogDebug("输出系统已关闭")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user