From d5c86d9adc6f0e3f2612dcfd92b8b64e88ab20d7 Mon Sep 17 00:00:00 2001 From: Mark Ingram Date: Mon, 1 Jul 2024 23:44:01 +0100 Subject: [PATCH] [opentelemetry-otlp] adds an example HTTP exporter backed by Hyper (#1861) Co-authored-by: Cijo Thomas --- .../examples/basic-otlp-http/Cargo.toml | 11 +++++ .../examples/basic-otlp-http/README.md | 8 ++++ .../examples/basic-otlp-http/src/hyper.rs | 45 +++++++++++++++++++ .../examples/basic-otlp-http/src/main.rs | 30 ++++++------- 4 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 opentelemetry-otlp/examples/basic-otlp-http/src/hyper.rs diff --git a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml index 2c5e2d69cf..5357988ad5 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml +++ b/opentelemetry-otlp/examples/basic-otlp-http/Cargo.toml @@ -5,14 +5,25 @@ edition = "2021" license = "Apache-2.0" publish = false +[features] +default = ["reqwest"] +reqwest = ["opentelemetry-otlp/reqwest-client"] +hyper = ["dep:async-trait", "dep:http", "dep:hyper", "dep:opentelemetry-http", "dep:bytes"] + + [dependencies] once_cell = { workspace = true } opentelemetry = { path = "../../../opentelemetry" } opentelemetry_sdk = { path = "../../../opentelemetry-sdk", features = ["rt-tokio", "metrics", "logs"] } +opentelemetry-http = { path = "../../../opentelemetry-http", optional = true } opentelemetry-otlp = { path = "../..", features = ["http-proto", "reqwest-client", "logs"] } opentelemetry-appender-tracing = { path = "../../../opentelemetry-appender-tracing", default-features = false} opentelemetry-semantic-conventions = { path = "../../../opentelemetry-semantic-conventions" } +async-trait = { workspace = true, optional = true } +bytes = { workspace = true, optional = true } +http = { workspace = true, optional = true } +hyper = { workspace = true, features = ["client"], optional = true } tokio = { workspace = true, features = ["full"] } tracing = { workspace = true, features = ["std"]} tracing-core = { workspace = true } diff --git a/opentelemetry-otlp/examples/basic-otlp-http/README.md b/opentelemetry-otlp/examples/basic-otlp-http/README.md index 2608deafce..91394b1560 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/README.md +++ b/opentelemetry-otlp/examples/basic-otlp-http/README.md @@ -52,6 +52,14 @@ Run the app which exports logs, metrics and traces via OTLP to the collector cargo run ``` + +By default the app will use a `reqwest` client to send. A hyper 0.14 client can be used with the `hyper` feature enabled + +```shell +cargo run --no-default-features --features=hyper +``` + + ## View results You should be able to see something similar below with different time and ID in the same console that docker runs. diff --git a/opentelemetry-otlp/examples/basic-otlp-http/src/hyper.rs b/opentelemetry-otlp/examples/basic-otlp-http/src/hyper.rs new file mode 100644 index 0000000000..ff6e84a05d --- /dev/null +++ b/opentelemetry-otlp/examples/basic-otlp-http/src/hyper.rs @@ -0,0 +1,45 @@ +use async_trait::async_trait; +use bytes::Bytes; +use http::{Request, Response}; +use hyper::{ + client::{connect::Connect, HttpConnector}, + Body, Client, +}; +use opentelemetry_http::{HttpClient, HttpError, ResponseExt}; + +pub struct HyperClient { + inner: hyper::Client, +} + +impl Default for HyperClient { + fn default() -> Self { + Self { + inner: Client::new(), + } + } +} + +impl std::fmt::Debug for HyperClient { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("HyperClient") + .field("inner", &self.inner) + .finish() + } +} + +#[async_trait] +impl HttpClient for HyperClient { + async fn send(&self, request: Request>) -> Result, HttpError> { + let request = request.map(Body::from); + + let (parts, body) = self + .inner + .request(request) + .await? + .error_for_status()? + .into_parts(); + let body = hyper::body::to_bytes(body).await?; + + Ok(Response::from_parts(parts, body)) + } +} diff --git a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs index 4e73537ce8..3e7c41b48d 100644 --- a/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs +++ b/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs @@ -6,7 +6,7 @@ use opentelemetry::{ Key, KeyValue, }; use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; -use opentelemetry_otlp::WithExportConfig; +use opentelemetry_otlp::{HttpExporterBuilder, WithExportConfig}; use opentelemetry_sdk::trace::{self as sdktrace, Config}; use opentelemetry_sdk::{ logs::{self as sdklogs}, @@ -18,6 +18,9 @@ use tracing_subscriber::EnvFilter; use std::error::Error; +#[cfg(feature = "hyper")] +mod hyper; + static RESOURCE: Lazy = Lazy::new(|| { Resource::new(vec![KeyValue::new( opentelemetry_semantic_conventions::resource::SERVICE_NAME, @@ -25,26 +28,25 @@ static RESOURCE: Lazy = Lazy::new(|| { )]) }); +fn http_exporter() -> HttpExporterBuilder { + let exporter = opentelemetry_otlp::new_exporter().http(); + #[cfg(feature = "hyper")] + let exporter = exporter.with_http_client(hyper::HyperClient::default()); + exporter +} + fn init_logs() -> Result { opentelemetry_otlp::new_pipeline() .logging() .with_resource(RESOURCE.clone()) - .with_exporter( - opentelemetry_otlp::new_exporter() - .http() - .with_endpoint("http://localhost:4318/v1/logs"), - ) + .with_exporter(http_exporter().with_endpoint("http://localhost:4318/v1/logs")) .install_batch(opentelemetry_sdk::runtime::Tokio) } fn init_tracer_provider() -> Result { opentelemetry_otlp::new_pipeline() .tracing() - .with_exporter( - opentelemetry_otlp::new_exporter() - .http() - .with_endpoint("http://localhost:4318/v1/traces"), - ) + .with_exporter(http_exporter().with_endpoint("http://localhost:4318/v1/traces")) .with_trace_config(Config::default().with_resource(RESOURCE.clone())) .install_batch(opentelemetry_sdk::runtime::Tokio) } @@ -52,11 +54,7 @@ fn init_tracer_provider() -> Result { fn init_metrics() -> Result { opentelemetry_otlp::new_pipeline() .metrics(opentelemetry_sdk::runtime::Tokio) - .with_exporter( - opentelemetry_otlp::new_exporter() - .http() - .with_endpoint("http://localhost:4318/v1/metrics"), - ) + .with_exporter(http_exporter().with_endpoint("http://localhost:4318/v1/metrics")) .with_resource(RESOURCE.clone()) .build() }