Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,706 changes: 1,028 additions & 678 deletions Cargo.lock

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ resolver = "1"
[workspace.dependencies]
anyhow = "1"
assert_cmd = "2"
fs-err = "2.9"
fs-err = "3.3"
helpers = { path = "helpers" }
hyper = "1.4.1"
hyper = "1.10.1"
log = "0.4"
metrics = "0.23.0"
metrics-exporter-prometheus = "0.15.3"
metrics-util = "0.17.0"
opentelemetry = "0.24.0"
opentelemetry-otlp = "0.17.0"
opentelemetry_sdk = "0.24.1"
rustls = "0.23.12"
metrics = "0.24.6"
metrics-exporter-prometheus = "0.18.3"
metrics-util = "0.20.4"
opentelemetry = "0.32.0"
opentelemetry-otlp = "0.32.0"
opentelemetry_sdk = "0.32.1"
rustls = "0.23.40"
serde_json = "1"
tempfile = "3.8"
tokio = "1.32.0"
tonic = "0.12.1"
tempfile = "3.27"
tokio = "1.52.3"
tonic = "0.14.6"
tracing = "0.1"
tracing-log = "0.2"
tracing-opentelemetry = "0.25.0"
tracing-opentelemetry = "0.33.0"
tracing-panic = "0.1"
tracing-subscriber = { version = "0.3", default-features = false }
ureq = "2"
ureq = "3"
2 changes: 1 addition & 1 deletion book/src/02_failures/01_error_trait.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Let's unpack the definition:
- All errors must implement the `Display` trait. This representation is primarily designed
for **users**. It should be understandable by a person that is not familiar (nor has access)
to the internals of the system.
- An errors may travel through multiple "layers" in your application. E.g. a failure to execute
- An error may travel through multiple "layers" in your application. E.g. a failure to execute
a query might arise from a network error, which in turn might be caused by a DNS resolution
failure.
Each additional semantic layer is often represented as a wrapper over the original error.
Expand Down
2 changes: 1 addition & 1 deletion exercises/01_structured_logging/01_log_crate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ name = "file"
path = "src/bins/file.rs"

[dependencies]
fs-err = "2.9"
fs-err = "3.3"
log = { workspace = true, features = ["std"] }

[dev-dependencies]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"
anyhow = { workspace = true }
hyper = { workspace = true, features = ["full"] }
opentelemetry = { workspace = true }
opentelemetry-otlp = { workspace = true, features = ["tls-roots"] }
opentelemetry-otlp = { workspace = true, features = ["grpc-tonic", "tls-ring"] }
opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] }
tonic = { workspace = true }
tracing = { workspace = true }
Expand Down
9 changes: 4 additions & 5 deletions exercises/01_structured_logging/08_opentelemetry/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! # Exercise
//!
//! We'll be using the [`tracing-opentelemetry`] crate to export our test telemetry data to
//! [Honeycomb](https://www.honeycomb.io/), a hosted observability platform.
//! [Honeycomb](https://www.honeycomb.io/), a hosted observability platform.
//! Their interface does a much better job than raw JSON at visualizing the rich data we're
//! collecting thanks to the `tracing` crate.
//!
Expand All @@ -16,9 +16,8 @@ use tracing::{instrument, Span};
pub fn get_total(order_numbers: &[u64]) -> Result<u64, anyhow::Error> {
let mut total = 0;
for order_number in order_numbers {
let order_details = get_order_details(*order_number).map_err(|e| {
let order_details = get_order_details(*order_number).inspect_err(|_| {
Span::current().record("outcome", "failure");
e
})?;
total += order_details.price;
}
Expand All @@ -34,11 +33,11 @@ pub struct OrderDetails {
/// A dummy function to simulate what would normally be a database query.
#[instrument("retrieve order", skip_all, fields(outcome))]
fn get_order_details(order_number: u64) -> Result<OrderDetails, anyhow::Error> {
if order_number % 4 == 0 {
if order_number.is_multiple_of(4) {
Span::current().record("outcome", "failure");
Err(anyhow::anyhow!("Failed to talk to the database"))
} else {
let prices = vec![999, 1089, 1029];
let prices = [999, 1089, 1029];
Span::current().record("outcome", "success");
Ok(OrderDetails {
order_number,
Expand Down
46 changes: 20 additions & 26 deletions exercises/01_structured_logging/08_opentelemetry/src/subscriber.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
use opentelemetry::KeyValue;
use opentelemetry::trace::TracerProvider;
use opentelemetry_otlp::WithExportConfig;
use opentelemetry_sdk::trace::Tracer;
use opentelemetry_sdk::{runtime, Resource};
use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::trace::SdkTracerProvider;
use tonic::metadata::MetadataMap;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::Registry;

pub fn init_test_subscriber() {
let tracer = init_tracer();
pub fn init_test_subscriber() -> SdkTracerProvider {
let provider = init_tracer_provider();
let tracer = provider.tracer("rust-telemetry-workshop");
let otel = tracing_opentelemetry::layer().with_tracer(tracer);

// Here we are using the `Layer` trait from the `tracing-subscriber` crate to combine together
// multiple pieces of functionality into a single subscriber.
// We'll talk more about layers later in the workshop.
Registry::default().with(otel).init()
Registry::default().with(otel).init();

provider
}

pub fn init_tracer() -> Tracer {
pub fn init_tracer_provider() -> SdkTracerProvider {
let honeycomb_key =
std::env::var("HONEYCOMB_API_KEY").expect("`HONEYCOMB_API_KEY` must be set");
let mut map = MetadataMap::with_capacity(1);
Expand All @@ -30,22 +31,15 @@ pub fn init_tracer() -> Tracer {
// documentation (or grab me after the workshop to talk about it).
// At a super high-level: you want batching and you want a sensible sampling strategy,
// but beyond that it's hard to give general advice.
opentelemetry_otlp::new_pipeline()
.tracing()
.with_trace_config(opentelemetry_sdk::trace::Config::default().with_resource(
Resource::new(vec![KeyValue::new(
"service.name",
"rust-telemetry-workshop",
)]),
))
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
.with_endpoint("https://api.honeycomb.io/api/traces")
.with_timeout(std::time::Duration::from_secs(5))
.with_metadata(map),
)
.install_batch(runtime::Tokio)
.unwrap()
.tracer("rust-telemetry-workshop")
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint("https://api.honeycomb.io/api/traces")
.with_timeout(std::time::Duration::from_secs(5))
.with_metadata(map)
.build()
.expect("Error setting up Open Telemetry exporter");

opentelemetry_sdk::trace::SdkTracerProvider::builder()
.with_batch_exporter(exporter)
.build()
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use opentelemetry::global::shutdown_tracer_provider;
use opentelemetry_training::init_test_subscriber;

#[tokio::test]
async fn failure() {
init_test_subscriber();
let provider = init_test_subscriber();
let order_numbers = vec![3, 4, 5];

opentelemetry_training::get_total(&order_numbers).unwrap_err();

// Ensure all spans are exported
tokio::task::spawn_blocking(|| shutdown_tracer_provider())
tokio::task::spawn_blocking(move || provider.shutdown())
.await
.unwrap();
.unwrap()
.expect("Error shutting down Open Telemetry tracing");
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use opentelemetry::global::shutdown_tracer_provider;
use opentelemetry_training::init_test_subscriber;

#[tokio::test]
async fn success() {
init_test_subscriber();
let provider = init_test_subscriber();
let order_numbers = vec![1, 2, 3];

let total = opentelemetry_training::get_total(&order_numbers).unwrap();
Expand All @@ -12,7 +11,8 @@ async fn success() {
assert_eq!(total, 3117);

// Ensure all spans are exported
tokio::task::spawn_blocking(|| shutdown_tracer_provider())
tokio::task::spawn_blocking(move || provider.shutdown())
.await
.unwrap();
.unwrap()
.expect("Error shutting down Open Telemetry tracing");
}
2 changes: 1 addition & 1 deletion exercises/01_structured_logging/09_subscriber/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ anyhow = { workspace = true }
helpers = { workspace = true }
hyper = { workspace = true, features = ["full"] }
opentelemetry = { workspace = true }
opentelemetry-otlp = { workspace = true, features = ["tls-roots"] }
opentelemetry-otlp = { workspace = true, features = ["tls-ring", "grpc-tonic"] }
opentelemetry_sdk = { workspace = true, features = ["rt-tokio"] }
tonic = { workspace = true }
tracing = { workspace = true }
Expand Down
9 changes: 4 additions & 5 deletions exercises/01_structured_logging/09_subscriber/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@
//! You can look at the subscribers we built in the previous exercises for inspiration!
mod subscriber;

pub use subscriber::init_test_subscriber;
pub use subscriber::{init_test_subscriber, init_tracer_provider};
use tracing::{instrument, Span};

/// Given a list of order numbers, compute the total price.
#[instrument("process total price", skip_all, fields(outcome))]
pub fn get_total(order_numbers: &[u64]) -> Result<u64, anyhow::Error> {
let mut total = 0;
for order_number in order_numbers {
let order_details = get_order_details(*order_number).map_err(|e| {
let order_details = get_order_details(*order_number).inspect_err(|_| {
Span::current().record("outcome", "failure");
e
})?;
total += order_details.price;
}
Expand All @@ -35,11 +34,11 @@ pub struct OrderDetails {
/// A dummy function to simulate what would normally be a database query.
#[instrument("retrieve order", level = tracing::Level::TRACE, skip_all, fields(outcome))]
fn get_order_details(order_number: u64) -> Result<OrderDetails, anyhow::Error> {
if order_number % 4 == 0 {
if order_number.is_multiple_of(4) {
Span::current().record("outcome", "failure");
Err(anyhow::anyhow!("Failed to talk to the database"))
} else {
let prices = vec![999, 1089, 1029];
let prices = [999, 1089, 1029];
Span::current().record("outcome", "success");
Ok(OrderDetails {
order_number,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use helpers::MockWriter;
use opentelemetry_sdk::trace::SdkTracerProvider;

pub fn init_test_subscriber() -> MockWriter {
pub fn init_test_subscriber(provider: &SdkTracerProvider) -> MockWriter {
todo!()
}

pub fn init_tracer_provider() -> SdkTracerProvider {
todo!()
}
11 changes: 6 additions & 5 deletions exercises/01_structured_logging/09_subscriber/tests/failure.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use opentelemetry::global::shutdown_tracer_provider;
use serde_json::json;
use subscriber::init_test_subscriber;
use subscriber::{init_test_subscriber, init_tracer_provider};

#[tokio::test]
async fn failure() {
let logging_buffer = init_test_subscriber();
let provider = init_tracer_provider();
let logging_buffer = init_test_subscriber(&provider);
let order_numbers = vec![3, 4, 5];

subscriber::get_total(&order_numbers).unwrap_err();
Expand All @@ -23,7 +23,8 @@ async fn failure() {
log_lines.end();

// Ensure all spans are exported
tokio::task::spawn_blocking(|| shutdown_tracer_provider())
tokio::task::spawn_blocking(move || provider.shutdown())
.await
.unwrap();
.unwrap()
.expect("Error shutting down Open Telemetry tracing");
}
11 changes: 6 additions & 5 deletions exercises/01_structured_logging/09_subscriber/tests/success.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use opentelemetry::global::shutdown_tracer_provider;
use serde_json::json;
use subscriber::init_test_subscriber;
use subscriber::{init_test_subscriber, init_tracer_provider};

#[tokio::test]
async fn success() {
let logging_buffer = init_test_subscriber();
let provider = init_tracer_provider();
let logging_buffer = init_test_subscriber(&provider);
let order_numbers = vec![1, 2, 3];

let total = subscriber::get_total(&order_numbers).unwrap();
Expand All @@ -25,7 +25,8 @@ async fn success() {
log_lines.end();

// Ensure all spans are exported
tokio::task::spawn_blocking(|| shutdown_tracer_provider())
tokio::task::spawn_blocking(move || provider.shutdown())
.await
.unwrap();
.unwrap()
.expect("Error shutting down Open Telemetry tracing");
}
6 changes: 4 additions & 2 deletions exercises/03_metrics/04_prometheus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ mod tests {
/// Initialize a pull-based Prometheus recorder, listening on the address specified as input.
fn init_test_recorder(socket_addr: SocketAddr) {
// Needed for TLS to work
rustls::crypto::aws_lc_rs::default_provider().install_default().unwrap();
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.unwrap();

todo!()
}
Expand All @@ -29,7 +31,7 @@ mod tests {

let metrics_endpoint = format!("http://{}:{}", listener_addr.ip(), listener_addr.port());
let response = ureq::get(&metrics_endpoint).call().unwrap();
let body = response.into_string().unwrap();
let body = response.into_body().read_to_string().unwrap();
// This is what metrics look like when exported in Prometheus' format!
// You can clearly see how each combination of metric name and labels value is, under the
// hood, its own metric series.
Expand Down
Loading