uhg-custom-appollo-roouter 1.58.1

This is a customized Apollo Router, NOT the official apollo router, do not use
Documentation
# branch
- dev2
- dev-hcp ------ this is the branch to create cra

C:\GitHub\router\apollo-router\Cargo.toml
[package]
name = "uhg-custom-appollo-roouter"
version = "1.0.5"

C:\GitHub\router\apollo-router-benchmarks\Cargo.toml
uhg-custom-appollo-roouter = { path = "../apollo-router" }

basically replace all "apollo-router" with "uhg-custom-appollo-roouter"

1. C:\GitHub\router\apollo-router\src\axum_factory\utils.rs

impl PropagatingMakeSpan {
    fn create_span<B>(&mut self, request: &Request<B>) -> Span {
        // JASON customization - add labels: consumerName, roles, correlationId, cid, azure_region
        let consumer_name = match request.headers().get("consumername") {
            Some(s) => std::str::from_utf8(s.as_bytes()).unwrap(),
            None => ""
        };
        let role_id = match request.headers().get("roleid") {
            Some(s) => std::str::from_utf8(s.as_bytes()).unwrap(),
            None => ""
        };
        let id = String::from("");
        let correlation_id = match request.headers().get("X-Correlation-Id") {
            Some(s) => std::str::from_utf8(s.as_bytes()).unwrap(),
            None => {
                // can't get trace id here! leave it empty, will be overriden in plugin repo
                // match crate::tracer::TraceId::maybe_new().map(|t| t.to_string()) {
                //     Some(v) => { id = format!("hcp-{}", v); }
                //     None => { }
                // };

                &id
            }
        };        
        let cid = match request.headers().get("Optum-Cid-Ext") {
            Some(s) => std::str::from_utf8(s.as_bytes()).unwrap(),
            None => ""
        };
        let azure_region = std::env::var("AZURE_REGION").unwrap_or(String::from(""));
        // end of JASON customization
        
       

        tracing::error_span!(
                REQUEST_SPAN_NAME,
                "consumerName" = consumer_name,
                "roles" = role_id,
                "correlationId" = correlation_id,
                "cid" = cid,
                "azure_region" = azure_region,
                "http.method" = %request.method(),
                "http.route" = %request.uri(),
                "http.flavor" = ?request.version(),
                "http.status" = 500, // This prevents setting later
                "otel.name" = ::tracing::field::Empty,
                "otel.kind" = "SERVER",
                "graphql.operation.name" = ::tracing::field::Empty,
                "graphql.operation.type" = ::tracing::field::Empty,
                "apollo_router.license" = LICENSE_EXPIRED_SHORT_MESSAGE,
                "apollo_private.request" = true,
            )

        tracing::info_span!(
                REQUEST_SPAN_NAME,
                "consumerName" = consumer_name,
                "roles" = role_id,
                "correlationId" = correlation_id,
                "cid" = cid,
                "azure_region" = azure_region,
                "http.method" = %request.method(),
                "http.route" = %request.uri(),
                "http.flavor" = ?request.version(),
                "otel.name" = ::tracing::field::Empty,
                "otel.kind" = "SERVER",
                "graphql.operation.name" = ::tracing::field::Empty,
                "graphql.operation.type" = ::tracing::field::Empty,
                "apollo_private.request" = true,
            )

2. C:\GitHub\router\apollo-router\src\plugins\telemetry\metrics\prometheus.rs
    
    use crate::plugins::telemetry::metrics::custom_aggregator::CustomHistogramAggregator;

impl MetricsConfigurator for Config {
    fn apply(
        &self,
        mut builder: MetricsBuilder,
        metrics_config: &MetricsCommon,
    ) -> Result<MetricsBuilder, BoxError> {
        if self.enabled {
            let mut controller = controllers::basic(
                processors::factory(
                    // JASON customization - CustomHistogramAggregator
                    CustomHistogramAggregator::new(metrics_config),
                    aggregation::stateless_temporality_selector(),
                )
                .with_memory(true),
            )
            .with_resource(Resource::new(
                metrics_config
                    .resources
                    .clone()
                    .into_iter()
                    .map(|(k, v)| KeyValue::new(k, v)),
            ))
            .build();

            // Check the last controller to see if the resources are the same, if they are we can use it as is.
            // Otherwise go with the new controller and store it so that it can be committed during telemetry activation.
            if let Some(last_controller) = CONTROLLER.lock().expect("lock poisoned").clone() {
                if controller.resource() == last_controller.resource() {
                    tracing::debug!("prometheus controller can be reused");
                    controller = last_controller
                } else {
                    tracing::debug!("prometheus controller cannot be reused");
                }
            }
            NEW_CONTROLLER
                .lock()
                .expect("lock poisoned")
                .replace(controller.clone());

            let exporter = opentelemetry_prometheus::exporter(controller).try_init()?;

            builder = builder.with_custom_endpoint(
                self.listen.clone(),
                Endpoint::from_router_service(
                    self.path.clone(),
                    PrometheusService {
                        registry: exporter.registry().clone(),
                    }
                    .boxed(),
                ),
            );
            builder = builder.with_meter_provider(exporter.meter_provider()?);
            builder = builder.with_exporter(exporter);
            tracing::info!(
                "Prometheus endpoint exposed at {}{}",
                self.listen,
                self.path
            );
        }
        Ok(builder)
    }
}


3. new file:  C:\GitHub\router\apollo-router\src\plugins\telemetry\metrics\custom_aggregator.rs
use std::collections::HashMap;
use std::sync::Arc;

use opentelemetry::sdk::export::metrics::AggregatorSelector;
use opentelemetry::sdk::metrics::aggregators;
use opentelemetry::sdk::metrics::aggregators::Aggregator;
use opentelemetry::sdk::metrics::sdk_api::Descriptor;
use opentelemetry::sdk::metrics::sdk_api::InstrumentKind;
use crate::plugins::telemetry::config::MetricsCommon;

// JASON customization - add labels: consumerName, correlationId, cid

//
// Metrics CustomAggregator
// reference: https://github.com/open-telemetry/opentelemetry-rust/blob/bfeda30583f3df6733e8ebce2f5964f6a7b69b5c/examples/dynatrace/src/main.rs
//

#[derive(Debug, Clone)]
pub(crate) struct CustomHistogramAggregator {
    buckets: Option<HashMap<String, Vec<f64>>>,
    default_bucket: Vec<f64>
}

impl CustomHistogramAggregator {
    pub(crate) fn new(metrics_config: &MetricsCommon) -> CustomHistogramAggregator {
        let mut buckets: Option<HashMap<String, Vec<f64>>> = None;
        let mut default_bucket: Vec<f64> = vec![0.001, 0.005, 0.015, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 5.0, 10.0];

        if metrics_config.buckets.is_some() {
            let buckets_all = metrics_config.buckets.as_ref().unwrap().clone();

            if buckets_all.contains_key("default") {
                default_bucket = buckets_all.get("default").unwrap().clone();
            }

            buckets = Some(buckets_all);
        }

        CustomHistogramAggregator { 
            buckets, 
            default_bucket 
        }
    }
}

impl AggregatorSelector for CustomHistogramAggregator {
    fn aggregator_for(
        &self,
        descriptor: &Descriptor,
    ) -> Option<Arc<(dyn Aggregator + Sync + std::marker::Send + 'static)>> {
        match descriptor.instrument_kind() {
            // Histogram
            InstrumentKind::Histogram => {
                let boundaries = match &self.buckets {
                    Some(buckets) => {
                        match buckets.get(descriptor.name()) {
                            Some(found_buckets) => found_buckets,
                            None => &self.default_bucket
                        }
                    },
                    None => &self.default_bucket
                };

                Some(Arc::new(aggregators::histogram(boundaries)))
            },

            // Gauge
            InstrumentKind::GaugeObserver => Some(Arc::new(aggregators::last_value())),

            // InstrumentKind::Counter, UpDownCounter, CounterObserver, UpDownCounterObserver
            _ => Some(Arc::new(aggregators::sum()))
        }
    }
}

4. unit tests
    apollo-router/tests/snapshots/tracing_tests__traced_basic_composition.snap

    "entries": [
          [
            "consumerName",
            ""
          ],
          [
            "roles",
            ""
          ],
          [
            "correlationId",
            ""
          ],
          [
            "cid",
            ""
          ],
          [
            "azure_region",
            ""
          ],
          [
            "http.method",

    apollo-router/tests/snapshots/tracing_tests__traced_basic_request.snap

    "fields": {
            "names": [
              "consumerName",
              "roles",
              "correlationId",
              "cid",
              "azure_region",
              "http.method",

    apollo-router/tests/snapshots/tracing_tests__variables.snap
        contains changes shown in above 2 files
OSZAR »