Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions opentelemetry-instrumentation-tower/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

### Changed

* **BREAKING**: Removed `with_meter()` method. The middleware now uses global meter and tracer providers by default via `opentelemetry::global::meter()` and `opentelemetry::global::tracer()`, with optional overrides via `with_tracer_provider()` and `with_meter_provider()` methods.
* **BREAKING**: Renamed types. Use the new names:
- `HTTPMetricsLayer` → `HTTPLayer`
- `HTTPMetricsService` → `HTTPService`
- `HTTPMetricsResponseFuture` → `HTTPResponseFuture`
- `HTTPMetricsLayerBuilder` → `HTTPLayerBuilder`
* Added OpenTelemetry trace support
* Migrate to use `opentelemetry-semantic-conventions` package for metric names and attribute keys instead of hardcoded strings
* Add dependency on otel semantic conventions crate and use constants from it instead of hardcoded attribute names. The values are unchanged
- `HTTP_SERVER_ACTIVE_REQUESTS_METRIC` now uses `semconv::metric::HTTP_SERVER_ACTIVE_REQUESTS`
Expand All @@ -20,6 +27,37 @@

* Add comprehensive test coverage for all HTTP server metrics with attribute validation

### Migration Guide

#### API Changes
Before:
```rust
use opentelemetry_instrumentation_tower::HTTPMetricsLayerBuilder;

let layer = HTTPMetricsLayerBuilder::builder()
.with_meter(meter)
.build()
.unwrap();
```

After:
```rust
use opentelemetry_instrumentation_tower::HTTPLayer;

// Set global providers first
global::set_meter_provider(meter_provider);
global::set_tracer_provider(tracer_provider); // for tracing support

// Then create the layer - simple API using global providers
let layer = HTTPLayer::new();
```

#### Type Name Changes
- Replace `HTTPMetricsLayerBuilder` with `HTTPLayerBuilder`
- Replace `HTTPMetricsLayer` with `HTTPLayer`
- Replace `HTTPMetricsService` with `HTTPService`
- Replace `HTTPMetricsResponseFuture` with `HTTPResponseFuture`

## v0.16.0

Initial release of OpenTelemetry Tower instrumentation middleware for HTTP metrics collection.
Expand Down
6 changes: 4 additions & 2 deletions opentelemetry-instrumentation-tower/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ rust-version = "1.75.0"

version = "0.16.0"
license = "Apache-2.0"
description = "OpenTelemetry Metrics Middleware for Tower-compatible Rust HTTP servers"
description = "OpenTelemetry Metrics and Tracing Middleware for Tower-compatible Rust HTTP servers"
homepage = "https://github.com/open-telemetry/opentelemetry-rust-contrib"
repository = "https://github.com/open-telemetry/opentelemetry-rust-contrib"
documentation = "https://docs.rs/tower-otel-http-metrics"
Expand All @@ -21,7 +21,8 @@ axum = { features = ["matched-path", "macros"], version = "0.8", default-feature
futures-util = { version = "0.3", default-features = false }
http = { version = "1", features = ["std"], default-features = false }
http-body = { version = "1", default-features = false }
opentelemetry = { workspace = true, features = ["futures", "metrics"]}
opentelemetry = { workspace = true, features = ["futures", "metrics", "trace"] }
opentelemetry_sdk = { workspace = true, features = ["trace"] }
opentelemetry-semantic-conventions = { workspace = true, features = ["semconv_experimental"] }
pin-project-lite = { version = "0.2", default-features = false }
tower-service = { version = "0.3", default-features = false }
Expand All @@ -31,6 +32,7 @@ tower-layer = { version = "0.3", default-features = false }
opentelemetry_sdk = { workspace = true, features = ["metrics", "testing"] }
tokio = { version = "1.0", features = ["macros", "rt"] }
tower = { version = "0.5", features = ["util"] }
tower-test = { version = "0.4" }

[lints]
workspace = true
36 changes: 34 additions & 2 deletions opentelemetry-instrumentation-tower/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
# Tower OTEL Metrics Middleware
# Tower OTEL HTTP Instrumentation Middleware

OpenTelemetry Metrics Middleware for Tower-compatible Rust HTTP servers.
OpenTelemetry HTTP Metrics and Tracing Middleware for Tower-compatible Rust HTTP servers.

This middleware provides both metrics and distributed tracing for HTTP requests, following OpenTelemetry semantic conventions.

## Features

- **HTTP Metrics**: Request duration, active requests, request/response body sizes
- **Distributed Tracing**: HTTP spans with semantic attributes
- **Semantic Conventions**: Uses OpenTelemetry semantic conventions for consistent attribute naming
- **Flexible Configuration**: Support for custom attribute extractors and tracer configuration
- **Framework Support**: Works with any Tower-compatible HTTP framework (Axum, Hyper, Tonic etc.)

## Usage

## Metrics

The middleware exports the following metrics:

- `http.server.request.duration` - Duration of HTTP requests
- `http.server.active_requests` - Number of active HTTP requests
- `http.server.request.body.size` - Size of HTTP request bodies
- `http.server.response.body.size` - Size of HTTP response bodies

## Tracing

HTTP spans are created with the following attributes (following OpenTelemetry semantic conventions):

- `http.request.method` - HTTP method
- `url.scheme` - URL scheme (http/https)
- `url.path` - Request path
- `url.full` - Full URL
- `user_agent.original` - User agent string
- `http.response.status_code` - HTTP response status code

## Examples

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use axum::routing::{get, post, put, Router};
use bytes::Bytes;
use opentelemetry::global;
use opentelemetry_instrumentation_tower as otel_tower_metrics;
use opentelemetry_instrumentation_tower::HTTPLayer;
use opentelemetry_otlp::MetricExporter;
use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider};
use std::time::Duration;

const SERVICE_NAME: &str = "example-axum-http-service";
Expand Down Expand Up @@ -40,28 +42,24 @@ async fn handle() -> Bytes {

#[tokio::main]
async fn main() {
let exporter = opentelemetry_otlp::MetricExporter::builder()
let exporter = MetricExporter::builder()
.with_tonic()
// .with_endpoint("http://localhost:4317") // default; leave out in favor of env var OTEL_EXPORTER_OTLP_ENDPOINT
.build()
.unwrap();

let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter)
let reader = PeriodicReader::builder(exporter)
.with_interval(_OTEL_METRIC_EXPORT_INTERVAL)
.build();

let meter_provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
let meter_provider = SdkMeterProvider::builder()
.with_reader(reader)
.with_resource(init_otel_resource())
.build();

global::set_meter_provider(meter_provider);
// init our otel metrics middleware
let global_meter = global::meter(SERVICE_NAME);
let otel_metrics_service_layer = otel_tower_metrics::HTTPMetricsLayerBuilder::builder()
.with_meter(global_meter)
.build()
.unwrap();

let otel_metrics_service_layer = HTTPLayer::new();

let app = Router::new()
.route("/", get(handle))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use http_body_util::Full;
use hyper::body::Bytes;
use hyper::{Request, Response};
use opentelemetry::global;
use opentelemetry_instrumentation_tower as otel_tower_metrics;
use opentelemetry_instrumentation_tower::HTTPLayer;
use opentelemetry_otlp::MetricExporter;
use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider};
use std::convert::Infallible;
use std::net::SocketAddr;
use std::time::Duration;
Expand Down Expand Up @@ -45,28 +47,24 @@ async fn handle(_req: Request<hyper::body::Incoming>) -> Result<Response<Full<By

#[tokio::main]
async fn main() {
let exporter = opentelemetry_otlp::MetricExporter::builder()
let exporter = MetricExporter::builder()
.with_tonic()
// .with_endpoint("http://localhost:4317") // default; leave out in favor of env var OTEL_EXPORTER_OTLP_ENDPOINT
.build()
.unwrap();

let reader = opentelemetry_sdk::metrics::PeriodicReader::builder(exporter)
let reader = PeriodicReader::builder(exporter)
.with_interval(_OTEL_METRIC_EXPORT_INTERVAL)
.build();

let meter_provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
let meter_provider = SdkMeterProvider::builder()
.with_reader(reader)
.with_resource(init_otel_resource())
.build();

global::set_meter_provider(meter_provider);
// init our otel metrics middleware
let global_meter = global::meter(SERVICE_NAME);
let otel_metrics_service_layer = otel_tower_metrics::HTTPMetricsLayerBuilder::builder()
.with_meter(global_meter)
.build()
.unwrap();

let otel_metrics_service_layer = HTTPLayer::new();

let tower_service = ServiceBuilder::new()
.layer(otel_metrics_service_layer)
Expand Down
Loading