--- title: Getting Started cSpell:ignore: eprintln println rolldice tokio weight: 10 --- This page will show you how to get started with OpenTelemetry in Rust. You will learn how you can instrument a simple Rust application, in such a way that [traces][] are emitted to the console. ## Prerequisites Ensure that you have the following installed locally: - [Rust](https://www.rust-lang.org/) - [Cargo](https://doc.rust-lang.org/cargo/) ## Example Application The following example uses a basic [hyper](https://hyper.rs/) application. If you are not using hyper, that's OK — you can use OpenTelemetry Rust with other HTTP implementations as well, such as Actix Web and Tide. For a complete list of libraries for supported frameworks, see the [registry](/ecosystem/registry/?component=instrumentation&language=rust). For more elaborate examples, see [examples](/docs/languages/rust/examples/). ### Dependencies To begin, create a file `Cargo.toml` in a new directory and add the following content: ```toml [package] name = "dice_server" version = "0.1.0" edition = "2021" publish = false [[bin]] name = "dice_server" path = "dice_server.rs" doc = false [dependencies] hyper = { version = "0.14", features = ["full"] } tokio = { version = "1.29", features = ["full"] } rand = { version = "0.8" } ``` ### Create and launch an HTTP Server In that same folder, create a file called `dice_server.rs` and add the following code to the file: ```rust use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Request, Response, Server, Method, StatusCode}; use rand::Rng; use std::{convert::Infallible, net::SocketAddr}; async fn handle(req: Request) -> Result, Infallible> { let mut response = Response::new(Body::empty()); match (req.method(), req.uri().path()) { (&Method::GET, "/rolldice") => { let random_number = rand::thread_rng().gen_range(1..7); *response.body_mut() = Body::from(random_number.to_string()); } _ => { *response.status_mut() = StatusCode::NOT_FOUND; } }; Ok(response) } #[tokio::main] async fn main() { let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) }); let server = Server::bind(&addr).serve(make_svc); println!("Listening on {addr}"); if let Err(e) = server.await { eprintln!("server error: {e}"); } } ``` Build and run the application with the following command, then open in your web browser to ensure it is working. ```console $ cargo run --bin dice_server ... Listening on 127.0.0.1:8080 ``` ## Instrumentation To add OpenTelemetry to your application, update the `Cargo.toml` with the dependencies for the OpenTelemetry Rust SDK [`opentelemetry`](https://crates.io/crates/opentelemetry) and the OpenTelemetry Stdout Exporter [`opentelemetry-stdout`](https://crates.io/crates/opentelemetry-stdout): ```toml opentelemetry = { version = "{{% version-from-registry otel-rust %}}", features = ["trace"] } opentelemetry-stdout = { version = "{{% version-from-registry exporter-rust-stdout %}}", features = ["trace"] } ``` Update the `dice_server.rs` file with code to initialize a tracer and to emit spans when the `handle` function is called: ```rust use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, Server, StatusCode}; use rand::Rng; use std::{convert::Infallible, net::SocketAddr}; use opentelemetry::global::ObjectSafeSpan; use opentelemetry::trace::{SpanKind, Status}; use opentelemetry::sdk::trace::TracerProvider; use opentelemetry::{global, sdk::propagation::TraceContextPropagator, trace::Tracer}; use opentelemetry_stdout::SpanExporter; async fn handle(req: Request) -> Result, Infallible> { let mut response = Response::new(Body::empty()); let tracer = global::tracer("dice_server"); let mut span = tracer .span_builder(format!("{} {}", req.method(), req.uri().path())) .with_kind(SpanKind::Server) .start(&tracer); match (req.method(), req.uri().path()) { (&Method::GET, "/rolldice") => { let random_number = rand::thread_rng().gen_range(1..7); *response.body_mut() = Body::from(random_number.to_string()); span.set_status(Status::Ok); } _ => { *response.status_mut() = StatusCode::NOT_FOUND; span.set_status(Status::error("Not Found")); } }; Ok(response) } fn init_tracer() { global::set_text_map_propagator(TraceContextPropagator::new()); let provider = TracerProvider::builder() .with_simple_exporter(SpanExporter::default()) .build(); global::set_tracer_provider(provider); } #[tokio::main] async fn main() { init_tracer(); let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) }); let server = Server::bind(&addr).serve(make_svc); println!("Listening on {addr}"); if let Err(e) = server.await { eprintln!("server error: {e}"); } } ``` Start your server again: ```sh $ cargo run --bin dice_server ... Listening on 127.0.0.1:8080 ``` When you send a request to the server at , you'll see a span being emitted to the console (output is pretty printed for convenience): ```json { "resourceSpans": [ { "resource": { "attributes": [ { "key": "service.name", "value": { "stringValue": "unknown_service" } } ] }, "scopeSpans": [ { "scope": { "name": "dice_server" }, "spans": [ { "attributes": [], "droppedAttributesCount": 0, "droppedEventsCount": 0, "droppedLinksCount": 0, "endTimeUnixNano": 1691076354768034000, "kind": 2, "name": "GET /rolldice", "parentSpanId": "", "spanId": "27e1d7d8e44a63c5", "startTimeUnixNano": 1691076354768025000, "status": { "code": 2 }, "traceId": "adfe9d364ee19610adde517d722167ca" } ] } ] } ] } ``` ## What next? For more: - Consult the [API & SDK](/docs/languages/rust/api/) reference - Try other [examples](/docs/languages/rust/examples/). [traces]: /docs/concepts/signals/traces/