package main import ( "context" "flag" "fmt" "log/slog" "os" "os/signal" "syscall" "time" "edp.buildth.ing/DevFW-CICD/forgejo-runner-optimiser/internal/collector" "edp.buildth.ing/DevFW-CICD/forgejo-runner-optimiser/internal/output" "edp.buildth.ing/DevFW-CICD/forgejo-runner-optimiser/internal/summary" ) const ( defaultInterval = 5 * time.Second defaultProcPath = "/proc" defaultLogLevel = "info" defaultLogFormat = "json" defaultTopN = 5 ) func main() { // Parse command line flags interval := flag.Duration("interval", defaultInterval, "Collection interval (e.g., 5s, 1m)") procPath := flag.String("proc-path", defaultProcPath, "Path to proc filesystem") logLevel := flag.String("log-level", defaultLogLevel, "Log level: debug, info, warn, error") logFormat := flag.String("log-format", defaultLogFormat, "Output format: json, text") topN := flag.Int("top", defaultTopN, "Number of top processes to include") pushEndpoint := flag.String("push-endpoint", "", "HTTP endpoint to push metrics to (e.g., http://localhost:8080/api/v1/metrics)") pushToken := flag.String("push-token", os.Getenv("COLLECTOR_PUSH_TOKEN"), "Bearer token for push endpoint authentication (or set COLLECTOR_PUSH_TOKEN)") flag.Parse() // Setup structured logging for application logs appLogLevel := output.ParseLogLevel(*logLevel) appHandler := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ Level: appLogLevel, }) appLogger := slog.New(appHandler) // Setup metrics output writer metricsWriter := output.NewLoggerWriter(output.LoggerConfig{ Output: os.Stdout, Format: output.ParseLogFormat(*logFormat), Level: slog.LevelInfo, }) defer func() { _ = metricsWriter.Close() }() // Create collector c := collector.New(collector.Config{ ProcPath: *procPath, Interval: *interval, TopN: *topN, }, metricsWriter, appLogger) // Attach summary writer to emit run summary on shutdown summaryWriter := summary.NewSummaryWriter(os.Stdout, *logFormat) c.SetSummaryWriter(summaryWriter) // Setup push client if endpoint is configured if *pushEndpoint != "" { pushClient := summary.NewPushClient(*pushEndpoint, *pushToken) c.SetPushClient(pushClient) execCtx := pushClient.ExecutionContext() appLogger.Info("push client configured", slog.String("endpoint", *pushEndpoint), slog.String("organization", execCtx.Organization), slog.String("repository", execCtx.Repository), slog.String("workflow", execCtx.Workflow), slog.String("job", execCtx.Job), slog.String("run_id", execCtx.RunID), ) } // Setup signal handling for graceful shutdown ctx, cancel := context.WithCancel(context.Background()) defer cancel() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-sigChan appLogger.Info("received signal", slog.String("signal", sig.String())) cancel() }() // Run collector appLogger.Info("starting resource collector", slog.Duration("interval", *interval), slog.String("proc_path", *procPath), slog.String("log_level", *logLevel), slog.String("log_format", *logFormat), slog.Int("top_n", *topN), ) if err := c.Run(ctx); err != nil && err != context.Canceled { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } appLogger.Info("collector stopped gracefully") }