Instrument your Rust application with OpenTelemetry
This walkthrough shows how to add observability to your Rust application using the OpenTelemetry Rust libraries and tools.
Feature | Supported |
---|---|
Automatic Instrumentation | No |
Automatic OneAgent Ingestion | No |
Prerequisites
- Dynatrace version 1.222+
- For tracing, W3C Trace Context is enabled
- From the Dynatrace menu, go to Settings > Preferences > OneAgent features.
- Turn on Send W3C Trace Context HTTP headers.
Get the Dynatrace access details
Determine the API base URL
For details on how to assemble the base OTLP endpoint URL, see Export with OTLP. The URL should end in /api/v2/otlp
.
Get API access token
The access token for ingesting traces, logs, and metrics can be generated in your Dynatrace menu under Access tokens.
Export with OTLP has more details on the format and the necessary access scopes.
Set up OpenTelemetry
-
Add the current version (
[VERSION]
) of the following crates to yourCargo.toml
file.1opentelemetry = { version = "[VERSION]", features = ["trace", "metrics"] }2opentelemetry_sdk = { version = "[VERSION]" , features = ["rt-tokio"] }3opentelemetry-otlp = { version = "[VERSION]", features = ["http-proto", "reqwest-client", "reqwest-rustls"] }4opentelemetry-http = { version = "[VERSION]" }5opentelemetry-semantic-conventions = { version = "[VERSION]" } -
Add the following
use
declarations.1use std::collections::HashMap;2use std::io::{BufRead, BufReader, Read};34use opentelemetry::{5 global,6 trace::{Span, TraceContextExt, TraceError, Tracer},7 Context, KeyValue,8};910use opentelemetry_sdk::{runtime, trace as sdktrace, Resource};11use opentelemetry_http::{HeaderExtractor, HeaderInjector};12use opentelemetry_otlp::WithExportConfig;13use opentelemetry_semantic_conventions as semcov; -
Add the following function to your startup file.
1fn init_opentelemetry() -> Result<sdktrace::Tracer, TraceError> {2 // Helper function to read potentially available OneAgent data3 fn read_dt_metadata() -> Resource {4 fn read_single(path: &str, metadata: &mut Vec<KeyValue>) -> std::io::Result<()> {5 let mut file = std::fs::File::open(path)?;6 if path.starts_with("dt_metadata") {7 let mut name = String::new();8 file.read_to_string(&mut name)?;9 file = std::fs::File::open(name)?;10 }11 for line in BufReader::new(file).lines() {12 if let Some((k, v)) = line?.split_once('=') {13 metadata.push(KeyValue::new(k.to_string(), v.to_string()))14 }15 }16 Ok(())17 }18 let mut metadata = Vec::new();19 for name in [20 "dt_metadata_e617c525669e072eebe3d0f08212e8f2.properties",21 "/var/lib/dynatrace/enrichment/dt_metadata.properties",22 "/var/lib/dynatrace/enrichment/dt_host_metadata.properties"23 ] {24 let _ = read_single(name, &mut metadata);25 }26 Resource::new(metadata)27 }2829 // ===== GENERAL SETUP =====30 let DT_API_TOKEN = env::var("DT_API_TOKEN").unwrap(); // TODO: change31 let DT_API_URL = env::var("DT_API_URL").unwrap();3233 let mut map = HashMap::new();34 map.insert("Authorization".to_string(), format!("Api-Token {}", DT_API_TOKEN));35 let mut resource = Resource::new([36 KeyValue::new(semcov::resource::SERVICE_NAME, "rust-app") //TODO Replace with the name of your application37 ]);38 resource = resource.merge(&read_dt_metadata());3940 // ===== TRACING SETUP =====41 global::set_text_map_propagator(TraceContextPropagator::new());4243 opentelemetry_otlp::new_pipeline()44 .tracing()45 .with_exporter(46 opentelemetry_otlp::new_exporter()47 .http()48 .with_endpoint(DT_API_URL)49 .with_headers(map),50 )51 .with_trace_config(52 sdktrace::config()53 .with_resource(resource)54 .with_sampler(sdktrace::Sampler::AlwaysOn),55 )56 .install_batch(runtime::Tokio)57} -
Configure the variables
DT_API_URL
andDT_API_TOKEN
ininit_opentelemetry()
for the Dynatrace URL and access token inotel.h
. -
Call
init_opentelemetry()
as early as possible in your startup code.
Instrument your application
Add tracing
-
First, we need to get a tracer object.
1let tracer = global::tracer("my-tracer"); -
With
tracer
, we can now start new spans.1let mut span = tracer.start("Call to /myendpoint");2span.set_attribute(KeyValue::new("http.method", "GET"));3span.set_attribute(KeyValue::new("net.protocol.version", "1.1"));45// TODO: Your code goes here67span.end();In the above code, we:
Create a new span and name it "Call to /myendpoint"
- Add two attributes, following the semantic naming convention, specific to the action of this span: information on the HTTP method and version
- Add a
TODO
in place of the eventual business logic - Call the span's
end()
method to complete the span
Collect metrics
OpenTelemetry metrics do not support export via HTTP yet.
Connect logs
OpenTelemetry logging is currently not yet available for Rust and is still under development.
Ensure context propagation optional
Context propagation is particularly important when network calls (for example, REST) are involved.
Extracting the context when receiving a request
For extracting information on an existing context, the following example defines the get_parent_context
function. That function is called when a request is received and gets passed the original HTTP request object.
It then calls the global get_text_map_propagator
function and uses the extract
function of the propagator object to fetch the provided context information from the headers and create a new context based on that.
This context is returned by get_parent_context
and allows us to continue the previous traces with our new spans.
1//Method to extract the parent context from the request2fn get_parent_context(req: Request<Body>) -> Context {3 global::get_text_map_propagator(|propagator| {4 propagator.extract(&HeaderExtractor(req.headers()))5 })6}78async fn incoming_request(req: Request<Body>) -> Result<Response<Body>, Infallible> {9 let parent_cx = get_parent_context(req);1011 let mut span = global::tracer("manual-server")12 .start_with_context("my-server-span", &parent_cx); //TODO Replace with the name of your span1314 span.set_attribute(KeyValue::new("my-server-key-1", "my-server-value-1")); //TODO Add attributes1516 // TODO Your incoming_request code goes here17}
Injecting the context when sending requests
The following example uses hyper to send an HTTP request. To inject the required propagation headers of our current context, the code instantiates a new hyper request and passes its headers to the inject_context
function.
Once the headers are added to our request, we can perform the HTTP call and the receiving endpoint will be able to continue the trace with the provided information.
1async fn outgoing_request(2 context: Context,3) -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {4 let client = Client::new();5 let span = global::tracer("manual-client")6 .start_with_context("my-client-span", &context); //TODO Replace with the name of your span7 let cx = Context::current_with_span(span);89 let mut req = hyper::Request::builder().uri("<HTTP_URL>");1011 //Method to inject the current context in the request12 global::get_text_map_propagator(|propagator| {13 propagator.inject_context(&cx, &mut HeaderInjector(&mut req.headers_mut().unwrap()))14 });1516 cx.span()17 .set_attribute(KeyValue::new("my-client-key-1", "my-client-value-1")); //TODO Add attributes1819 // TODO Your outgoing_request code goes here20}
Configure data capture to meet privacy requirements optional
While Dynatrace automatically captures all OpenTelemetry attributes, only attribute values specified in the allowlist are stored and displayed in the Dynatrace web UI. This prevents accidental storage of personal data, so you can meet your privacy requirements and control the amount of monitoring data stored.
To view your custom attributes, you need to allow them in the Dynatrace web UI first. To learn how to configure attribute storage and masking, see Attribute redaction.
Verify data ingestion into Dynatrace
Once you have finished the instrumentation of your application, perform a couple of test actions to create and send demo traces, metrics, and logs and verify that they were correctly ingested into Dynatrace.
To do that for traces, in the Dynatrace menu, go to Distributed traces and select the Ingested traces tab. If you use OneAgent, select PurePaths instead.
Metrics and logs can be found under their respective entries at Observe and explore.