diff --git a/OpenTelemetryPlan/06-implementation-phases.md b/OpenTelemetryPlan/06-implementation-phases.md index 5f3a6d300e..9add8d3df1 100644 --- a/OpenTelemetryPlan/06-implementation-phases.md +++ b/OpenTelemetryPlan/06-implementation-phases.md @@ -572,13 +572,14 @@ graph LR server=otel # NEW: uses OTel OTLP metrics exporter # No prefix: it applies on the StatsD path only, not this one. -# Endpoint and auth inherited from [telemetry] section: +# Endpoints and auth come from the [telemetry] section: [telemetry] enabled=1 -endpoint=http://localhost:4318/v1/traces +traces_endpoint=http://localhost:4318/v1/traces +metrics_endpoint=http://localhost:4318/v1/metrics ``` -The `OTelCollector` reads the OTLP endpoint from `[telemetry]` config (replacing `/v1/traces` with `/v1/metrics` for the metrics exporter). No additional config keys needed. +Each signal has its own key and each URL is used verbatim, so an operator can send traces and metrics to different collectors, or to one whose OTLP paths are not the defaults. **Backward compatibility**: `server=statsd` continues to work exactly as before. diff --git a/OpenTelemetryPlan/09-data-collection-reference.md b/OpenTelemetryPlan/09-data-collection-reference.md index 94f3ec755b..5cbb860e4f 100644 --- a/OpenTelemetryPlan/09-data-collection-reference.md +++ b/OpenTelemetryPlan/09-data-collection-reference.md @@ -615,9 +615,9 @@ name and maps `.` and space to `_`, and the only place the class reads `prefix_` is its startup log line. Exported names are therefore the lowercased raw names (`jobq_job_count`, `rpc_requests_total`) and the service is identified by the OTel resource `service.name`, not by a name prefix. `endpoint` is read from this section -but likewise reaches only that log line — the real exporter URL is derived inside -`Telemetry::initMetrics()` from `[telemetry] endpoint`, by swapping the trailing -`/v1/traces` for `/v1/metrics`. +but likewise reaches only that log line — the exporter URL comes from +`[telemetry] metrics_endpoint`, which `Telemetry::initMetrics()` uses verbatim. No +endpoint is derived from another. Fallback (StatsD). `StatsDCollector` is still selected by this value, but the stack in `docker/telemetry/` no longer receives it: using this path also requires diff --git a/docker/telemetry/integration-test.sh b/docker/telemetry/integration-test.sh index 032be1b063..3ea027a9b3 100755 --- a/docker/telemetry/integration-test.sh +++ b/docker/telemetry/integration-test.sh @@ -385,7 +385,7 @@ ${IPS_FIXED} enabled=1 service_instance_id=Node-${i} traces_endpoint=http://localhost:4318/v1/traces -exporter=otlp_http +metrics_endpoint=http://localhost:4318/v1/metrics batch_size=512 batch_delay_ms=2000 max_queue_size=2048 diff --git a/include/xrpl/telemetry/Telemetry.h b/include/xrpl/telemetry/Telemetry.h index f808d65f7b..09ae1a2915 100644 --- a/include/xrpl/telemetry/Telemetry.h +++ b/include/xrpl/telemetry/Telemetry.h @@ -210,6 +210,14 @@ public: */ std::string tracesEndpoint = "http://localhost:4318/v1/traces"; + /** + * Full OTLP/HTTP URL where metrics are sent, including the signal path. + * Used verbatim and independent of tracesEndpoint, so an operator can + * point the two signals at different collectors, or at one whose OTLP + * paths are not the defaults. + */ + std::string metricsEndpoint = "http://localhost:4318/v1/metrics"; + /** * Whether to use TLS for the exporter connection. */ diff --git a/src/libxrpl/telemetry/Telemetry.cpp b/src/libxrpl/telemetry/Telemetry.cpp index e135b37bc7..32682b0354 100644 --- a/src/libxrpl/telemetry/Telemetry.cpp +++ b/src/libxrpl/telemetry/Telemetry.cpp @@ -83,12 +83,6 @@ namespace xrpl::telemetry { static_assert(kMeterName == beast::insight::kOTelMeterName); static_assert(kMeterVersion == beast::insight::kOTelMeterVersion); -/** - * OTLP/HTTP path per signal, appended by signalEndpoint(). - */ -constexpr std::string_view kTracesPath{"/v1/traces"}; -constexpr std::string_view kMetricsPath{"/v1/metrics"}; - /** * Metric export cadence. The interval matches the 1 s scrape the dashboards * assume; the timeout bounds a stalled collector. @@ -390,11 +384,12 @@ public: start() override { JLOG(journal_.info()) << "Telemetry starting: traces_endpoint=" << setup_.tracesEndpoint + << " metrics_endpoint=" << setup_.metricsEndpoint << " sampling=" << setup_.samplingRatio; // Configure OTLP HTTP exporter otlp_http::OtlpHttpExporterOptions exporterOpts; - exporterOpts.url = signalEndpoint(setup_.tracesEndpoint, kTracesPath); + exporterOpts.url = setup_.tracesEndpoint; if (setup_.useTls) { exporterOpts.ssl_ca_cert_path = setup_.tlsCertPath; diff --git a/src/libxrpl/telemetry/TelemetryConfig.cpp b/src/libxrpl/telemetry/TelemetryConfig.cpp index 142b86b317..e8a68f3ab6 100644 --- a/src/libxrpl/telemetry/TelemetryConfig.cpp +++ b/src/libxrpl/telemetry/TelemetryConfig.cpp @@ -36,6 +36,7 @@ constexpr char const* enabled = "enabled"; constexpr char const* serviceName = "service_name"; constexpr char const* serviceInstanceId = "service_instance_id"; constexpr char const* tracesEndpoint = "traces_endpoint"; +constexpr char const* metricsEndpoint = "metrics_endpoint"; constexpr char const* useTls = "use_tls"; constexpr char const* tlsCaCert = "tls_ca_cert"; constexpr char const* tlsClientCert = "tls_client_cert"; @@ -61,6 +62,7 @@ constexpr char const* traceLedger = "trace_ledger"; namespace dflt { constexpr char const* serviceName = "xrpld"; constexpr char const* tracesEndpoint = "http://localhost:4318/v1/traces"; +constexpr char const* metricsEndpoint = "http://localhost:4318/v1/metrics"; constexpr std::uint32_t batchSize = 512u; constexpr std::uint32_t batchDelayMs = 5000u; constexpr std::uint32_t maxQueueSize = 2048u; @@ -137,6 +139,8 @@ makeTelemetrySetup( setup.serviceInstanceId = section.valueOr(key::serviceInstanceId, nodePublicKey); setup.tracesEndpoint = section.valueOr(key::tracesEndpoint, dflt::tracesEndpoint); + setup.metricsEndpoint = + section.valueOr(key::metricsEndpoint, dflt::metricsEndpoint); setup.useTls = section.valueOr(key::useTls, 0) != 0; setup.tlsCertPath = section.valueOr(key::tlsCaCert, ""); diff --git a/src/tests/libxrpl/telemetry/TelemetryConfig.cpp b/src/tests/libxrpl/telemetry/TelemetryConfig.cpp index 355092e644..898929407a 100644 --- a/src/tests/libxrpl/telemetry/TelemetryConfig.cpp +++ b/src/tests/libxrpl/telemetry/TelemetryConfig.cpp @@ -117,6 +117,7 @@ TEST(TelemetryConfig, setup_defaults) EXPECT_TRUE(s.serviceInstanceId.empty()); EXPECT_TRUE(s.nodeId.empty()); EXPECT_EQ(s.tracesEndpoint, "http://localhost:4318/v1/traces"); + EXPECT_EQ(s.metricsEndpoint, "http://localhost:4318/v1/metrics"); EXPECT_FALSE(s.useTls); EXPECT_TRUE(s.tlsCertPath.empty()); EXPECT_DOUBLE_EQ(s.samplingRatio, 1.0); @@ -159,8 +160,8 @@ TEST(TelemetryConfig, parse_full_section) section.set("enabled", "1"); section.set("service_name", "my-rippled"); section.set("service_instance_id", "custom-id"); - section.set("exporter", "otlp_http"); section.set("traces_endpoint", "http://collector:4318/v1/traces"); + section.set("metrics_endpoint", "http://collector:4318/v1/metrics"); section.set("use_tls", "1"); section.set("tls_ca_cert", caCert); section.set("batch_size", "256"); @@ -178,6 +179,7 @@ TEST(TelemetryConfig, parse_full_section) EXPECT_EQ(setup.serviceName, "my-rippled"); EXPECT_EQ(setup.serviceInstanceId, "custom-id"); EXPECT_EQ(setup.tracesEndpoint, "http://collector:4318/v1/traces"); + EXPECT_EQ(setup.metricsEndpoint, "http://collector:4318/v1/metrics"); EXPECT_TRUE(setup.useTls); EXPECT_EQ(setup.tlsCertPath, caCert); EXPECT_EQ(setup.batchSize, 256u);