@@ -28,65 +28,78 @@ For more elaborate examples, see [examples](/docs/languages/rust/examples/).
28
28
29
29
### Dependencies
30
30
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 :
33
33
34
34
``` toml
35
35
[package ]
36
36
name = " dice_server"
37
37
version = " 0.1.0"
38
38
edition = " 2021"
39
- publish = false
40
-
41
- [[bin ]]
42
- name = " dice_server"
43
- path = " dice_server.rs"
44
- doc = false
45
39
46
40
[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"
50
46
```
51
47
52
48
### Create and launch an HTTP Server
53
49
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:
56
51
57
52
``` 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 ;
60
63
use rand :: Rng ;
61
- use std :: { convert :: Infallible , net :: SocketAddr } ;
64
+ use tokio :: net :: TcpListener ;
62
65
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
+ }
65
72
73
+ async fn handle (req : Request <hyper :: body :: Incoming >) -> Result <Response <Full <Bytes >>, Infallible > {
66
74
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
+ }
77
81
}
78
82
79
83
#[tokio:: main]
80
- async fn main () {
84
+ async fn main () -> Result <(), Box < dyn std :: error :: Error + Send + Sync >> {
81
85
let addr = SocketAddr :: from (([127 , 0 , 0 , 1 ], 8080 ));
82
86
83
- let make_svc = make_service_fn (| _conn | async { Ok :: <_ , Infallible >(service_fn (handle )) });
84
87
85
- let server = Server :: bind (& addr ). serve ( make_svc ) ;
88
+ let listener = TcpListener :: bind (addr ). await ? ;
86
89
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
+ });
90
103
}
91
104
}
92
105
```
@@ -95,7 +108,7 @@ Build and run the application with the following command, then open
95
108
< http://localhost:8080/rolldice > in your web browser to ensure it is working.
96
109
97
110
``` console
98
- $ cargo run --bin dice_server
111
+ $ cargo run
99
112
...
100
113
Listening on 127.0.0.1:8080
101
114
```
@@ -114,125 +127,120 @@ opentelemetry_sdk = "{{% version-from-registry otel-rust-sdk %}}"
114
127
opentelemetry-stdout = { version = " {{% version-from-registry exporter-rust-stdout %}}" , features = [" trace" ] }
115
128
```
116
129
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:
119
132
120
133
``` 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 ;
130
148
use opentelemetry_stdout :: SpanExporter ;
149
+ use rand :: Rng ;
150
+ use tokio :: net :: TcpListener ;
131
151
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
+ }
134
158
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 ();
136
161
137
162
let mut span = tracer
138
163
. span_builder (format! (" {} {}" , req . method (), req . uri (). path ()))
139
164
. with_kind (SpanKind :: Server )
140
- . start (& tracer );
165
+ . start (tracer );
141
166
142
167
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 ,
148
169
_ => {
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 ())
151
175
}
152
- };
176
+ }
177
+ }
153
178
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" ))
155
182
}
156
183
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 ()
160
186
. with_simple_exporter (SpanExporter :: default ())
161
187
. build ();
162
188
global :: set_tracer_provider (provider );
163
189
}
164
190
165
191
#[tokio:: main]
166
- async fn main () {
167
- init_tracer ();
192
+ async fn main () -> Result <(), Box <dyn std :: error :: Error + Send + Sync >> {
168
193
let addr = SocketAddr :: from (([127 , 0 , 0 , 1 ], 8080 ));
169
194
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 ();
174
197
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
+ });
178
209
}
179
210
}
180
211
```
181
212
182
213
Start your server again:
183
214
184
215
``` sh
185
- $ cargo run --bin dice_server
216
+ $ cargo run
186
217
...
187
218
Listening on 127.0.0.1:8080
188
219
```
189
220
190
221
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
236
244
```
237
245
238
246
## What next?
0 commit comments