Skip to content

Commit 5119193

Browse files
committed
feat(metrics): route prometheus export through the bridge; drop forge runtime collector
1 parent 175de97 commit 5119193

3 files changed

Lines changed: 64 additions & 11 deletions

File tree

internal/metrics/collector.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type collector struct {
101101
errors []error
102102
httpCollector *collectors.HTTPCollector
103103
tsStorage *storage.TimeSeriesStorage
104+
promBridge *exporters.PrometheusBridge
104105
}
105106

106107
// CollectorConfig contains configuration for the metrics collector.
@@ -154,6 +155,13 @@ func New(config *CollectorConfig, logger logger.Logger) Metrics {
154155
// Initialize exporters
155156
c.initializeExporters()
156157

158+
// Prometheus bridge: reads the merged snapshot fresh on each scrape.
159+
c.promBridge = exporters.NewPrometheusBridge(c.GetMetrics, exporters.PrometheusConfig{
160+
Namespace: c.config.Collection.Namespace,
161+
EnableGoCollector: true,
162+
EnableProcessCollector: true,
163+
})
164+
157165
// Initialize time-series storage for historical metric queries.
158166
c.tsStorage = storage.NewTimeSeriesStorageWithConfig(&storage.TimeSeriesStorageConfig{
159167
Retention: time.Hour,
@@ -582,7 +590,10 @@ func (c *collector) GetMetricsByTag(tagKey, tagValue string) map[string]any {
582590

583591
// Export exports metrics in the specified format.
584592
func (c *collector) Export(format metrics.ExportFormat) ([]byte, error) {
585-
// Snapshot exporter without holding lock during export operation
593+
if format == metrics.ExportFormatPrometheus && c.promBridge != nil {
594+
return c.promBridge.GatherText()
595+
}
596+
586597
c.mu.RLock()
587598
exporter, exists := c.exporters[format]
588599
c.mu.RUnlock()
@@ -593,9 +604,15 @@ func (c *collector) Export(format metrics.ExportFormat) ([]byte, error) {
593604

594605
// Get metrics without holding the collector lock
595606
// This prevents deadlock with other systems (health checks, etc.) that may need locks
596-
metrics := c.GetMetrics()
607+
allMetrics := c.GetMetrics()
597608

598-
return exporter.Export(metrics)
609+
return exporter.Export(allMetrics)
610+
}
611+
612+
// PrometheusHandler returns an http.Handler that serves the Prometheus scrape
613+
// endpoint. Implements shared.PrometheusProvider.
614+
func (c *collector) PrometheusHandler() http.Handler {
615+
return c.promBridge.Handler()
599616
}
600617

601618
// ExportToFile exports metrics to a file.
@@ -739,14 +756,6 @@ func (c *collector) initializeBuiltinCollectors() error {
739756
}
740757
}
741758

742-
// Register runtime metrics collector
743-
if c.config.Features.RuntimeMetrics {
744-
runtimeCollector := collectors.NewRuntimeCollector()
745-
if err := c.registerCollectorLocked(runtimeCollector); err != nil {
746-
return fmt.Errorf("failed to register runtime collector: %w", err)
747-
}
748-
}
749-
750759
// Register HTTP collector
751760
if c.config.Features.HTTPMetrics {
752761
httpCollector := collectors.NewHTTPCollector()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package metrics
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/prometheus/common/expfmt"
8+
"github.com/xraph/forge/internal/shared"
9+
)
10+
11+
func TestCollector_ExportPrometheusParses(t *testing.T) {
12+
c := New(DefaultCollectorConfig(), nil)
13+
14+
c.Counter("widget_built_total").Inc()
15+
c.Gauge("widget_pending").Set(4)
16+
17+
out, err := c.Export(shared.ExportFormatPrometheus)
18+
if err != nil {
19+
t.Fatalf("Export: %v", err)
20+
}
21+
22+
var parser expfmt.TextParser
23+
if _, err := parser.TextToMetricFamilies(strings.NewReader(string(out))); err != nil {
24+
t.Fatalf("output is not valid prometheus text: %v", err)
25+
}
26+
if !strings.Contains(string(out), "widget_built_total") {
27+
t.Fatalf("expected widget_built_total in output, got:\n%s", out)
28+
}
29+
}
30+
31+
func TestCollector_ImplementsPrometheusProvider(t *testing.T) {
32+
c := New(DefaultCollectorConfig(), nil)
33+
if _, ok := c.(shared.PrometheusProvider); !ok {
34+
t.Fatal("collector does not implement shared.PrometheusProvider")
35+
}
36+
}

internal/shared/metrics.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package shared
22

33
import (
4+
"net/http"
5+
46
"github.com/xraph/go-utils/metrics"
57
)
68

@@ -72,3 +74,9 @@ type Exporter = metrics.Exporter
7274

7375
// CollectorStats contains statistics about the metrics collector.
7476
type CollectorStats = metrics.CollectorStats
77+
78+
// PrometheusProvider is implemented by metrics collectors that can serve a
79+
// Prometheus scrape endpoint via an http.Handler.
80+
type PrometheusProvider interface {
81+
PrometheusHandler() http.Handler
82+
}

0 commit comments

Comments
 (0)