Skip to content

Commit 7a19423

Browse files
committed
[opentelemetry-otlp] adds an example HTTP exporter backed by a Hyper 0.2 Client
Resolves open-telemetry#1659
1 parent 406cc31 commit 7a19423

File tree

6 files changed

+280
-0
lines changed

6 files changed

+280
-0
lines changed

opentelemetry-otlp/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ now use `.with_resource(RESOURCE::default())` to configure Resource when using
1616
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.
1717
- **Breaking** Correct the misspelling of "webkpi" to "webpki" in features [#1842](https://github.com/open-telemetry/opentelemetry-rust/pull/1842)
1818
- Bump MSRV to 1.70 [#1840](https://github.com/open-telemetry/opentelemetry-rust/pull/1840)
19+
- Adds `basic-otlp-http-hyper` example showing how to export with a custom Hyper Client
1920

2021
## v0.16.0
2122

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "basic-otlp-http-hyper"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "Apache-2.0"
6+
publish = false
7+
8+
[dependencies]
9+
async-trait = { workspace = true }
10+
bytes = { workspace = true }
11+
once_cell = { workspace = true }
12+
opentelemetry = { path = "../../../opentelemetry" }
13+
opentelemetry_sdk = { path = "../../../opentelemetry-sdk", features = ["rt-tokio"] }
14+
opentelemetry-http = { path = "../../../opentelemetry-http" }
15+
opentelemetry-otlp = { path = "../..", features = ["http-proto"] }
16+
opentelemetry-semantic-conventions = { path = "../../../opentelemetry-semantic-conventions" }
17+
18+
http = { workspace = true }
19+
hyper = { workspace = true, features = ["client"] }
20+
tokio = { workspace = true, features = ["full"] }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Basic OTLP exporter Example - Hyper 0.2
2+
3+
This example shows how to setup OpenTelemetry OTLP exporter for traces to exports them to the [OpenTelemetry
4+
Collector](https://github.com/open-telemetry/opentelemetry-collector) via OTLP
5+
over HTTP/protobuf. The Collector then sends the data to the appropriate
6+
backend, in this case, the logging Exporter, which displays data to console.
7+
8+
This example uses a simple implementation of the `HttpClient` backed by a Hyper Client.
9+
10+
## Usage
11+
12+
### `docker-compose`
13+
14+
By default runs against the `otel/opentelemetry-collector:latest` image, and uses `reqwest-client`
15+
as the http client, using http as the transport.
16+
17+
```shell
18+
docker-compose up
19+
```
20+
21+
In another terminal run the application `cargo run`
22+
23+
The docker-compose terminal will display traces.
24+
25+
Press Ctrl+C to stop the collector, and then tear it down:
26+
27+
```shell
28+
docker-compose down
29+
```
30+
31+
### Manual
32+
33+
If you don't want to use `docker-compose`, you can manually run the `otel/opentelemetry-collector` container
34+
and inspect the logs to see traces being transferred.
35+
36+
On Unix based systems use:
37+
38+
```shell
39+
# From the current directory, run `opentelemetry-collector`
40+
docker run --rm -it -p 4318:4318 -v $(pwd):/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
41+
```
42+
43+
On Windows use:
44+
45+
```shell
46+
# From the current directory, run `opentelemetry-collector`
47+
docker run --rm -it -p 4318:4318 -v "%cd%":/cfg otel/opentelemetry-collector:latest --config=/cfg/otel-collector-config.yaml
48+
```
49+
50+
Run the app which exports traces via OTLP to the collector
51+
52+
```shell
53+
cargo run
54+
```
55+
56+
## View results
57+
58+
You should be able to see something similar below with different time and ID in the same console that docker runs.
59+
60+
### Span
61+
62+
```text
63+
...
64+
2024-06-04T07:39:05.722Z info ResourceSpans #0
65+
Resource SchemaURL:
66+
Resource attributes:
67+
-> service.name: Str(basic-otlp-http-hyper)
68+
ScopeSpans #0
69+
ScopeSpans SchemaURL:
70+
InstrumentationScope basic
71+
Span #0
72+
Trace ID : 0e833514074391284d809e71afd34931
73+
Parent ID : b5ce8f2bdedd0698
74+
ID : c2555b67b2072134
75+
Name : Sub operation...
76+
Kind : Internal
77+
Start time : 2024-06-04 07:39:05.698201 +0000 UTC
78+
End time : 2024-06-04 07:39:05.698213 +0000 UTC
79+
Status code : Unset
80+
Status message :
81+
Attributes:
82+
-> another.key: Str(yes)
83+
Events:
84+
SpanEvent #0
85+
-> Name: Sub span event
86+
-> Timestamp: 2024-06-04 07:39:05.698206 +0000 UTC
87+
-> DroppedAttributesCount: 0
88+
ResourceSpans #1
89+
Resource SchemaURL:
90+
Resource attributes:
91+
-> service.name: Str(basic-otlp-http-hyper)
92+
ScopeSpans #0
93+
ScopeSpans SchemaURL:
94+
InstrumentationScope basic
95+
Span #0
96+
Trace ID : 0e833514074391284d809e71afd34931
97+
Parent ID :
98+
ID : b5ce8f2bdedd0698
99+
Name : Main operation
100+
Kind : Internal
101+
Start time : 2024-06-04 07:39:05.698168 +0000 UTC
102+
End time : 2024-06-04 07:39:05.698223 +0000 UTC
103+
Status code : Unset
104+
Status message :
105+
Attributes:
106+
-> another.key: Str(yes)
107+
Events:
108+
SpanEvent #0
109+
-> Name: Nice operation!
110+
-> Timestamp: 2024-06-04 07:39:05.698186 +0000 UTC
111+
-> DroppedAttributesCount: 0
112+
-> Attributes::
113+
-> bogons: Int(100)
114+
{"kind": "exporter", "data_type": "traces", "name": "logging"}
115+
...
116+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: "2"
2+
services:
3+
4+
# Collector
5+
otel-collector:
6+
image: otel/opentelemetry-collector:latest
7+
command: ["--config=/etc/otel-collector-config.yaml", "${OTELCOL_ARGS}"]
8+
volumes:
9+
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
10+
ports:
11+
- "4318:4318" # OTLP HTTP receiver
12+
13+
14+
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# This is a configuration file for the OpenTelemetry Collector intended to be
2+
# used in conjunction with the opentelemetry-otlp example.
3+
#
4+
# For more information about the OpenTelemetry Collector see:
5+
# https://github.com/open-telemetry/opentelemetry-collector
6+
#
7+
receivers:
8+
otlp:
9+
protocols:
10+
grpc:
11+
http:
12+
13+
exporters:
14+
logging:
15+
loglevel: debug
16+
17+
service:
18+
pipelines:
19+
traces:
20+
receivers: [otlp]
21+
exporters: [logging]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use async_trait::async_trait;
2+
use bytes::Bytes;
3+
use http::{Request, Response};
4+
use hyper::client::{connect::Connect, HttpConnector};
5+
use hyper::{Body, Client};
6+
use once_cell::sync::Lazy;
7+
use opentelemetry::{
8+
global,
9+
trace::{TraceContextExt, TraceError, Tracer, TracerProvider as _},
10+
Key, KeyValue,
11+
};
12+
use opentelemetry_http::{HttpClient, HttpError, ResponseExt};
13+
use opentelemetry_otlp::WithExportConfig;
14+
use opentelemetry_sdk::trace::{self as sdktrace, Config};
15+
use opentelemetry_sdk::Resource;
16+
17+
use std::error::Error;
18+
19+
static RESOURCE: Lazy<Resource> = Lazy::new(|| {
20+
Resource::new(vec![KeyValue::new(
21+
opentelemetry_semantic_conventions::resource::SERVICE_NAME,
22+
"basic-otlp-http-hyper",
23+
)])
24+
});
25+
26+
struct HyperClient<C> {
27+
inner: hyper::Client<C>,
28+
}
29+
30+
impl Default for HyperClient<HttpConnector> {
31+
fn default() -> Self {
32+
Self {
33+
inner: Client::new(),
34+
}
35+
}
36+
}
37+
38+
impl<C> std::fmt::Debug for HyperClient<C> {
39+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
f.debug_struct("HyperClient")
41+
.field("inner", &self.inner)
42+
.finish()
43+
}
44+
}
45+
46+
#[async_trait]
47+
impl<C: Connect + Clone + Send + Sync + 'static> HttpClient for HyperClient<C> {
48+
async fn send(&self, request: Request<Vec<u8>>) -> Result<Response<Bytes>, HttpError> {
49+
let request = request.map(Body::from);
50+
51+
let (parts, body) = self
52+
.inner
53+
.request(request)
54+
.await?
55+
.error_for_status()?
56+
.into_parts();
57+
let body = hyper::body::to_bytes(body).await?;
58+
59+
Ok(Response::from_parts(parts, body))
60+
}
61+
}
62+
63+
fn init_tracer_provider() -> Result<sdktrace::TracerProvider, TraceError> {
64+
opentelemetry_otlp::new_pipeline()
65+
.tracing()
66+
.with_exporter(
67+
opentelemetry_otlp::new_exporter()
68+
.http()
69+
.with_http_client(HyperClient::default())
70+
.with_endpoint("http://localhost:4318/v1/traces"),
71+
)
72+
.with_trace_config(Config::default().with_resource(RESOURCE.clone()))
73+
.install_batch(opentelemetry_sdk::runtime::Tokio)
74+
}
75+
76+
#[tokio::main]
77+
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
78+
let result = init_tracer_provider();
79+
assert!(
80+
result.is_ok(),
81+
"Init tracer failed with error: {:?}",
82+
result.err()
83+
);
84+
85+
let tracer_provider = result.unwrap();
86+
global::set_tracer_provider(tracer_provider.clone());
87+
88+
let tracer = global::tracer_provider().tracer_builder("basic").build();
89+
90+
tracer.in_span("Main operation", |cx| {
91+
let span = cx.span();
92+
span.add_event(
93+
"Nice operation!".to_string(),
94+
vec![Key::new("bogons").i64(100)],
95+
);
96+
span.set_attribute(KeyValue::new("another.key", "yes"));
97+
98+
tracer.in_span("Sub operation...", |cx| {
99+
let span = cx.span();
100+
span.set_attribute(KeyValue::new("another.key", "yes"));
101+
span.add_event("Sub span event", vec![]);
102+
});
103+
});
104+
105+
global::shutdown_tracer_provider();
106+
Ok(())
107+
}

0 commit comments

Comments
 (0)