Skip to content
Merged
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "tower-otel-http-metrics"
edition = "2021"
version = "0.1.0"
version = "0.2.0a0"
license = "MIT"
description = "OpenTelemetry Metrics Middleware for Tower-compatible Rust HTTP servers"
homepage = "https://github.com/francoposa/tower-otel-http-metrics"
Expand All @@ -15,13 +15,13 @@ default = []
axum = ["dep:axum"]

[dependencies]
axum = { features = ["matched-path"], version = "0.6", default-features = false, optional = true }
axum = { features = ["matched-path"], version = "0.7", default-features = false, optional = true }
futures-core = { version = "0.3", default-features = false }
futures-util = { version = "0.3", default-features = false }
http = { version = "0.2", default-features = false }
http-body = { version = "0.4", default-features = false }
opentelemetry = { version = "0.20", default-features = false }
opentelemetry_api = { version = "0.20", default-features = false }
opentelemetry-otlp = { version = "0.13", default-features = false }
http = { version = "1", default-features = false }
opentelemetry = { version = "0.21", default-features = false }
opentelemetry_api = { version = "0.20", features = ["metrics"], default-features = false }
pin-project-lite = { version = "0.2", default-features = false }
tower = { version = "0.4", default-features = false }
tower-service = { version = "0.3", default-features = false }
tower-layer = { version = "0.3", default-features = false }
90 changes: 58 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,30 @@ Adding OpenTelementry HTTP Server metrics using the [`Axum`](https://docs.rs/axu
over a Tower-compatible [`Hyper`](https://docs.rs/hyper/latest/hyper) Service:

```rust
use std::convert::Infallible;
use std::borrow::Cow;
use std::time::Duration;

use axum::routing::{get, post, put, Router};
use hyper::{Body, Request, Response, Server};
use opentelemetry::sdk::resource::{
use bytes::Bytes;
use opentelemetry_api::global;
use opentelemetry_otlp::{
WithExportConfig, {self},
};
use opentelemetry_sdk::resource::{
EnvResourceDetector, SdkProvidedResourceDetector, TelemetryResourceDetector,
};
use opentelemetry::sdk::Resource;
use opentelemetry_otlp::{self, WithExportConfig};

use opentelemetry_sdk::Resource;
use tower_otel_http_metrics;

const SERVICE_NAME: &str = "example-axum-http-service";

async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("hello, world")))
async fn handle() -> Bytes {
Bytes::from("hello, world")
}

#[tokio::main]
async fn main() {
// init otel resource config
// init otel resource config
let otlp_resource_detected = Resource::from_detectors(
Duration::from_secs(3),
vec![
Expand All @@ -56,7 +58,7 @@ async fn main() {
// this configuration interface is annoyingly slightly different from the tracing one
// also the above documentation is outdated, it took awhile to get this correct one working
opentelemetry_otlp::new_pipeline()
.metrics(opentelemetry::runtime::Tokio)
.metrics(opentelemetry_sdk::runtime::Tokio)
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
Expand All @@ -68,19 +70,23 @@ async fn main() {
.unwrap();

// init our otel metrics middleware
let otel_metrics_service_layer =
tower_otel_http_metrics::HTTPMetricsLayer::new(String::from(SERVICE_NAME));
let global_meter = global::meter(Cow::from(SERVICE_NAME));
let otel_metrics_service_layer = tower_otel_http_metrics::HTTPMetricsLayerBuilder::new()
.with_meter(global_meter)
.build()
.unwrap();

let app = Router::new()
.route("/", get(handle))
.route("/", post(handle))
.route("/", put(handle))
.layer(otel_metrics_service_layer);

let server = Server::bind(&"0.0.0.0:5000".parse().unwrap()).serve(app.into_make_service());
let listener = tokio::net::TcpListener::bind("0.0.0.0:5000").await.unwrap();
let server = axum::serve(listener, app);

if let Err(e) = server.await {
eprintln!("server error: {}", e);
if let Err(err) = server.await {
eprintln!("server error: {}", err);
}
}
```
Expand All @@ -91,25 +97,31 @@ Adding OpenTelementry HTTP Server metrics to a bare-bones Tower-compatible Servi
using [`Hyper`](https://docs.rs/crate/hyper/latest):

```rust
use std::borrow::Cow;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::time::Duration;

use hyper::{Body, Request, Response, Server};
use opentelemetry::sdk::resource::{
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::{Request, Response};
use opentelemetry_api::global;
use opentelemetry_otlp::{
WithExportConfig, {self},
};
use opentelemetry_sdk::resource::{
EnvResourceDetector, SdkProvidedResourceDetector, TelemetryResourceDetector,
};
use opentelemetry::sdk::Resource;
use opentelemetry_otlp::{self, WithExportConfig};
use tower::make::Shared;
use opentelemetry_sdk::Resource;
use tokio::net::TcpListener;
use tower::ServiceBuilder;

use tower_otel_http_metrics;

const SERVICE_NAME: &str = "example-tower-http-service";

async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("hello, world")))
async fn handle(_req: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
Ok(Response::new(Full::new(Bytes::from("hello, world"))))
}

#[tokio::main]
Expand All @@ -133,7 +145,7 @@ async fn main() {
// this configuration interface is annoyingly slightly different from the tracing one
// also the above documentation is outdated, it took awhile to get this correct one working
opentelemetry_otlp::new_pipeline()
.metrics(opentelemetry::runtime::Tokio)
.metrics(opentelemetry_sdk::runtime::Tokio)
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
Expand All @@ -145,20 +157,34 @@ async fn main() {
.unwrap();

// init our otel metrics middleware
let otel_metrics_service_layer =
tower_otel_http_metrics::HTTPMetricsLayer::new(String::from(SERVICE_NAME));
let global_meter = global::meter(Cow::from(SERVICE_NAME));
let otel_metrics_service_layer = tower_otel_http_metrics::HTTPMetricsLayerBuilder::new()
.with_meter(global_meter)
.build()
.unwrap();

let service = ServiceBuilder::new()
let tower_service = ServiceBuilder::new()
.layer(otel_metrics_service_layer)
.service_fn(handle);
let hyper_service = hyper_util::service::TowerToHyperService::new(tower_service);

let addr = SocketAddr::from(([127, 0, 0, 1], 5000));
let listener = TcpListener::bind(addr).await.unwrap();

let make_service = Shared::new(service);
loop {
let (stream, _) = listener.accept().await.unwrap();

let addr = SocketAddr::from(([0, 0, 0, 0], 5000));
let server = Server::bind(&addr).serve(make_service);
let io = hyper_util::rt::TokioIo::new(stream);
let service_clone = hyper_service.clone();

if let Err(e) = server.await {
eprintln!("server error: {}", e);
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.serve_connection(io, service_clone)
.await
{
eprintln!("server error: {}", err);
}
});
}
}
```
16 changes: 10 additions & 6 deletions examples/axum-http-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ edition = "2021"

[dependencies]
tower_otel_http_metrics = { path = "../../../tower-otel-http-metrics", package = "tower-otel-http-metrics", features = ["axum"], default-features = false }
axum = { version = "0.6.*", default-features = false }
hyper = { version = "0.14.*", default-features = false }
opentelemetry = { version = "0.20.*", features = ["rt-tokio"], default-features = false }
opentelemetry-otlp = { version = "0.13.0", features = ["metrics"], default-features = false }
opentelemetry-semantic-conventions = { version = "0.12.0", default-features = false }
tokio = { version = "*", features = ["rt-multi-thread"], default-features = false }
axum = { features = ["http1", "tokio"], version = "0.7", default-features = false }
hyper = { version = "1", default-features = false }
bytes = { version = "1", default-features = false }
http-body-util = { version = "0.1" }
opentelemetry = { version = "0.21", default-features = false }
opentelemetry_sdk = { version = "0.21", features = ["rt-tokio"], default-features = false }
opentelemetry_api = { version = "0.20", default-features = false }
opentelemetry-semantic-conventions = { version = "0.13", default-features = false }
opentelemetry-otlp = { version = "0.14", features = ["metrics", "grpc-tonic"], default-features = false }
tokio = { version = "1", features = ["rt-multi-thread"], default-features = false }
34 changes: 20 additions & 14 deletions examples/axum-http-service/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
use std::convert::Infallible;
use std::borrow::Cow;
use std::time::Duration;

use axum::routing::{get, post, put, Router};
use hyper::{Body, Request, Response, Server};
use opentelemetry::sdk::resource::{
use bytes::Bytes;
use opentelemetry_api::global;
use opentelemetry_otlp::{
WithExportConfig, {self},
};
use opentelemetry_sdk::resource::{
EnvResourceDetector, SdkProvidedResourceDetector, TelemetryResourceDetector,
};
use opentelemetry::sdk::Resource;
use opentelemetry_otlp::{self, WithExportConfig};

use opentelemetry_sdk::Resource;
use tower_otel_http_metrics;

const SERVICE_NAME: &str = "example-axum-http-service";

async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("hello, world")))
async fn handle() -> Bytes {
Bytes::from("hello, world")
}

#[tokio::main]
Expand All @@ -38,7 +40,7 @@ async fn main() {
// this configuration interface is annoyingly slightly different from the tracing one
// also the above documentation is outdated, it took awhile to get this correct one working
opentelemetry_otlp::new_pipeline()
.metrics(opentelemetry::runtime::Tokio)
.metrics(opentelemetry_sdk::runtime::Tokio)
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
Expand All @@ -50,18 +52,22 @@ async fn main() {
.unwrap();

// init our otel metrics middleware
let otel_metrics_service_layer =
tower_otel_http_metrics::HTTPMetricsLayer::new(String::from(SERVICE_NAME));
let global_meter = global::meter(Cow::from(SERVICE_NAME));
let otel_metrics_service_layer = tower_otel_http_metrics::HTTPMetricsLayerBuilder::new()
.with_meter(global_meter)
.build()
.unwrap();

let app = Router::new()
.route("/", get(handle))
.route("/", post(handle))
.route("/", put(handle))
.layer(otel_metrics_service_layer);

let server = Server::bind(&"0.0.0.0:5000".parse().unwrap()).serve(app.into_make_service());
let listener = tokio::net::TcpListener::bind("0.0.0.0:5000").await.unwrap();
let server = axum::serve(listener, app);

if let Err(e) = server.await {
eprintln!("server error: {}", e);
if let Err(err) = server.await {
eprintln!("server error: {}", err);
}
}
18 changes: 11 additions & 7 deletions examples/tower-http-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ edition = "2021"

[dependencies]
tower_otel_http_metrics = { path = "../../../tower-otel-http-metrics", package = "tower-otel-http-metrics", default-features = false }
hyper = { version = "0.14.*", default-features = false }
opentelemetry = { version = "0.20.*", features = ["rt-tokio"], default-features = false }
opentelemetry-otlp = { version = "0.13.0", features = ["metrics"], default-features = false }
opentelemetry-semantic-conventions = { version = "0.12.0", default-features = false }
tokio = { version = "*", features = ["rt-multi-thread"], default-features = false }
tower = { version = "*", features = ["make"], default-features = false }
tower-http = { version = "*", default-features = false }
hyper = { version = "1", default-features = false }
http-body-util = { version = "0.1", default-features = false }
hyper-util = { version = "0.1", features = ["http1", "service", "server", "tokio"], default-features = false }
opentelemetry = { version = "0.21", default-features = false }
opentelemetry_sdk = { version = "0.21", features = ["rt-tokio"], default-features = false }
opentelemetry_api = { version = "0.20", default-features = false }
opentelemetry-semantic-conventions = { version = "0.13", default-features = false }
opentelemetry-otlp = { version = "0.14", features = ["metrics", "grpc-tonic"], default-features = false }
tokio = { version = "1", features = ["rt-multi-thread"], default-features = false }
tower = { version = "0.4", default-features = false }
tower-http = { version = "0.5", default-features = false }
54 changes: 37 additions & 17 deletions examples/tower-http-service/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
use std::borrow::Cow;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::time::Duration;

use hyper::{Body, Request, Response, Server};
use opentelemetry::sdk::resource::{
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::{Request, Response};
use opentelemetry_api::global;
use opentelemetry_otlp::{
WithExportConfig, {self},
};
use opentelemetry_sdk::resource::{
EnvResourceDetector, SdkProvidedResourceDetector, TelemetryResourceDetector,
};
use opentelemetry::sdk::Resource;
use opentelemetry_otlp::{self, WithExportConfig};
use tower::make::Shared;
use opentelemetry_sdk::Resource;
use tokio::net::TcpListener;
use tower::ServiceBuilder;

use tower_otel_http_metrics;

const SERVICE_NAME: &str = "example-tower-http-service";

async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("hello, world")))
async fn handle(_req: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
Ok(Response::new(Full::new(Bytes::from("hello, world"))))
}

#[tokio::main]
Expand All @@ -40,7 +46,7 @@ async fn main() {
// this configuration interface is annoyingly slightly different from the tracing one
// also the above documentation is outdated, it took awhile to get this correct one working
opentelemetry_otlp::new_pipeline()
.metrics(opentelemetry::runtime::Tokio)
.metrics(opentelemetry_sdk::runtime::Tokio)
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
Expand All @@ -52,19 +58,33 @@ async fn main() {
.unwrap();

// init our otel metrics middleware
let otel_metrics_service_layer =
tower_otel_http_metrics::HTTPMetricsLayer::new(String::from(SERVICE_NAME));
let global_meter = global::meter(Cow::from(SERVICE_NAME));
let otel_metrics_service_layer = tower_otel_http_metrics::HTTPMetricsLayerBuilder::new()
.with_meter(global_meter)
.build()
.unwrap();

let service = ServiceBuilder::new()
let tower_service = ServiceBuilder::new()
.layer(otel_metrics_service_layer)
.service_fn(handle);
let hyper_service = hyper_util::service::TowerToHyperService::new(tower_service);

let addr = SocketAddr::from(([127, 0, 0, 1], 5000));
let listener = TcpListener::bind(addr).await.unwrap();

let make_service = Shared::new(service);
loop {
let (stream, _) = listener.accept().await.unwrap();

let addr = SocketAddr::from(([0, 0, 0, 0], 5000));
let server = Server::bind(&addr).serve(make_service);
let io = hyper_util::rt::TokioIo::new(stream);
let service_clone = hyper_service.clone();

if let Err(e) = server.await {
eprintln!("server error: {}", e);
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.serve_connection(io, service_clone)
.await
{
eprintln!("server error: {}", err);
}
});
}
}
Loading