@@ -181,16 +181,17 @@ fn selected_metrics_primary_endpoint<'a>(
181181 vector_use_v3_series : bool ,
182182) -> Option < ( & ' a str , bool ) > {
183183 if opw_enabled {
184+ let opw_url = opw_url. trim ( ) ;
184185 metrics_primary_url_can_resolve ( opw_url) . then_some ( ( opw_url, opw_use_v3_series) )
185186 } else if vector_enabled {
187+ let vector_url = vector_url. trim ( ) ;
186188 metrics_primary_url_can_resolve ( vector_url) . then_some ( ( vector_url, vector_use_v3_series) )
187189 } else {
188190 None
189191 }
190192}
191193
192194fn metrics_primary_url_can_resolve ( url : & str ) -> bool {
193- let url = url. trim ( ) ;
194195 if url. is_empty ( ) {
195196 return false ;
196197 }
@@ -403,10 +404,14 @@ pub struct DatadogMetricsConfiguration {
403404 vector_metrics_use_v3_api_series : bool ,
404405
405406 /// The Datadog site used to resolve the primary metrics endpoint.
407+ ///
408+ /// Defaults to `datadoghq.com`.
406409 #[ serde( default = "default_site" ) ]
407410 site : String ,
408411
409412 /// The optional explicit primary metrics endpoint.
413+ ///
414+ /// Defaults to unset, in which case `site` determines the endpoint.
410415 #[ serde( default , alias = "url" , deserialize_with = "deserialize_dd_url" ) ]
411416 dd_url : Option < String > ,
412417
@@ -427,6 +432,27 @@ impl DatadogMetricsConfiguration {
427432 self
428433 }
429434
435+ /// Restricts endpoint-aware protocol selection to a single overridden metrics endpoint.
436+ ///
437+ /// This mirrors a forwarder branch that replaces the normal primary endpoint and removes additional and
438+ /// OPW/Vector endpoints, such as Multi-Region Failover.
439+ pub fn with_metrics_endpoint_override ( mut self , dd_url : String ) -> Self {
440+ self . dd_url = Some ( dd_url) ;
441+ self . additional_endpoints = AdditionalEndpoints :: default ( ) ;
442+ self . observability_pipelines_worker_metrics_enabled = false ;
443+ self . vector_metrics_enabled = false ;
444+ self
445+ }
446+
447+ /// Forces series metrics to use V2 without producing V3 shadow payloads.
448+ ///
449+ /// This is used for local destinations that only accept the V2 series protocol, such as the Cluster Agent.
450+ pub fn with_v2_series_only ( mut self ) -> Self {
451+ self . data_plane_metrics_v3_series_enabled = false ;
452+ self . v3_api . series . shadow_sample_rate = 0.0 ;
453+ self
454+ }
455+
430456 fn v3_payload_limits ( & self ) -> V3PayloadLimits {
431457 V3PayloadLimits :: new (
432458 self . max_series_payload_size ,
@@ -2057,6 +2083,52 @@ serializer_experimental_use_v3_api:
20572083 assert ! ( !config. requires_v2_series( false ) . expect( "endpoints should resolve" ) ) ;
20582084 }
20592085
2086+ #[ test]
2087+ fn endpoint_override_uses_the_overridden_endpoint_protocol ( ) {
2088+ let config = v3_series_config (
2089+ r#"
2090+ dd_url: https://primary.example.com
2091+ data_plane_metrics_v3_series_enabled: true
2092+ use_v3_api_series_enabled: "false"
2093+ serializer_experimental_use_v3_api:
2094+ series:
2095+ endpoints:
2096+ - https://primary.example.com
2097+ - https://v3-mrf.example.com
2098+ "# ,
2099+ ) ;
2100+
2101+ let v2_mrf_config = config
2102+ . clone ( )
2103+ . with_metrics_endpoint_override ( "https://v2-mrf.example.com" . to_string ( ) ) ;
2104+ let v3_mrf_config = config. with_metrics_endpoint_override ( "https://v3-mrf.example.com" . to_string ( ) ) ;
2105+
2106+ assert ! ( v2_mrf_config
2107+ . requires_v2_series( false )
2108+ . expect( "V2 MRF endpoint should resolve" ) ) ;
2109+ assert ! ( !v3_mrf_config
2110+ . requires_v2_series( false )
2111+ . expect( "V3 MRF endpoint should resolve" ) ) ;
2112+ }
2113+
2114+ #[ test]
2115+ fn v2_series_only_override_keeps_v2_and_disables_shadowing ( ) {
2116+ let config = v3_series_config (
2117+ r#"
2118+ data_plane_metrics_v3_series_enabled: true
2119+ use_v3_api_series_enabled: "true"
2120+ serializer_experimental_use_v3_api:
2121+ series:
2122+ shadow_sample_rate: 1.0
2123+ "# ,
2124+ )
2125+ . with_v2_series_only ( ) ;
2126+
2127+ assert ! ( !config. data_plane_metrics_v3_series_enabled) ;
2128+ assert_eq ! ( 0.0 , config. v3_api. series. shadow_sample_rate) ;
2129+ assert ! ( config. requires_v2_series( false ) . expect( "endpoint should resolve" ) ) ;
2130+ }
2131+
20602132 #[ test]
20612133 fn agent_default_v3_does_not_enable_opw_only_encoder_mode ( ) {
20622134 let series_config = UseV3ApiSeriesConfig :: default ( ) ;
0 commit comments