Skip to content

Commit

Permalink
[opentelemetry-otlp] adds an example HTTP exporter backed by a Hyper …
Browse files Browse the repository at this point in the history
…0.2 Client

Resolves #1659
  • Loading branch information
markdingram committed Jun 4, 2024
1 parent 406cc31 commit 3f585e8
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 0 deletions.
1 change: 1 addition & 0 deletions opentelemetry-otlp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ now use `.with_resource(RESOURCE::default())` to configure Resource when using
These methods would also no longer set the global tracer provider. It would now be the responsibility of users to set it by calling `global::set_tracer_provider(tracer_provider.clone());`. Refer to the [basic-otlp](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry-otlp/examples/basic-otlp/src/main.rs) and [basic-otlp-http](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry-otlp/examples/basic-otlp-http/src/main.rs) examples on how to initialize OTLP Trace Exporter.
- **Breaking** Correct the misspelling of "webkpi" to "webpki" in features [#1842](https://github.com/open-telemetry/opentelemetry-rust/pull/1842)
- Bump MSRV to 1.70 [#1840](https://github.com/open-telemetry/opentelemetry-rust/pull/1840)
- Adds `basic-otlp-http-hyper` example showing how to export with a custom Hyper Client

## v0.16.0

Expand Down
20 changes: 20 additions & 0 deletions opentelemetry-otlp/examples/basic-otlp-http-hyper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "basic-otlp-http-hyper"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
publish = false

[dependencies]
async-trait = { workspace = true }
bytes = { workspace = true }
once_cell = { workspace = true }
opentelemetry = { path = "../../../opentelemetry" }
opentelemetry_sdk = { path = "../../../opentelemetry-sdk", features = ["rt-tokio"] }
opentelemetry-http = { path = "../../../opentelemetry-http" }
opentelemetry-otlp = { path = "../..", features = ["http-proto"] }
opentelemetry-semantic-conventions = { path = "../../../opentelemetry-semantic-conventions" }

http = { workspace = true }
hyper = { workspace = true, features = ["client"] }
tokio = { workspace = true, features = ["full"] }
116 changes: 116 additions & 0 deletions opentelemetry-otlp/examples/basic-otlp-http-hyper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Basic OTLP exporter Example - Hyper 0.14

This example shows how to setup OpenTelemetry OTLP exporter for traces to exports them to the [OpenTelemetry
Collector](https://github.com/open-telemetry/opentelemetry-collector) via OTLP
over HTTP/protobuf. The Collector then sends the data to the appropriate
backend, in this case, the logging Exporter, which displays data to console.

This example uses a simple implementation of the `HttpClient` backed by a Hyper Client.

## Usage

### `docker-compose`

By default runs against the `otel/opentelemetry-collector:latest` image, and uses `reqwest-client`
as the http client, using http as the transport.

```shell
docker-compose up
```

In another terminal run the application `cargo run`

The docker-compose terminal will display traces.

Press Ctrl+C to stop the collector, and then tear it down:

```shell
docker-compose down
```

### Manual

If you don't want to use `docker-compose`, you can manually run the `otel/opentelemetry-collector` container
and inspect the logs to see traces being transferred.

On Unix based systems use:

```shell
# From the current directory, run `opentelemetry-collector`
docker run --rm -it -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
```

On Windows use:

```shell
# From the current directory, run `opentelemetry-collector`
docker run --rm -it -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
```

Run the app which exports traces via OTLP to the collector

```shell
cargo run
```

## View results

You should be able to see something similar below with different time and ID in the same console that docker runs.

### Span

```text
...
2024-06-04T07:39:05.722Z info ResourceSpans #0
Resource SchemaURL:
Resource attributes:
-> service.name: Str(basic-otlp-http-hyper)
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope basic
Span #0
Trace ID : 0e833514074391284d809e71afd34931
Parent ID : b5ce8f2bdedd0698
ID : c2555b67b2072134
Name : Sub operation...
Kind : Internal
Start time : 2024-06-04 07:39:05.698201 +0000 UTC
End time : 2024-06-04 07:39:05.698213 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> another.key: Str(yes)
Events:
SpanEvent #0
-> Name: Sub span event
-> Timestamp: 2024-06-04 07:39:05.698206 +0000 UTC
-> DroppedAttributesCount: 0
ResourceSpans #1
Resource SchemaURL:
Resource attributes:
-> service.name: Str(basic-otlp-http-hyper)
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope basic
Span #0
Trace ID : 0e833514074391284d809e71afd34931
Parent ID :
ID : b5ce8f2bdedd0698
Name : Main operation
Kind : Internal
Start time : 2024-06-04 07:39:05.698168 +0000 UTC
End time : 2024-06-04 07:39:05.698223 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> another.key: Str(yes)
Events:
SpanEvent #0
-> Name: Nice operation!
-> Timestamp: 2024-06-04 07:39:05.698186 +0000 UTC
-> DroppedAttributesCount: 0
-> Attributes::
-> bogons: Int(100)
{"kind": "exporter", "data_type": "traces", "name": "logging"}
...
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: "2"
services:

# Collector
otel-collector:
image: otel/opentelemetry-collector:latest
command: ["--config=/etc/otel-collector-config.yaml", "${OTELCOL_ARGS}"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4318:4318" # OTLP HTTP receiver




Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# This is a configuration file for the OpenTelemetry Collector intended to be
# used in conjunction with the opentelemetry-otlp example.
#
# For more information about the OpenTelemetry Collector see:
# https://github.com/open-telemetry/opentelemetry-collector
#
receivers:
otlp:
protocols:
grpc:
http:

exporters:
logging:
loglevel: debug

service:
pipelines:
traces:
receivers: [otlp]
exporters: [logging]
107 changes: 107 additions & 0 deletions opentelemetry-otlp/examples/basic-otlp-http-hyper/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use async_trait::async_trait;
use bytes::Bytes;
use http::{Request, Response};
use hyper::client::{connect::Connect, HttpConnector};
use hyper::{Body, Client};
use once_cell::sync::Lazy;
use opentelemetry::{
global,
trace::{TraceContextExt, TraceError, Tracer, TracerProvider as _},
Key, KeyValue,
};
use opentelemetry_http::{HttpClient, HttpError, ResponseExt};
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::trace::{self as sdktrace, Config};
use opentelemetry_sdk::Resource;

use std::error::Error;

static RESOURCE: Lazy<Resource> = Lazy::new(|| {
Resource::new(vec![KeyValue::new(
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
"basic-otlp-http-hyper",
)])
});

struct HyperClient<C> {
inner: hyper::Client<C>,
}

impl Default for HyperClient<HttpConnector> {
fn default() -> Self {
Self {
inner: Client::new(),
}
}
}

impl<C> std::fmt::Debug for HyperClient<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HyperClient")
.field("inner", &self.inner)
.finish()
}
}

#[async_trait]
impl<C: Connect + Clone + Send + Sync + 'static> HttpClient for HyperClient<C> {
async fn send(&self, request: Request<Vec<u8>>) -> Result<Response<Bytes>, 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))
}
}

fn init_tracer_provider() -> Result<sdktrace::TracerProvider, TraceError> {
opentelemetry_otlp::new_pipeline()
.tracing()
.with_exporter(
opentelemetry_otlp::new_exporter()
.http()
.with_http_client(HyperClient::default())
.with_endpoint("http://localhost:4318/v1/traces"),
)
.with_trace_config(Config::default().with_resource(RESOURCE.clone()))
.install_batch(opentelemetry_sdk::runtime::Tokio)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
let result = init_tracer_provider();
assert!(
result.is_ok(),
"Init tracer failed with error: {:?}",
result.err()
);

let tracer_provider = result.unwrap();
global::set_tracer_provider(tracer_provider.clone());

let tracer = global::tracer_provider().tracer_builder("basic").build();

tracer.in_span("Main operation", |cx| {
let span = cx.span();
span.add_event(
"Nice operation!".to_string(),
vec![Key::new("bogons").i64(100)],
);
span.set_attribute(KeyValue::new("another.key", "yes"));

tracer.in_span("Sub operation...", |cx| {
let span = cx.span();
span.set_attribute(KeyValue::new("another.key", "yes"));
span.add_event("Sub span event", vec![]);
});
});

global::shutdown_tracer_provider();
Ok(())
}

0 comments on commit 3f585e8

Please sign in to comment.