package main import ( "context" "log/slog" "os" "os/signal" "sync" "syscall" "time" ) var hostname string func init() { var err error hostname, err = os.Hostname() if err != nil { hostname = "unknown" } } func main() { cfg, err := LoadConfigV2() if err != nil { slog.Error("error loading configuration", "error", err) os.Exit(1) } slog.Info("TIXEL System Monitor started") var storage StorageInterface if cfg.LocalStorage.Enable { sqliteStorage, err := NewSQLiteStorage(cfg.LocalStorage.DBPath) if err != nil { slog.Error("failed to initialize SQLite storage", "error", err) os.Exit(1) } storage = sqliteStorage defer storage.Close() slog.Info("SQLite storage initialized", "path", cfg.LocalStorage.DBPath) } else { slog.Error("Local storage is disabled, but it's required for the new architecture") os.Exit(1) } var exportManager *ExportManager if cfg.Export.Enabled { exportConfig := ExportManagerConfig{ BatchSize: cfg.Export.BatchSize, ExportInterval: cfg.Export.ExportInterval, RetryAttempts: cfg.Export.RetryAttempts, RetryBackoff: cfg.Export.RetryBackoff, HealthCheckInterval: cfg.Export.HealthCheckInterval, } exportManager = NewExportManager(storage, exportConfig) if cfg.Elasticsearch.Enabled { esExporter, err := NewElasticsearchExporterV2(cfg.Elasticsearch) if err != nil { slog.Error("failed to create Elasticsearch exporter", "error", err) os.Exit(1) } if err := esExporter.HealthCheck(context.Background()); err != nil { slog.Error("Elasticsearch health check failed", "error", err) os.Exit(1) } exportManager.RegisterExporter("elasticsearch", esExporter) slog.Info("Elasticsearch exporter registered") } // Add more exporters here in the future // exportManager.RegisterExporter("checkmk", checkmkExporter) // exportManager.RegisterExporter("grafana", grafanaExporter) } logChan := make(chan LogEntry, 1000) ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() processor := NewLogProcessor(storage) processor.Start(ctx, logChan) }() if exportManager != nil { wg.Add(1) go func() { defer wg.Done() exportManager.Start(ctx) }() } for _, service := range cfg.Services { if !service.Enabled { slog.Info("Service deactivated, skipping...", "service", service.Name) continue } wg.Add(1) go func(s ServiceConfig) { defer wg.Done() monitor := NewServiceMonitor(s) if err := monitor.Start(ctx, logChan); err != nil { slog.Error("error watching service", "service", s.Name, "error", err) } }(service) slog.Info("started watching Service-Log", "service", service.Name) } for _, tool := range cfg.Tools { if !tool.Enabled { slog.Info("Tool is deactivated, skipping...", "tool", tool.Name) continue } wg.Add(1) go func(t ToolConfig) { defer wg.Done() monitor := NewFileMonitor(t) if err := monitor.Start(ctx, logChan); err != nil { slog.Error("error watching", "tool", t.Name, "error", err) } }(tool) slog.Info("started watching logs", "tool", tool.Name, "file", tool.LogFile) } if cfg.SystemMetrics.Enabled { wg.Add(1) go func() { defer wg.Done() collector := NewSystemMetricsCollector(cfg.SystemMetrics, cfg.PollIntervalSeconds) collector.StartV2(ctx, storage, logChan) }() slog.Info("Started collecting System-Metrics") } if cfg.WebService.Enabled { wg.Add(1) go func() { defer wg.Done() webService := NewWebServiceV2(cfg, storage) if err := webService.Start(ctx); err != nil { slog.Error("web service error", "error", err) } }() slog.Info("Web service started", "host", cfg.WebService.Host, "port", cfg.WebService.Port) } sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) <-sigCh slog.Info("Shutdown-Signal received, stopping threads...") cancel() close(logChan) done := make(chan struct{}) go func() { wg.Wait() close(done) }() select { case <-done: slog.Info("All threads closed") case <-time.After(10 * time.Second): slog.Info("Shutdown-Timeout reached, force quitting") } slog.Info("Program stopped") }