mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00

主要更改: - 重构Log.go和Output.go为模块化架构 - 创建独立的logging和output模块 - 新增LevelBaseInfoSuccess默认日志级别(显示BASE、INFO、SUCCESS) - 添加运行时间显示到每条日志前面 - 保持完全向后兼容的API接口 - 支持多种输出格式(TXT、JSON、CSV) - 优化日志格式化和颜色显示 技术改进: - 模块化设计便于扩展和维护 - 智能时间格式化(毫秒→秒→分钟→小时) - 支持缓冲和批量输出 - 线程安全的并发处理
379 lines
7.7 KiB
Go
379 lines
7.7 KiB
Go
package output
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Manager 输出管理器
|
|
type Manager struct {
|
|
mu sync.RWMutex
|
|
config *ManagerConfig
|
|
writer OutputWriter
|
|
reader OutputReader
|
|
statistics *Statistics
|
|
buffer []*ScanResult
|
|
bufferMutex sync.Mutex
|
|
flushTicker *time.Ticker
|
|
stopChan chan struct{}
|
|
initialized bool
|
|
closed bool
|
|
}
|
|
|
|
// NewManager 创建新的输出管理器
|
|
func NewManager(config *ManagerConfig) (*Manager, error) {
|
|
if config == nil {
|
|
return nil, fmt.Errorf("配置不能为空")
|
|
}
|
|
|
|
// 验证输出格式
|
|
if err := validateFormat(config.Format); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 创建输出目录
|
|
if err := createOutputDir(config.OutputPath); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
manager := &Manager{
|
|
config: config,
|
|
statistics: NewStatistics(),
|
|
stopChan: make(chan struct{}),
|
|
}
|
|
|
|
// 初始化写入器
|
|
if err := manager.initializeWriter(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 初始化读取器
|
|
manager.initializeReader()
|
|
|
|
// 如果启用缓冲,初始化缓冲区
|
|
if config.EnableBuffer {
|
|
manager.buffer = make([]*ScanResult, 0, config.BufferSize)
|
|
|
|
// 如果启用自动刷新,启动定时器
|
|
if config.AutoFlush {
|
|
manager.startAutoFlush()
|
|
}
|
|
}
|
|
|
|
manager.initialized = true
|
|
return manager, nil
|
|
}
|
|
|
|
// validateFormat 验证输出格式
|
|
func validateFormat(format OutputFormat) error {
|
|
switch format {
|
|
case FormatTXT, FormatJSON, FormatCSV:
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("不支持的输出格式: %s", format)
|
|
}
|
|
}
|
|
|
|
// createOutputDir 创建输出目录
|
|
func createOutputDir(outputPath string) error {
|
|
dir := filepath.Dir(outputPath)
|
|
return os.MkdirAll(dir, 0755)
|
|
}
|
|
|
|
// initializeWriter 初始化写入器
|
|
func (m *Manager) initializeWriter() error {
|
|
var writer OutputWriter
|
|
var err error
|
|
|
|
switch m.config.Format {
|
|
case FormatTXT:
|
|
writer, err = NewTXTWriter(m.config.OutputPath)
|
|
case FormatJSON:
|
|
writer, err = NewJSONWriter(m.config.OutputPath)
|
|
case FormatCSV:
|
|
writer, err = NewCSVWriter(m.config.OutputPath)
|
|
default:
|
|
return fmt.Errorf("不支持的输出格式: %s", m.config.Format)
|
|
}
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("初始化写入器失败: %v", err)
|
|
}
|
|
|
|
m.writer = writer
|
|
|
|
// 写入头部(如果需要)
|
|
return m.writer.WriteHeader()
|
|
}
|
|
|
|
// initializeReader 初始化读取器
|
|
func (m *Manager) initializeReader() {
|
|
// 目前只有CSV格式支持读取
|
|
if m.config.Format == FormatCSV {
|
|
m.reader = NewCSVReader(m.config.OutputPath)
|
|
}
|
|
}
|
|
|
|
// startAutoFlush 启动自动刷新
|
|
func (m *Manager) startAutoFlush() {
|
|
m.flushTicker = time.NewTicker(m.config.FlushInterval)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-m.flushTicker.C:
|
|
m.flushBuffer()
|
|
case <-m.stopChan:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// SaveResult 保存扫描结果
|
|
func (m *Manager) SaveResult(result *ScanResult) error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if !m.initialized {
|
|
return fmt.Errorf("输出管理器未初始化")
|
|
}
|
|
|
|
if m.closed {
|
|
return fmt.Errorf("输出管理器已关闭")
|
|
}
|
|
|
|
// 更新统计信息
|
|
m.statistics.AddResult(result.Type)
|
|
|
|
// 如果启用缓冲,先添加到缓冲区
|
|
if m.config.EnableBuffer {
|
|
return m.addToBuffer(result)
|
|
}
|
|
|
|
// 直接写入
|
|
return m.writer.Write(result)
|
|
}
|
|
|
|
// addToBuffer 添加结果到缓冲区
|
|
func (m *Manager) addToBuffer(result *ScanResult) error {
|
|
m.bufferMutex.Lock()
|
|
defer m.bufferMutex.Unlock()
|
|
|
|
m.buffer = append(m.buffer, result)
|
|
|
|
// 如果缓冲区已满,立即刷新
|
|
if len(m.buffer) >= m.config.BufferSize {
|
|
return m.flushBufferUnsafe()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// flushBuffer 刷新缓冲区(加锁版本)
|
|
func (m *Manager) flushBuffer() error {
|
|
m.bufferMutex.Lock()
|
|
defer m.bufferMutex.Unlock()
|
|
return m.flushBufferUnsafe()
|
|
}
|
|
|
|
// flushBufferUnsafe 刷新缓冲区(无锁版本,内部使用)
|
|
func (m *Manager) flushBufferUnsafe() error {
|
|
if len(m.buffer) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// 批量写入
|
|
for _, result := range m.buffer {
|
|
if err := m.writer.Write(result); err != nil {
|
|
return fmt.Errorf("写入结果失败: %v", err)
|
|
}
|
|
}
|
|
|
|
// 刷新写入器
|
|
if err := m.writer.Flush(); err != nil {
|
|
return fmt.Errorf("刷新写入器失败: %v", err)
|
|
}
|
|
|
|
// 清空缓冲区
|
|
m.buffer = m.buffer[:0]
|
|
return nil
|
|
}
|
|
|
|
// GetResults 获取扫描结果
|
|
func (m *Manager) GetResults() ([]*ScanResult, error) {
|
|
return m.GetResultsWithFilter(nil)
|
|
}
|
|
|
|
// GetResultsWithFilter 获取过滤后的扫描结果
|
|
func (m *Manager) GetResultsWithFilter(filter *ResultFilter) ([]*ScanResult, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if m.reader == nil {
|
|
return nil, fmt.Errorf("当前输出格式不支持读取")
|
|
}
|
|
|
|
return m.reader.ReadWithFilter(filter)
|
|
}
|
|
|
|
// GetStatistics 获取统计信息
|
|
func (m *Manager) GetStatistics() *Statistics {
|
|
return m.statistics
|
|
}
|
|
|
|
// Flush 刷新所有缓冲区
|
|
func (m *Manager) Flush() error {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
if !m.initialized || m.closed {
|
|
return nil
|
|
}
|
|
|
|
// 刷新缓冲区
|
|
if m.config.EnableBuffer {
|
|
if err := m.flushBuffer(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 刷新写入器
|
|
return m.writer.Flush()
|
|
}
|
|
|
|
// Close 关闭输出管理器
|
|
func (m *Manager) Close() error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return nil
|
|
}
|
|
|
|
// 停止自动刷新
|
|
if m.flushTicker != nil {
|
|
m.flushTicker.Stop()
|
|
close(m.stopChan)
|
|
}
|
|
|
|
// 最后一次刷新缓冲区
|
|
if m.config.EnableBuffer {
|
|
m.flushBufferUnsafe()
|
|
}
|
|
|
|
// 关闭写入器
|
|
var err error
|
|
if m.writer != nil {
|
|
err = m.writer.Close()
|
|
}
|
|
|
|
// 关闭读取器
|
|
if m.reader != nil {
|
|
if closeErr := m.reader.Close(); closeErr != nil && err == nil {
|
|
err = closeErr
|
|
}
|
|
}
|
|
|
|
m.closed = true
|
|
return err
|
|
}
|
|
|
|
// IsInitialized 检查是否已初始化
|
|
func (m *Manager) IsInitialized() bool {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return m.initialized
|
|
}
|
|
|
|
// IsClosed 检查是否已关闭
|
|
func (m *Manager) IsClosed() bool {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return m.closed
|
|
}
|
|
|
|
// GetConfig 获取配置
|
|
func (m *Manager) GetConfig() *ManagerConfig {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
|
|
// 返回配置副本
|
|
config := *m.config
|
|
return &config
|
|
}
|
|
|
|
// UpdateConfig 更新配置(部分配置可以动态更新)
|
|
func (m *Manager) UpdateConfig(updates map[string]interface{}) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if m.closed {
|
|
return fmt.Errorf("输出管理器已关闭")
|
|
}
|
|
|
|
// 只允许更新部分配置
|
|
for key, value := range updates {
|
|
switch key {
|
|
case "enable_buffer":
|
|
if enableBuffer, ok := value.(bool); ok {
|
|
m.config.EnableBuffer = enableBuffer
|
|
}
|
|
case "buffer_size":
|
|
if bufferSize, ok := value.(int); ok && bufferSize > 0 {
|
|
m.config.BufferSize = bufferSize
|
|
}
|
|
case "auto_flush":
|
|
if autoFlush, ok := value.(bool); ok {
|
|
m.config.AutoFlush = autoFlush
|
|
if autoFlush && m.flushTicker == nil {
|
|
m.startAutoFlush()
|
|
} else if !autoFlush && m.flushTicker != nil {
|
|
m.flushTicker.Stop()
|
|
m.flushTicker = nil
|
|
}
|
|
}
|
|
case "flush_interval":
|
|
if flushInterval, ok := value.(time.Duration); ok && flushInterval > 0 {
|
|
m.config.FlushInterval = flushInterval
|
|
if m.flushTicker != nil {
|
|
m.flushTicker.Stop()
|
|
m.startAutoFlush()
|
|
}
|
|
}
|
|
default:
|
|
return fmt.Errorf("不支持更新的配置项: %s", key)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 全局输出管理器实例
|
|
var (
|
|
globalManager *Manager
|
|
managerOnce sync.Once
|
|
)
|
|
|
|
// GetGlobalManager 获取全局输出管理器
|
|
func GetGlobalManager() *Manager {
|
|
return globalManager
|
|
}
|
|
|
|
// SetGlobalManager 设置全局输出管理器
|
|
func SetGlobalManager(manager *Manager) {
|
|
globalManager = manager
|
|
}
|
|
|
|
// InitGlobalManager 初始化全局输出管理器
|
|
func InitGlobalManager(config *ManagerConfig) error {
|
|
manager, err := NewManager(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
SetGlobalManager(manager)
|
|
return nil
|
|
} |