From 740d5ef1bc4d698bc68bf7884dd0ede72f6cb242 Mon Sep 17 00:00:00 2001 From: samzong Date: Sat, 29 Nov 2025 01:24:09 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(observability):=20add=20config?= =?UTF-8?q?urable=20Prometheus=20metrics=20endpoint,=20keep=20default=20en?= =?UTF-8?q?abled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: samzong --- config/config.yaml | 2 + config/observability/config.tracing.yaml | 2 + src/semantic-router/cmd/main.go | 29 +++++++---- src/semantic-router/pkg/config/config.go | 9 ++++ src/semantic-router/pkg/config/config_test.go | 50 +++++++++++++++++++ 5 files changed, 83 insertions(+), 9 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index 3454e0d13..fc11e99fe 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -506,6 +506,8 @@ embedding_models: # Observability Configuration observability: + metrics: + enabled: true # Set to false to disable the Prometheus /metrics endpoint tracing: enabled: true # Enable distributed tracing for docker-compose stack provider: "opentelemetry" # Provider: opentelemetry, openinference, openllmetry diff --git a/config/observability/config.tracing.yaml b/config/observability/config.tracing.yaml index 960a86fa9..2cfba796b 100644 --- a/config/observability/config.tracing.yaml +++ b/config/observability/config.tracing.yaml @@ -123,6 +123,8 @@ api: enabled: true observability: + metrics: + enabled: true # Set to false to disable the Prometheus /metrics endpoint tracing: enabled: true provider: "opentelemetry" diff --git a/src/semantic-router/cmd/main.go b/src/semantic-router/cmd/main.go index 9a46583bf..016672cc2 100644 --- a/src/semantic-router/cmd/main.go +++ b/src/semantic-router/cmd/main.go @@ -102,15 +102,26 @@ func main() { os.Exit(0) }() - // Start metrics server - go func() { - http.Handle("/metrics", promhttp.Handler()) - metricsAddr := fmt.Sprintf(":%d", *metricsPort) - logging.Infof("Starting metrics server on %s", metricsAddr) - if metricsErr := http.ListenAndServe(metricsAddr, nil); metricsErr != nil { - logging.Errorf("Metrics server error: %v", metricsErr) - } - }() + // Start metrics server if enabled + metricsEnabled := true + if cfg.Observability.Metrics.Enabled != nil { + metricsEnabled = *cfg.Observability.Metrics.Enabled + } + if *metricsPort <= 0 { + metricsEnabled = false + } + if metricsEnabled { + go func() { + http.Handle("/metrics", promhttp.Handler()) + metricsAddr := fmt.Sprintf(":%d", *metricsPort) + logging.Infof("Starting metrics server on %s", metricsAddr) + if metricsErr := http.ListenAndServe(metricsAddr, nil); metricsErr != nil { + logging.Errorf("Metrics server error: %v", metricsErr) + } + }() + } else { + logging.Infof("Metrics server disabled") + } // Create and start the ExtProc server server, err := extproc.NewServer(*configPath, *port, *secure, *certPath) diff --git a/src/semantic-router/pkg/config/config.go b/src/semantic-router/pkg/config/config.go index 0951989fe..01980f384 100644 --- a/src/semantic-router/pkg/config/config.go +++ b/src/semantic-router/pkg/config/config.go @@ -262,6 +262,15 @@ type APIConfig struct { type ObservabilityConfig struct { // Tracing configuration for distributed tracing Tracing TracingConfig `yaml:"tracing"` + // Metrics configuration for Prometheus metrics endpoint + Metrics MetricsConfig `yaml:"metrics"` +} + +// MetricsConfig represents configuration for metrics endpoint +type MetricsConfig struct { + // Enabled controls whether the Prometheus metrics endpoint is served + // When omitted, defaults to true + Enabled *bool `yaml:"enabled,omitempty"` } // TracingConfig represents configuration for distributed tracing diff --git a/src/semantic-router/pkg/config/config_test.go b/src/semantic-router/pkg/config/config_test.go index 889fe86fc..e150a7f8c 100644 --- a/src/semantic-router/pkg/config/config_test.go +++ b/src/semantic-router/pkg/config/config_test.go @@ -206,6 +206,56 @@ tools: }) }) + Context("with observability metrics configuration", func() { + It("should default to enabled when metrics block is omitted", func() { + configContent := ` +observability: + tracing: + enabled: false +` + err := os.WriteFile(configFile, []byte(configContent), 0o644) + Expect(err).NotTo(HaveOccurred()) + + cfg, err := Load(configFile) + Expect(err).NotTo(HaveOccurred()) + Expect(cfg.Observability.Metrics.Enabled).To(BeNil()) + }) + + It("should honor explicit metrics disable flag", func() { + configContent := ` +observability: + metrics: + enabled: false + tracing: + enabled: false +` + err := os.WriteFile(configFile, []byte(configContent), 0o644) + Expect(err).NotTo(HaveOccurred()) + + cfg, err := Load(configFile) + Expect(err).NotTo(HaveOccurred()) + Expect(cfg.Observability.Metrics.Enabled).NotTo(BeNil()) + Expect(*cfg.Observability.Metrics.Enabled).To(BeFalse()) + }) + + It("should honor explicit metrics enable flag", func() { + configContent := ` +observability: + metrics: + enabled: true + tracing: + enabled: false +` + err := os.WriteFile(configFile, []byte(configContent), 0o644) + Expect(err).NotTo(HaveOccurred()) + + cfg, err := Load(configFile) + Expect(err).NotTo(HaveOccurred()) + Expect(cfg.Observability.Metrics.Enabled).NotTo(BeNil()) + Expect(*cfg.Observability.Metrics.Enabled).To(BeTrue()) + }) + }) + Context("with invalid YAML syntax", func() { BeforeEach(func() { invalidYAML := `