rs-zero 0.2.11

Rust-first microservice framework inspired by go-zero engineering practices
Documentation
#![cfg(feature = "otlp")]

use std::{collections::BTreeMap, time::Duration};

use rs_zero::observability::{OtlpProtocol, OtlpTraceConfig, install_otlp_tracing};
use tokio::net::TcpStream;
use tracing::{Level, span};

#[tokio::test]
#[ignore = "requires external OTLP collector from examples/production-adapters/docker-compose.external.yml"]
async fn observability_otlp_external_exports_span_and_shutdowns() {
    let endpoint = std::env::var("RS_ZERO_OTLP_ENDPOINT")
        .unwrap_or_else(|_| "http://127.0.0.1:14318".to_string());
    assert_collector_port_is_open(&endpoint).await;

    let mut resource = BTreeMap::new();
    resource.insert(
        "service.name".to_string(),
        "rs-zero-otlp-external".to_string(),
    );

    let handle = install_otlp_tracing(
        OtlpTraceConfig {
            endpoint: http_trace_endpoint(&endpoint),
            protocol: OtlpProtocol::HttpProtobuf,
            resource,
            timeout: Duration::from_secs(5),
            ..OtlpTraceConfig::default()
        },
        "info".to_string(),
    )
    .expect("install otlp tracing");

    let span = span!(Level::INFO, "rs_zero_otlp_external", adapter = "otlp");
    let entered = span.enter();
    tracing::info!(event = "external_otlp_probe", "sending external otlp span");
    drop(entered);
    drop(span);

    handle.flush().expect("flush otlp spans");
    handle.shutdown().expect("shutdown otlp exporter");
}

async fn assert_collector_port_is_open(endpoint: &str) {
    let authority = endpoint_authority(endpoint);
    TcpStream::connect(&authority)
        .await
        .unwrap_or_else(|error| panic!("connect OTLP collector at {authority}: {error}"));
}

fn http_trace_endpoint(endpoint: &str) -> String {
    let scheme = if endpoint.starts_with("https://") {
        "https"
    } else {
        "http"
    };
    format!("{scheme}://{}/v1/traces", endpoint_authority(endpoint))
}

fn endpoint_authority(endpoint: &str) -> String {
    endpoint
        .strip_prefix("http://")
        .or_else(|| endpoint.strip_prefix("https://"))
        .unwrap_or(endpoint)
        .split('/')
        .next()
        .unwrap_or(endpoint)
        .to_string()
}