271 lines
9.8 KiB
Go
271 lines
9.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"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", "./tixel_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) {
|
|
viper.SetConfigName("config")
|
|
viper.AddConfigPath(".")
|
|
viper.AddConfigPath("/opt/tixel/tixel-watch/")
|
|
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
|
|
}
|