Shipping Service
You are viewing the English version of this page because it has not yet been fully translated. Interested in helping out? See Contributing.
This service is responsible for providing shipping information including pricing and tracking information, when requested from Checkout Service.
Shipping service is built with Actix Web,
Tracing for logs and OpenTelemetry Libraries. All other
sub-dependencies are included in Cargo.toml
.
Depending on your framework and runtime, you may consider consulting Rust docs to supplement. You’ll find examples of async and sync spans in quote requests and tracking IDs respectively.
Instrumentation
The OpenTelemetry SDK is configured in the telemetry_conf
file.
A function get_resource()
is implemented to create a Resource using the
default Resource Detectors plus OS
and Process
detectors:
fn get_resource() -> Resource {
let detectors: Vec<Box<dyn ResourceDetector>> = vec![
Box::new(OsResourceDetector),
Box::new(ProcessResourceDetector),
];
Resource::builder().with_detectors(&detectors).build()
}
With get_resource()
in place, the function can be called multiple times across
all provider initializations.
Initializing Tracer Provider
fn init_tracer_provider() {
global::set_text_map_propagator(TraceContextPropagator::new());
let tracer_provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
.with_resource(get_resource())
.with_batch_exporter(
opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.build()
.expect("Failed to initialize tracing provider"),
)
.build();
global::set_tracer_provider(tracer_provider);
}
Initializing Meter Provider
fn init_meter_provider() -> opentelemetry_sdk::metrics::SdkMeterProvider {
let meter_provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
.with_resource(get_resource())
.with_periodic_exporter(
opentelemetry_otlp::MetricExporter::builder()
.with_temporality(opentelemetry_sdk::metrics::Temporality::Delta)
.with_tonic()
.build()
.expect("Failed to initialize metric exporter"),
)
.build();
global::set_meter_provider(meter_provider.clone());
meter_provider
}
Initializing Logger Provider
For logs, the Shipping service uses Tracing, so the OpenTelemetryTracingBridge
is used to bridge logs from the tracing crate to OpenTelemetry.
fn init_logger_provider() {
let logger_provider = opentelemetry_sdk::logs::SdkLoggerProvider::builder()
.with_resource(get_resource())
.with_batch_exporter(
opentelemetry_otlp::LogExporter::builder()
.with_tonic()
.build()
.expect("Failed to initialize logger provider"),
)
.build();
let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider);
let filter_otel = EnvFilter::new("info");
let otel_layer = otel_layer.with_filter(filter_otel);
tracing_subscriber::registry().with(otel_layer).init();
}
Instrumentation Initialization
After defining the functions to initialize the providers for Traces, Metrics and
Logs, a public function init_otel()
is created:
pub fn init_otel() -> Result<()> {
init_logger_provider();
init_tracer_provider();
init_meter_provider();
Ok(())
}
This function calls all initializers and returns OK(())
if everything starts
properly.
The init_otel()
function is then called on main
:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
match init_otel() {
Ok(_) => {
info!("Successfully configured OTel");
}
Err(err) => {
panic!("Couldn't start OTel: {0}", err);
}
};
[...]
}
Instrumentation Configuration
With the providers now configured and initialized, Shipping uses the
opentelemetry-instrumentation-actix-web
crate
to instrument the application during server-side and client-side configuration.
Server side
The server is wrapped with RequestTracing
and RequestMetrics
to
automatically create Traces and Metrics when receiving requests:
HttpServer::new(|| {
App::new()
.wrap(RequestTracing::new())
.wrap(RequestMetrics::default())
.service(get_quote)
.service(ship_order)
})
Client side
When making a request to another service, trace_request()
is added to the
call:
let mut response = client
.post(quote_service_addr)
.trace_request()
.send_json(&reqbody)
.await
.map_err(|err| anyhow::anyhow!("Failed to call quote service: {err}"))?;
Manual instrumentation
The opentelemetry-instrumentation-actix-web
crate allows us to instrument
server and client side by adding the commands mentioned in the previous section.
In the Demo we also demonstrate how to manually enhance automatically created spans and how to create manual metrics on the application.
Manual spans
In the following snippet, the current active span is enhanced with a span event and a span attribute:
Ok(get_active_span(|span| {
let q = create_quote_from_float(f);
span.add_event(
"Received Quote".to_string(),
vec![KeyValue::new("app.shipping.cost.total", format!("{}", q))],
);
span.set_attribute(KeyValue::new("app.shipping.cost.total", format!("{}", q)));
q
}))
Manual metrics
A custom metric counter is created to count how many items are in the shipping request:
let meter = global::meter("otel_demo.shipping.quote");
let counter = meter.u64_counter("app.shipping.items_count").build();
counter.add(count as u64, &[]);
Logs
Because the Shipping service is using Tracing as a log interface, it uses the
opentelemetry-appender-tracing
crate to bridge Tracing logs into OpenTelemetry
logs.
The appender was already configured during the initialization of the logger provider, with the following two lines:
let otel_layer = OpenTelemetryTracingBridge::new(&logger_provider);
tracing_subscriber::registry().with(otel_layer).init();
With that in place, we can use Tracing as we would normally, for example:
info!(
name = "SendingQuoteValue",
quote.dollars = quote.dollars,
quote.cents = quote.cents,
message = "Sending Quote"
);
The opentelemetry-appender-tracing
crate takes care of adding OpenTelemetry
context to the log entry, and the final exported log contains all resource
attributes configured and TraceContext
information.
Feedback
Cette page est-elle utile?
Thank you. Your feedback is appreciated!
Please let us know how we can improve this page. Your feedback is appreciated!