watch-tool/config.go

276 lines
9.9 KiB
Go

package main
import (
"fmt"
"log/slog"
"os"
"regexp"
"time"
"github.com/spf13/viper"
)
type ExportConfig struct {
Enabled bool `mapstructure:"enabled"`
BatchSize int `mapstructure:"batch_size"`
ExportInterval time.Duration `mapstructure:"export_interval"`
RetryAttempts int `mapstructure:"retry_attempts"`
RetryBackoff time.Duration `mapstructure:"retry_backoff"`
HealthCheckInterval time.Duration `mapstructure:"health_check_interval"`
}
type WebConfig struct {
Enabled bool `mapstructure:"enabled"`
Port int `mapstructure:"port"`
Host string `mapstructure:"host"`
}
type LogFormat struct {
Name string `mapstructure:"name"`
Pattern string `mapstructure:"pattern"`
Fields map[string]string `mapstructure:"fields"`
}
type ToolConfig struct {
Name string `mapstructure:"name"`
LogFile string `mapstructure:"log_file"`
Format LogFormat `mapstructure:"format"`
Enabled bool `mapstructure:"enabled"`
BufferSize int `mapstructure:"buffer_size"`
}
type ServiceConfig struct {
Name string `mapstructure:"name"`
Service string `mapstructure:"service"`
Enabled bool `mapstructure:"enabled"`
SinceTime string `mapstructure:"since_time"`
Priority string `mapstructure:"priority"`
}
type ElasticsearchConfig struct {
Enabled bool `mapstructure:"enabled"`
URL string `mapstructure:"url"`
Index string `mapstructure:"index"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
APIKey string `mapstructure:"api_key"`
Timeout int `mapstructure:"timeout"`
}
type LocalStorage struct {
Enable bool `mapstructure:"enabled"`
DBPath string `mapstructure:"db_path"`
RotationConfig StorageRotationConfig `mapstructure:"rotation"`
}
type SystemMetrics struct {
Enabled bool `mapstructure:"enabled"`
CollectCPU bool `mapstructure:"collect_cpu"`
CollectMemory bool `mapstructure:"collect_memory"`
CollectDisk bool `mapstructure:"collect_disk"`
CollectNetwork bool `mapstructure:"collect_network"`
CollectProcesses bool `mapstructure:"collect_processes"`
DiskPaths []string `mapstructure:"disk_paths"`
NetworkInterfaces []string `mapstructure:"network_interfaces"`
TopProcessesLimit int `mapstructure:"top_processes_limit"`
CollectNetworkConnections bool `mapstructure:"collect_network_connections"`
CollectLoadAverage bool `mapstructure:"collect_load_average"`
CollectTCPStats bool `mapstructure:"collect_tcp_stats"`
CollectFileHandles bool `mapstructure:"collect_filehandles"`
CollectDiskIO bool `mapstructure:"collect_disk_io"`
CollectNetworkLatency bool `mapstructure:"collect_network_latency"`
CollectBandwidthUsage bool `mapstructure:"collect_bandwidth_usage"`
TransferPorts []int `mapstructure:"transfer_ports"`
LatencyTestHosts []string `mapstructure:"latency_test_hosts"`
}
type Config struct {
Elasticsearch ElasticsearchConfig `mapstructure:"elasticsearch"`
LocalStorage LocalStorage `mapstrucutre:"localstorage"`
Export ExportConfig `mapstructure:"export"`
Tools []ToolConfig `mapstructure:"tools"`
Services []ServiceConfig `mapstructure:"services"`
PollIntervalSeconds int `mapstructure:"poll_interval_seconds"`
SystemMetrics SystemMetrics `mapstructure:"system_metrics"`
WebService WebConfig `mapstructure:"web_service"`
Logging struct {
Level string `mapstructure:"level"`
FilePath string `mapstructure:"file_path"`
} `mapstructure:"logging"`
PatternsFile string `mapstructure:"patterns_file"`
Drain3 Drain3Config `mapstructure:"drain3"`
}
type Drain3Config struct {
Enabled bool `mapstructure:"enabled"`
StateDir string `mapstructure:"state_dir"`
Depth int `mapstructure:"depth"`
SimThreshold float64 `mapstructure:"sim_th"`
MaxChildren int `mapstructure:"max_children"`
SaveIntervalSeconds int `mapstructure:"save_interval"`
}
type StorageRotationConfig struct {
// MaxSizeBytes is the maximum size of the database in bytes (0 = deactivated)
MaxSizeBytes int64 `mapstructure:"max_size_bytes"`
// MaxAgeHours is the maximum age of the database (0 = deaactivated)
MaxAgeHours time.Duration `mapstructure:"max_age_hours"`
// MaxFiles is the maximum count of old files, to keep
MaxFiles int `mapstructure:"max_files"`
// CheckIntervalMinutes is the intervall for checking rotation conditions
CheckIntervalMinutes time.Duration `mapstructure:"check_interval_minutes"`
// ArchiveDir is the dir to store archived files (empty = same dir as db)
ArchiveDir string `mapstructure:"archive_dir"`
}
func (src StorageRotationConfig) GetMaxAge() time.Duration {
if src.MaxAgeHours <= 0 {
return 0
}
return time.Duration(src.MaxAgeHours) * time.Hour
}
func (src StorageRotationConfig) GetCheckInterval() time.Duration {
if src.CheckIntervalMinutes <= 0 {
return 5 * time.Minute
}
return time.Duration(src.CheckIntervalMinutes) * time.Minute
}
func setConfigDefaults() {
viper.SetDefault("poll_interval_seconds", 30)
viper.SetDefault("elasticsearch.timeout", 30)
viper.SetDefault("system_metrics.enabled", true)
viper.SetDefault("system_metrics.collect_cpu", true)
viper.SetDefault("system_metrics.collect_memory", true)
viper.SetDefault("system_metrics.collect_disk", true)
viper.SetDefault("system_metrics.collect_network", false)
viper.SetDefault("system_metrics.disk_paths", []string{"/"})
viper.SetDefault("web_service.enabled", false)
viper.SetDefault("web_service.port", 8080)
viper.SetDefault("web_service.host", "localhost")
viper.SetDefault("logging.level", "info")
viper.SetDefault("export.enabled", true)
viper.SetDefault("export.batch_size", 100)
viper.SetDefault("export.export_interval", "30s")
viper.SetDefault("export.retry_attempts", 3)
viper.SetDefault("export.retry_backoff", "5s")
viper.SetDefault("export.health_check_interval", "60s")
viper.SetDefault("localstorage.enabled", true)
viper.SetDefault("localstorage.db_path", "./watch.db")
viper.SetDefault("localstorage.rotation.max_size_bytes", int64(100*1024*1024))
viper.SetDefault("localstorage.rotation.max_age_hours", 24)
viper.SetDefault("localstorage.rotation.max_files", 7)
viper.SetDefault("localstorage.rotation.check_interval_minutes", 5)
viper.SetDefault("localstorage.rotation.archive_dir", "")
viper.SetDefault("patterns_file", "./configs/patterns.yaml")
viper.SetDefault("drain3.enabled", true)
viper.SetDefault("drain3.state_dir", "./drain3_states")
viper.SetDefault("drain3.depth", 4)
viper.SetDefault("drain3.sim_th", 0.4)
viper.SetDefault("drain3.max_children", 100)
}
func LoadConfig() (*Config, error) {
home, err := os.UserConfigDir()
if err != nil {
return nil, fmt.Errorf("unable to get user config dir: %w", err)
}
viper.SetConfigName("config")
viper.AddConfigPath(".")
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
setConfigDefaults()
if err := viper.ReadInConfig(); err != nil {
return nil, fmt.Errorf("error reading config: %w", err)
}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("error parsing config: %w", err)
}
if err := validateConfig(&cfg); err != nil {
return nil, fmt.Errorf("config validation failed: %w", err)
}
return &cfg, nil
}
func validateConfig(cfg *Config) error {
if !cfg.LocalStorage.Enable {
return fmt.Errorf("local storage must be enabled in the new architecture")
}
if cfg.LocalStorage.DBPath == "" {
return fmt.Errorf("local storage db_path is required")
}
if cfg.Export.Enabled && cfg.Elasticsearch.Enabled {
if cfg.Elasticsearch.URL == "" {
return fmt.Errorf("elasticsearch.url is required when elasticsearch export is enabled")
}
if cfg.Elasticsearch.Index == "" {
return fmt.Errorf("elasticsearch.index is required when elasticsearch export is enabled")
}
}
if cfg.PollIntervalSeconds <= 0 {
slog.Warn("poll_interval_seconds is invalid, setting to 30", "value", cfg.PollIntervalSeconds)
cfg.PollIntervalSeconds = 30
}
if cfg.Export.Enabled {
if cfg.Export.BatchSize <= 0 {
cfg.Export.BatchSize = 100
}
if cfg.Export.ExportInterval <= 0 {
cfg.Export.ExportInterval = 30 * time.Second
}
if cfg.Export.RetryAttempts < 0 {
cfg.Export.RetryAttempts = 3
}
if cfg.Export.RetryBackoff <= 0 {
cfg.Export.RetryBackoff = 5 * time.Second
}
if cfg.Export.HealthCheckInterval <= 0 {
cfg.Export.HealthCheckInterval = 60 * time.Second
}
}
for _, tool := range cfg.Tools {
if tool.BufferSize <= 0 {
tool.BufferSize = 100
}
if tool.Format.Pattern != "" {
if _, err := regexp.Compile(tool.Format.Pattern); err != nil {
return fmt.Errorf("invalid regex for tool '%s': %w", tool.Name, err)
}
}
}
if cfg.LocalStorage.RotationConfig.MaxSizeBytes < 0 {
slog.Warn("Invalid rotation max_size_bytes, setting to 100MB", "value", cfg.LocalStorage.RotationConfig.MaxSizeBytes)
cfg.LocalStorage.RotationConfig.MaxSizeBytes = 100 * 1024 * 1024
}
if cfg.LocalStorage.RotationConfig.MaxAgeHours < 0 {
slog.Warn("Invalid rotation max_age_hours, setting to 24", "value", cfg.LocalStorage.RotationConfig.MaxAgeHours)
cfg.LocalStorage.RotationConfig.MaxAgeHours = 24
}
if cfg.LocalStorage.RotationConfig.MaxFiles < 0 {
slog.Warn("Invalid rotation max_files, setting to 7", "value", cfg.LocalStorage.RotationConfig.MaxFiles)
cfg.LocalStorage.RotationConfig.MaxFiles = 7
}
if cfg.LocalStorage.RotationConfig.CheckIntervalMinutes < 1 {
slog.Warn("Invalid rotation check_interval_minutes, setting to 5", "value", cfg.LocalStorage.RotationConfig.CheckIntervalMinutes)
cfg.LocalStorage.RotationConfig.CheckIntervalMinutes = 5
}
return nil
}