forgejo-runner-optimiser/cmd/collector/main.go
Manuel Ganter cfe583fbc4
All checks were successful
ci / build (push) Successful in 46s
feat(collector): add HTTP push for metrics to receiver
Add push client that sends run summary to a configurable HTTP endpoint
on shutdown. Execution context is read from GitHub Actions style
environment variables (with Gitea fallbacks).

New flag: -push-endpoint <url>

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:44:20 +01:00

105 lines
3.1 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"log/slog"
"os"
"os/signal"
"syscall"
"time"
"edp.buildth.ing/DevFW-CICD/forgejo-runner-resource-collector/internal/collector"
"edp.buildth.ing/DevFW-CICD/forgejo-runner-resource-collector/internal/output"
"edp.buildth.ing/DevFW-CICD/forgejo-runner-resource-collector/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)")
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)
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")
}