@@ -28,74 +28,88 @@ 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 and add the following
32
+ 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
}
105
+
92
106
```
93
107
94
108
Build and run the application with the following command, then open
95
109
< http://localhost:8080/rolldice > in your web browser to ensure it is working.
96
110
97
111
``` console
98
- $ cargo run --bin dice_server
112
+ $ cargo run
99
113
...
100
114
Listening on 127.0.0.1:8080
101
115
```
@@ -114,125 +128,120 @@ opentelemetry_sdk = "{{% version-from-registry otel-rust-sdk %}}"
114
128
opentelemetry-stdout = { version = " {{% version-from-registry exporter-rust-stdout %}}" , features = [" trace" ] }
115
129
```
116
130
117
- Update the ` dice_server .rs` file with code to initialize a tracer and to emit
131
+ Update the ` main .rs` file with code to initialize a tracer and to emit
118
132
spans when the ` handle ` function is called:
119
133
120
134
``` 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 ;
135
+ use std :: convert :: Infallible ;
136
+ use std :: net :: SocketAddr ;
137
+ use std :: sync :: OnceLock ;
138
+
139
+ use http_body_util :: Full ;
140
+ use hyper :: body :: Bytes ;
141
+ use hyper :: server :: conn :: http1;
142
+ use hyper :: service :: service_fn;
143
+ use hyper :: Method ;
144
+ use hyper :: {Request , Response };
145
+ use hyper_util :: rt :: TokioIo ;
146
+ use opentelemetry :: global :: {self , BoxedTracer };
147
+ use opentelemetry :: trace :: {Span , SpanKind , Status , Tracer };
148
+ use opentelemetry_sdk :: trace :: SdkTracerProvider ;
130
149
use opentelemetry_stdout :: SpanExporter ;
150
+ use rand :: Rng ;
151
+ use tokio :: net :: TcpListener ;
131
152
132
- async fn handle (req : Request <Body >) -> Result <Response <Body >, Infallible > {
133
- let mut response = Response :: new (Body :: empty ());
153
+ async fn roll_dice (_ : Request <hyper :: body :: Incoming >) -> Result <Response <Full <Bytes >>, Infallible > {
154
+ let random_number = rand :: rng (). random_range (1 ..= 6 );
155
+ Ok (Response :: new (Full :: new (Bytes :: from (
156
+ random_number . to_string (),
157
+ ))))
158
+ }
134
159
135
- let tracer = global :: tracer (" dice_server" );
160
+ async fn handle (req : Request <hyper :: body :: Incoming >) -> Result <Response <Full <Bytes >>, Infallible > {
161
+ let tracer = get_tracer ();
136
162
137
163
let mut span = tracer
138
164
. span_builder (format! (" {} {}" , req . method (), req . uri (). path ()))
139
165
. with_kind (SpanKind :: Server )
140
- . start (& tracer );
166
+ . start (tracer );
141
167
142
168
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
- }
169
+ (& Method :: GET , " /rolldice" ) => roll_dice (req ). await ,
148
170
_ => {
149
- * response . status_mut () = StatusCode :: NOT_FOUND ;
150
- span . set_status (Status :: error (" Not Found" ));
171
+ span . set_status (Status :: Ok );
172
+ Ok (Response :: builder ()
173
+ . status (404 )
174
+ . body (Full :: new (Bytes :: from (" Not Found" )))
175
+ . unwrap ())
151
176
}
152
- };
177
+ }
178
+ }
153
179
154
- Ok (response )
180
+ fn get_tracer () -> & 'static BoxedTracer {
181
+ static TRACER : OnceLock <BoxedTracer > = OnceLock :: new ();
182
+ TRACER . get_or_init (|| global :: tracer (" dice_server" ))
155
183
}
156
184
157
- fn init_tracer () {
158
- global :: set_text_map_propagator (TraceContextPropagator :: new ());
159
- let provider = TracerProvider :: builder ()
185
+ fn init_tracer_provider () {
186
+ let provider = SdkTracerProvider :: builder ()
160
187
. with_simple_exporter (SpanExporter :: default ())
161
188
. build ();
162
189
global :: set_tracer_provider (provider );
163
190
}
164
191
165
192
#[tokio:: main]
166
- async fn main () {
167
- init_tracer ();
193
+ async fn main () -> Result <(), Box <dyn std :: error :: Error + Send + Sync >> {
168
194
let addr = SocketAddr :: from (([127 , 0 , 0 , 1 ], 8080 ));
169
195
170
- let make_svc = make_service_fn (| _conn | async { Ok :: <_ , Infallible >(service_fn (handle )) });
196
+ let listener = TcpListener :: bind (addr ). await ? ;
197
+ init_tracer_provider ();
171
198
172
- let server =
173
- Server :: bind (& addr ). serve (make_svc );
174
-
175
- println! (" Listening on {addr}" );
176
- if let Err (e ) = server . await {
177
- eprintln! (" server error: {e}" );
199
+ loop {
200
+ let (stream , _ ) = listener . accept (). await ? ;
201
+ let io = TokioIo :: new (stream );
202
+ tokio :: task :: spawn (async move {
203
+ if let Err (err ) = http1 :: Builder :: new ()
204
+ . serve_connection (io , service_fn (handle ))
205
+ . await
206
+ {
207
+ eprintln! (" Error serving connection: {:?}" , err );
208
+ }
209
+ });
178
210
}
179
211
}
180
212
```
181
213
182
214
Start your server again:
183
215
184
216
``` sh
185
- $ cargo run --bin dice_server
217
+ $ cargo run
186
218
...
187
219
Listening on 127.0.0.1:8080
188
220
```
189
221
190
222
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
- }
223
+ you'll see a span being emitted to the console:
224
+
225
+ ``` txt
226
+ Spans
227
+ Resource
228
+ -> telemetry.sdk.version=String(Static("0.28.0"))
229
+ -> service.name=String(Static("unknown_service"))
230
+ -> telemetry.sdk.language=String(Static("rust"))
231
+ -> telemetry.sdk.name=String(Static("opentelemetry"))
232
+ Span #0
233
+ Instrumentation Scope
234
+ Name : "dice_server"
235
+
236
+ Name : GET /rolldice
237
+ TraceId : 9f03de7cf14780bd54b95d7095332107
238
+ SpanId : 9faed88b3f9ed699
239
+ TraceFlags : TraceFlags(1)
240
+ ParentSpanId: 0000000000000000
241
+ Kind : Server
242
+ Start time: 2025-03-11 00:47:26.687497
243
+ End time: 2025-03-11 00:47:26.687653
244
+ Status: Unset
236
245
```
237
246
238
247
## What next?
0 commit comments