Skip to content

Commit 94e3141

Browse files
cijothomasopentelemetrybotMsksgmymotongpootiffany76
authored
Fix Rust getting started (open-telemetry#6515)
Co-authored-by: opentelemetrybot <[email protected]> Co-authored-by: Msksgm <[email protected]> Co-authored-by: Yoshi Yamaguchi <[email protected]> Co-authored-by: Tiffany Hrabusa <[email protected]> Co-authored-by: pinky <[email protected]> Co-authored-by: Kieron Lanning <[email protected]> Co-authored-by: jason plumb <[email protected]> Co-authored-by: Fabrizio Ferri-Benedetti <[email protected]> Co-authored-by: Patrice Chalin <[email protected]>
1 parent 44d2ea2 commit 94e3141

File tree

4 files changed

+128
-120
lines changed

4 files changed

+128
-120
lines changed

content/en/docs/languages/rust/getting-started.md

+125-117
Original file line numberDiff line numberDiff line change
@@ -28,65 +28,78 @@ For more elaborate examples, see [examples](/docs/languages/rust/examples/).
2828

2929
### Dependencies
3030

31-
To begin, create a file `Cargo.toml` in a new directory and add the following
32-
content:
31+
To begin, create an executable using `cargo new dice_server` in a new directory
32+
and add the following content to the `Cargo.toml` file:
3333

3434
```toml
3535
[package]
3636
name = "dice_server"
3737
version = "0.1.0"
3838
edition = "2021"
39-
publish = false
40-
41-
[[bin]]
42-
name = "dice_server"
43-
path = "dice_server.rs"
44-
doc = false
4539

4640
[dependencies]
47-
hyper = { version = "0.14", features = ["full"] }
48-
tokio = { version = "1.29", features = ["full"] }
49-
rand = { version = "0.8" }
41+
hyper = { version = "1", features = ["full"] }
42+
tokio = { version = "1", features = ["full"] }
43+
http-body-util = "0.1"
44+
hyper-util = { version = "0.1", features = ["full"] }
45+
rand = "0.9.0"
5046
```
5147

5248
### Create and launch an HTTP Server
5349

54-
In that same folder, create a file called `dice_server.rs` and add the following
55-
code to the file:
50+
Modify `main.rs` to the following:
5651

5752
```rust
58-
use hyper::service::{make_service_fn, service_fn};
59-
use hyper::{Body, Request, Response, Server, Method, StatusCode};
53+
use std::convert::Infallible;
54+
use std::net::SocketAddr;
55+
56+
use http_body_util::Full;
57+
use hyper::body::Bytes;
58+
use hyper::server::conn::http1;
59+
use hyper::service::service_fn;
60+
use hyper::Method;
61+
use hyper::{Request, Response};
62+
use hyper_util::rt::TokioIo;
6063
use rand::Rng;
61-
use std::{convert::Infallible, net::SocketAddr};
64+
use tokio::net::TcpListener;
6265

63-
async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> {
64-
let mut response = Response::new(Body::empty());
66+
async fn roll_dice(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
67+
let random_number = rand::rng().random_range(1..=6);
68+
Ok(Response::new(Full::new(Bytes::from(
69+
random_number.to_string(),
70+
))))
71+
}
6572

73+
async fn handle(req: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
6674
match (req.method(), req.uri().path()) {
67-
(&Method::GET, "/rolldice") => {
68-
let random_number = rand::thread_rng().gen_range(1..7);
69-
*response.body_mut() = Body::from(random_number.to_string());
70-
}
71-
_ => {
72-
*response.status_mut() = StatusCode::NOT_FOUND;
73-
}
74-
};
75-
76-
Ok(response)
75+
(&Method::GET, "/rolldice") => roll_dice(req).await,
76+
_ => Ok(Response::builder()
77+
.status(404)
78+
.body(Full::new(Bytes::from("Not Found")))
79+
.unwrap()),
80+
}
7781
}
7882

7983
#[tokio::main]
80-
async fn main() {
84+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
8185
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
8286

83-
let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) });
8487

85-
let server = Server::bind(&addr).serve(make_svc);
88+
let listener = TcpListener::bind(addr).await?;
8689

87-
println!("Listening on {addr}");
88-
if let Err(e) = server.await {
89-
eprintln!("server error: {e}");
90+
loop {
91+
let (stream, _) = listener.accept().await?;
92+
93+
let io = TokioIo::new(stream);
94+
95+
tokio::task::spawn(async move {
96+
if let Err(err) = http1::Builder::new()
97+
.serve_connection(io, service_fn(handle))
98+
.await
99+
{
100+
eprintln!("Error serving connection: {:?}", err);
101+
}
102+
});
90103
}
91104
}
92105
```
@@ -95,7 +108,7 @@ Build and run the application with the following command, then open
95108
<http://localhost:8080/rolldice> in your web browser to ensure it is working.
96109

97110
```console
98-
$ cargo run --bin dice_server
111+
$ cargo run
99112
...
100113
Listening on 127.0.0.1:8080
101114
```
@@ -114,125 +127,120 @@ opentelemetry_sdk = "{{% version-from-registry otel-rust-sdk %}}"
114127
opentelemetry-stdout = { version = "{{% version-from-registry exporter-rust-stdout %}}", features = ["trace"] }
115128
```
116129

117-
Update the `dice_server.rs` file with code to initialize a tracer and to emit
118-
spans when the `handle` function is called:
130+
Update the `main.rs` file with code to initialize a tracer and to emit spans
131+
when the `handle` function is called:
119132

120133
```rust
121-
use hyper::service::{make_service_fn, service_fn};
122-
use hyper::{Body, Method, Request, Response, Server, StatusCode};
123-
use rand::Rng;
124-
use std::{convert::Infallible, net::SocketAddr};
125-
use opentelemetry::global::ObjectSafeSpan;
126-
use opentelemetry::trace::{SpanKind, Status};
127-
use opentelemetry::{global, trace::Tracer};
128-
use opentelemetry_sdk::propagation::TraceContextPropagator;
129-
use opentelemetry_sdk::trace::TracerProvider;
134+
use std::convert::Infallible;
135+
use std::net::SocketAddr;
136+
use std::sync::OnceLock;
137+
138+
use http_body_util::Full;
139+
use hyper::body::Bytes;
140+
use hyper::server::conn::http1;
141+
use hyper::service::service_fn;
142+
use hyper::Method;
143+
use hyper::{Request, Response};
144+
use hyper_util::rt::TokioIo;
145+
use opentelemetry::global::{self, BoxedTracer};
146+
use opentelemetry::trace::{Span, SpanKind, Status, Tracer};
147+
use opentelemetry_sdk::trace::SdkTracerProvider;
130148
use opentelemetry_stdout::SpanExporter;
149+
use rand::Rng;
150+
use tokio::net::TcpListener;
131151

132-
async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> {
133-
let mut response = Response::new(Body::empty());
152+
async fn roll_dice(_: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
153+
let random_number = rand::rng().random_range(1..=6);
154+
Ok(Response::new(Full::new(Bytes::from(
155+
random_number.to_string(),
156+
))))
157+
}
134158

135-
let tracer = global::tracer("dice_server");
159+
async fn handle(req: Request<hyper::body::Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
160+
let tracer = get_tracer();
136161

137162
let mut span = tracer
138163
.span_builder(format!("{} {}", req.method(), req.uri().path()))
139164
.with_kind(SpanKind::Server)
140-
.start(&tracer);
165+
.start(tracer);
141166

142167
match (req.method(), req.uri().path()) {
143-
(&Method::GET, "/rolldice") => {
144-
let random_number = rand::thread_rng().gen_range(1..7);
145-
*response.body_mut() = Body::from(random_number.to_string());
146-
span.set_status(Status::Ok);
147-
}
168+
(&Method::GET, "/rolldice") => roll_dice(req).await,
148169
_ => {
149-
*response.status_mut() = StatusCode::NOT_FOUND;
150-
span.set_status(Status::error("Not Found"));
170+
span.set_status(Status::Ok);
171+
Ok(Response::builder()
172+
.status(404)
173+
.body(Full::new(Bytes::from("Not Found")))
174+
.unwrap())
151175
}
152-
};
176+
}
177+
}
153178

154-
Ok(response)
179+
fn get_tracer() -> &'static BoxedTracer {
180+
static TRACER: OnceLock<BoxedTracer> = OnceLock::new();
181+
TRACER.get_or_init(|| global::tracer("dice_server"))
155182
}
156183

157-
fn init_tracer() {
158-
global::set_text_map_propagator(TraceContextPropagator::new());
159-
let provider = TracerProvider::builder()
184+
fn init_tracer_provider() {
185+
let provider = SdkTracerProvider::builder()
160186
.with_simple_exporter(SpanExporter::default())
161187
.build();
162188
global::set_tracer_provider(provider);
163189
}
164190

165191
#[tokio::main]
166-
async fn main() {
167-
init_tracer();
192+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
168193
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
169194

170-
let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) });
171-
172-
let server =
173-
Server::bind(&addr).serve(make_svc);
195+
let listener = TcpListener::bind(addr).await?;
196+
init_tracer_provider();
174197

175-
println!("Listening on {addr}");
176-
if let Err(e) = server.await {
177-
eprintln!("server error: {e}");
198+
loop {
199+
let (stream, _) = listener.accept().await?;
200+
let io = TokioIo::new(stream);
201+
tokio::task::spawn(async move {
202+
if let Err(err) = http1::Builder::new()
203+
.serve_connection(io, service_fn(handle))
204+
.await
205+
{
206+
eprintln!("Error serving connection: {:?}", err);
207+
}
208+
});
178209
}
179210
}
180211
```
181212

182213
Start your server again:
183214

184215
```sh
185-
$ cargo run --bin dice_server
216+
$ cargo run
186217
...
187218
Listening on 127.0.0.1:8080
188219
```
189220

190221
When you send a request to the server at <http://localhost:8080/rolldice>,
191-
you'll see a span being emitted to the console (output is pretty printed for
192-
convenience):
193-
194-
```json
195-
{
196-
"resourceSpans": [
197-
{
198-
"resource": {
199-
"attributes": [
200-
{
201-
"key": "service.name",
202-
"value": {
203-
"stringValue": "unknown_service"
204-
}
205-
}
206-
]
207-
},
208-
"scopeSpans": [
209-
{
210-
"scope": {
211-
"name": "dice_server"
212-
},
213-
"spans": [
214-
{
215-
"attributes": [],
216-
"droppedAttributesCount": 0,
217-
"droppedEventsCount": 0,
218-
"droppedLinksCount": 0,
219-
"endTimeUnixNano": 1691076354768034000,
220-
"kind": 2,
221-
"name": "GET /rolldice",
222-
"parentSpanId": "",
223-
"spanId": "27e1d7d8e44a63c5",
224-
"startTimeUnixNano": 1691076354768025000,
225-
"status": {
226-
"code": 2
227-
},
228-
"traceId": "adfe9d364ee19610adde517d722167ca"
229-
}
230-
]
231-
}
232-
]
233-
}
234-
]
235-
}
222+
you'll see a span being emitted to the console:
223+
224+
```txt
225+
Spans
226+
Resource
227+
-> telemetry.sdk.version=String(Static("0.28.0"))
228+
-> service.name=String(Static("unknown_service"))
229+
-> telemetry.sdk.language=String(Static("rust"))
230+
-> telemetry.sdk.name=String(Static("opentelemetry"))
231+
Span #0
232+
Instrumentation Scope
233+
Name : "dice_server"
234+
235+
Name : GET /rolldice
236+
TraceId : 9f03de7cf14780bd54b95d7095332107
237+
SpanId : 9faed88b3f9ed699
238+
TraceFlags : TraceFlags(1)
239+
ParentSpanId: 0000000000000000
240+
Kind : Server
241+
Start time: 2025-03-11 00:47:26.687497
242+
End time: 2025-03-11 00:47:26.687653
243+
Status: Unset
236244
```
237245

238246
## What next?

data/registry/exporter-rust-stdout.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ createdAt: 2024-01-19
1616
package:
1717
registry: crates
1818
name: opentelemetry-stdout
19-
version: 0.27.0
19+
version: 0.28.0

data/registry/otel-rust-sdk.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ createdAt: 2020-02-04
1313
package:
1414
registry: crates
1515
name: opentelemetry_sdk
16-
version: 0.22.1
16+
version: 0.28.0

data/registry/otel-rust.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ createdAt: 2020-02-04
1313
package:
1414
registry: crates
1515
name: opentelemetry
16-
version: 0.22.0
16+
version: 0.28.0

0 commit comments

Comments
 (0)