Skip to content

Commit

Permalink
Tracing via context (#99)
Browse files Browse the repository at this point in the history
This change moves the storage and management of active spans from the
current `SpanStack` implementation to the new `Context` api, as well as
updating the propagation APIs to inject and extract contexts to remain
compatible with the tracers. This has some breaking API changes to the
way tracing is currently done, most notable is that the `Tracer`'s
`start` method no longer requires an optional parent span context as
that can now be provided by the current context.

The previous tracing api was:

```rust
// extract
let remote_span_context = propagator.extract(&carrier);

// start
let parent = tracer.start("parent", Some(remote_context));
tracer.mark_span_as_active(&parent);

// nest (note: `Some(parent.get_context())` and `None` had the same effect here)
let child = tracer.start("child", None);
tracer.mark_span_as_active(&child)

// inject
propagator.inject(child.get_context(), &mut carrier);

// Marking as inactive was required and error-prone
trace.mark_span_as_inactive(&child);
trace.mark_span_as_inactive(&parent);
```

And the new API is:

```rust
// extract
let _attach = propagator.extract(&carrier).attach();

// start
let parent = tracer.start("parent");
let _parent_active = tracer.mark_span_as_active(parent);

// start
let child = tracer.start("child");
let _child_active = tracer.mark_span_as_active(parent);

// inject
propagator.inject(&mut carrier)
```

Additional changes to facilitate the switch:

* `tracer.with_span` now accepts a span for naming consistency and
managing the active state of a more complex span (likely produced by a
builder), and the previous functionality that accepts a `&str` has been
renamed to `in_span`, both of which now yield a context to the provided
closure.
* The `Instrument` trait has been renamed to `FutureExt` to avoid
clashing with metric instruments, and accepts contexts.
* `TracerGenerics` methods have been folded in to the `Tracer` trait so
the trait is no longer needed 🎉 .
* A `TraceContextExt` trait provides methods for working with trace data
in a context. Most notably `context.span()` returns a `&dyn api::Span`
reference, and `Context::current_with_span(span)` creates a clone of the
current context with the span added.
* Span's managing their own active state is no longer needed 🎉.
* Span's `get_context` method has been renamed to `span_context` to
avoid the ambiguity.
  • Loading branch information
jtescher authored May 7, 2020
1 parent 3497774 commit c579248
Show file tree
Hide file tree
Showing 38 changed files with 778 additions and 601 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ bincode = { version = "1.2.1", optional = true }

[dev-dependencies]
criterion = "0.3.1"
tokio = "0.2"
tokio = { version = "0.2", features = ["full"] }

[features]
default = ["metrics", "trace"]
Expand Down
12 changes: 5 additions & 7 deletions benches/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ use opentelemetry::{
};

fn criterion_benchmark(c: &mut Criterion) {
trace_benchmark_group(c, "start-end-span", |tracer| {
tracer.start("foo", None).end()
});
trace_benchmark_group(c, "start-end-span", |tracer| tracer.start("foo").end());

trace_benchmark_group(c, "start-end-span-4-attrs", |tracer| {
let span = tracer.start("foo", None);
let span = tracer.start("foo");
span.set_attribute(Key::new("key1").bool(false));
span.set_attribute(Key::new("key2").string("hello"));
span.set_attribute(Key::new("key3").u64(123));
Expand All @@ -19,7 +17,7 @@ fn criterion_benchmark(c: &mut Criterion) {
});

trace_benchmark_group(c, "start-end-span-8-attrs", |tracer| {
let span = tracer.start("foo", None);
let span = tracer.start("foo");
span.set_attribute(Key::new("key1").bool(false));
span.set_attribute(Key::new("key2").string("hello"));
span.set_attribute(Key::new("key3").u64(123));
Expand All @@ -32,7 +30,7 @@ fn criterion_benchmark(c: &mut Criterion) {
});

trace_benchmark_group(c, "start-end-span-all-attr-types", |tracer| {
let span = tracer.start("foo", None);
let span = tracer.start("foo");
span.set_attribute(Key::new("key1").bool(false));
span.set_attribute(Key::new("key2").string("hello"));
span.set_attribute(Key::new("key3").i64(123));
Expand All @@ -45,7 +43,7 @@ fn criterion_benchmark(c: &mut Criterion) {
});

trace_benchmark_group(c, "start-end-span-all-attr-types-2x", |tracer| {
let span = tracer.start("foo", None);
let span = tracer.start("foo");
span.set_attribute(Key::new("key1").bool(false));
span.set_attribute(Key::new("key2").string("hello"));
span.set_attribute(Key::new("key3").i64(123));
Expand Down
14 changes: 6 additions & 8 deletions examples/actix/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use opentelemetry::api::{Key, Span, TracerGenerics};
use opentelemetry::{global, sdk};

use actix_service::Service;
use actix_web::{web, App, HttpServer};
use futures::future::Future;
use opentelemetry::api::{Key, TraceContextExt, Tracer};
use opentelemetry::{global, sdk};

fn init_tracer() -> thrift::Result<()> {
let exporter = opentelemetry_jaeger::Exporter::builder()
Expand All @@ -30,9 +29,8 @@ fn init_tracer() -> thrift::Result<()> {

fn index() -> &'static str {
let tracer = global::tracer("request");

tracer.with_span("index", move |span| {
span.set_attribute(Key::new("parameter").i64(10));
tracer.in_span("index", |ctx| {
ctx.span().set_attribute(Key::new("parameter").i64(10));
"Index"
})
}
Expand All @@ -44,8 +42,8 @@ fn main() -> thrift::Result<()> {
App::new()
.wrap_fn(|req, srv| {
let tracer = global::tracer("request");
tracer.with_span("middleware", move |span| {
span.set_attribute(Key::new("path").string(req.path()));
tracer.in_span("middleware", move |cx| {
cx.span().set_attribute(Key::new("path").string(req.path()));
srv.call(req).map(|res| res)
})
})
Expand Down
24 changes: 14 additions & 10 deletions examples/async/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//!
//! [`hello_world`]: https://github.com/tokio-rs/tokio/blob/132e9f1da5965530b63554d7a1c59824c3de4e30/tokio/examples/hello_world.rs
use opentelemetry::{
api::{trace::futures::Instrument, Tracer},
api::{trace::futures::FutureExt, Context, TraceContextExt, Tracer},
global, sdk,
};
use std::time::Duration;
Expand All @@ -28,24 +28,27 @@ use tokio::net::TcpStream;

async fn connect(addr: &SocketAddr) -> io::Result<TcpStream> {
let tracer = global::tracer("connector");
let span = tracer.start("Connecting", None);
let span = tracer.start("Connecting");
let cx = Context::current_with_value(span);

TcpStream::connect(&addr).instrument(span).await
TcpStream::connect(&addr).with_context(cx).await
}

async fn write(stream: &mut TcpStream) -> io::Result<usize> {
let tracer = global::tracer("writer");
let span = tracer.start("Writing", None);
let span = tracer.start("Writing");
let cx = Context::current_with_span(span);

stream.write(b"hello world\n").instrument(span).await
stream.write(b"hello world\n").with_context(cx).await
}

async fn run(addr: &SocketAddr) -> io::Result<usize> {
let tracer = global::tracer("runner");
let span = tracer.start(&format!("running: {}", addr), None);
let span = tracer.start(&format!("running: {}", addr));
let cx = Context::current_with_span(span);

let mut stream = connect(addr).instrument(tracer.clone_span(&span)).await?;
write(&mut stream).instrument(span).await
let mut stream = connect(addr).with_context(cx.clone()).await?;
write(&mut stream).with_context(cx).await
}

fn init_tracer() -> thrift::Result<()> {
Expand Down Expand Up @@ -83,10 +86,11 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
let addr = "127.0.0.1:6142".parse()?;
let addr2 = "127.0.0.1:6143".parse()?;
let tracer = global::tracer("async_example");
let span = tracer.start("root", None);
let span = tracer.start("root");
let cx = Context::current_with_span(span);

let (run1, run2) = futures::future::join(run(&addr), run(&addr2))
.instrument(span)
.with_context(cx)
.await;
run1?;
run2?;
Expand Down
8 changes: 5 additions & 3 deletions examples/basic/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use opentelemetry::api::{
Gauge, GaugeHandle, Key, Measure, MeasureHandle, Meter, MetricOptions, Span, TracerGenerics,
Gauge, GaugeHandle, Key, Measure, MeasureHandle, Meter, MetricOptions, TraceContextExt, Tracer,
};
use opentelemetry::{global, sdk};

Expand Down Expand Up @@ -54,7 +54,8 @@ fn main() -> thrift::Result<()> {

let measure = measure_two.acquire_handle(&common_labels);

global::tracer("component-main").with_span("operation", move |span| {
global::tracer("component-main").in_span("operation", move |cx| {
let span = cx.span();
span.add_event(
"Nice operation!".to_string(),
vec![Key::new("bogons").i64(100)],
Expand All @@ -68,7 +69,8 @@ fn main() -> thrift::Result<()> {
vec![one_metric.measurement(1.0), measure_two.measurement(2.0)],
);

global::tracer("component-bar").with_span("Sub operation...", move |span| {
global::tracer("component-bar").in_span("Sub operation...", move |cx| {
let span = cx.span();
span.set_attribute(lemons_key.string("five"));

span.add_event("Sub span event".to_string(), vec![]);
Expand Down
14 changes: 7 additions & 7 deletions examples/grpc/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use hello_world::greeter_client::GreeterClient;
use hello_world::HelloRequest;
use opentelemetry::api::{HttpTextFormat, KeyValue, Span, TraceContextPropagator, Tracer};
use opentelemetry::api::{
Context, HttpTextFormat, KeyValue, TraceContextExt, TraceContextPropagator, Tracer,
};
use opentelemetry::sdk::Sampler;
use opentelemetry::{api, global, sdk};

Expand Down Expand Up @@ -55,19 +57,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_init()?;
let mut client = GreeterClient::connect("http://[::1]:50051").await?;
let propagator = TraceContextPropagator::new();
let request_span = global::tracer("client").start("client-request", None);
let span = global::tracer("client").start("client-request");
let cx = Context::current_with_span(span);

let mut request = tonic::Request::new(HelloRequest {
name: "Tonic".into(),
});
propagator.inject(
request_span.get_context(),
&mut TonicMetadataMapCarrier(request.metadata_mut()),
);
propagator.inject_context(&cx, &mut TonicMetadataMapCarrier(request.metadata_mut()));

let response = client.say_hello(request).await?;

request_span.add_event(
cx.span().add_event(
"response-received".to_string(),
vec![KeyValue::new("response", format!("{:?}", response))],
);
Expand Down
4 changes: 2 additions & 2 deletions examples/grpc/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ impl Greeter for MyGreeter {
request: Request<HelloRequest>, // Accept request of type HelloRequest
) -> Result<Response<HelloReply>, Status> {
let propagator = api::TraceContextPropagator::new();
let parent = propagator.extract(&HttpHeaderMapCarrier(request.metadata()));
let span = global::tracer("greeter").start("Processing reply", Some(parent));
let parent_cx = propagator.extract(&HttpHeaderMapCarrier(request.metadata()));
let span = global::tracer("greeter").start_from_context("Processing reply", &parent_cx);
span.set_attribute(KeyValue::new("request", format!("{:?}", request)));

// Return an instance of type HelloReply
Expand Down
12 changes: 5 additions & 7 deletions examples/http/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use hyper::{body::Body, Client};
use opentelemetry::api::{HttpTextFormat, Span, Tracer};
use opentelemetry::api::{Context, HttpTextFormat, TraceContextExt, Tracer};
use opentelemetry::{api, exporter::trace::stdout, global, sdk};

struct ClientHeaderMapCarrier<'a>(&'a mut hyper::header::HeaderMap);
Expand Down Expand Up @@ -36,16 +36,14 @@ async fn main() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sy

let client = Client::new();
let propagator = api::TraceContextPropagator::new();
let span = global::tracer("example/client").start("say hello", None);
let span = global::tracer("example/client").start("say hello");
let cx = Context::current_with_span(span);

let mut req = hyper::Request::builder().uri("http://127.0.0.1:3000");
propagator.inject(
span.get_context(),
&mut ClientHeaderMapCarrier(req.headers_mut().unwrap()),
);
propagator.inject_context(&cx, &mut ClientHeaderMapCarrier(req.headers_mut().unwrap()));
let res = client.request(req.body(Body::from("Hallo!"))?).await?;

span.add_event(
cx.span().add_event(
"Got response!".to_string(),
vec![api::KeyValue::new("status", res.status().to_string())],
);
Expand Down
7 changes: 3 additions & 4 deletions examples/http/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use opentelemetry::{
api,
api::{HttpTextFormat, Span, Tracer},
api::{self, HttpTextFormat, Span, Tracer},
exporter::trace::stdout,
global, sdk,
};
Expand All @@ -21,8 +20,8 @@ impl<'a> api::Carrier for HttpHeaderMapCarrier<'a> {

async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> {
let propagator = api::TraceContextPropagator::new();
let parent_context = propagator.extract(&HttpHeaderMapCarrier(req.headers()));
let span = global::tracer("example/server").start("hello", Some(parent_context));
let parent_cx = propagator.extract(&HttpHeaderMapCarrier(req.headers()));
let span = global::tracer("example/server").start_from_context("hello", &parent_cx);
span.add_event("handling this...".to_string(), Vec::new());

Ok(Response::new("Hello, World!".into()))
Expand Down
4 changes: 2 additions & 2 deletions examples/stdout.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use opentelemetry::exporter::trace::stdout;
use opentelemetry::{
api::{Provider, TracerGenerics},
api::{Provider, Tracer},
global, sdk,
};

Expand All @@ -21,5 +21,5 @@ fn main() {

global::trace_provider()
.get_tracer("component-main")
.with_span("operation", move |_span| {});
.in_span("operation", |_cx| {});
}
6 changes: 3 additions & 3 deletions examples/zipkin/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use opentelemetry::api::{Span, Tracer, TracerGenerics};
use opentelemetry::api::{Span, Tracer};
use opentelemetry::{global, sdk};
use std::thread;
use std::time::Duration;
Expand All @@ -25,7 +25,7 @@ fn init_tracer() {

fn bar() {
let tracer = global::tracer("component-bar");
let span = tracer.start("bar", None);
let span = tracer.start("bar");
thread::sleep(Duration::from_millis(6));
span.end()
}
Expand All @@ -34,7 +34,7 @@ fn main() {
init_tracer();
let tracer = global::tracer("component-main");

tracer.with_span("foo", |_span| {
tracer.in_span("foo", |_cx| {
thread::sleep(Duration::from_millis(6));
bar();
thread::sleep(Duration::from_millis(6));
Expand Down
13 changes: 4 additions & 9 deletions opentelemetry-jaeger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ use self::thrift::jaeger;
use opentelemetry::{api, exporter::trace, sdk};
use std::sync::{Arc, Mutex};
use std::{
any, net,
net,
time::{Duration, SystemTime},
};

Expand Down Expand Up @@ -167,11 +167,6 @@ impl trace::SpanExporter for Exporter {

/// Ignored for now.
fn shutdown(&self) {}

/// Allows `Exporter` to be downcast from trait object.
fn as_any(&self) -> &dyn any::Any {
self
}
}

/// Jaeger exporter builder
Expand Down Expand Up @@ -328,17 +323,17 @@ impl Into<jaeger::Log> for api::Event {
impl Into<jaeger::Span> for Arc<trace::SpanData> {
/// Convert spans to jaeger thrift span for exporting.
fn into(self) -> jaeger::Span {
let trace_id = self.context.trace_id().to_u128();
let trace_id = self.span_context.trace_id().to_u128();
let trace_id_high = (trace_id >> 64) as i64;
let trace_id_low = trace_id as i64;
jaeger::Span {
trace_id_low,
trace_id_high,
span_id: self.context.span_id().to_u64() as i64,
span_id: self.span_context.span_id().to_u64() as i64,
parent_span_id: self.parent_span_id.to_u64() as i64,
operation_name: self.name.clone(),
references: links_to_references(&self.links),
flags: self.context.trace_flags() as i32,
flags: self.span_context.trace_flags() as i32,
start_time: self
.start_time
.duration_since(SystemTime::UNIX_EPOCH)
Expand Down
15 changes: 8 additions & 7 deletions opentelemetry-zipkin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ extern crate typed_builder;
mod model;
mod uploader;

use core::any;
use model::{annotation, endpoint, span};
use opentelemetry::api;
use opentelemetry::exporter::trace;
Expand Down Expand Up @@ -170,10 +169,6 @@ impl trace::SpanExporter for Exporter {
}

fn shutdown(&self) {}

fn as_any(&self) -> &dyn any::Any {
self
}
}

/// Converts `api::Event` into an `annotation::Annotation`
Expand Down Expand Up @@ -207,9 +202,15 @@ fn into_zipkin_span_kind(kind: api::SpanKind) -> Option<span::Kind> {
/// be ingested into a Zipkin collector.
fn into_zipkin_span(config: &ExporterConfig, span_data: Arc<trace::SpanData>) -> span::Span {
span::Span::builder()
.trace_id(format!("{:032x}", span_data.context.trace_id().to_u128()))
.trace_id(format!(
"{:032x}",
span_data.span_context.trace_id().to_u128()
))
.parent_id(format!("{:016x}", span_data.parent_span_id.to_u64()))
.id(format!("{:016x}", span_data.context.span_id().to_u64()))
.id(format!(
"{:016x}",
span_data.span_context.span_id().to_u64()
))
.name(span_data.name.clone())
.kind(into_zipkin_span_kind(span_data.span_kind.clone()))
.timestamp(
Expand Down
2 changes: 2 additions & 0 deletions src/api/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ use std::fmt;
use std::hash::{BuildHasherDefault, Hasher};
use std::rc::Rc;

pub mod propagation;

thread_local! {
static CURRENT_CONTEXT: RefCell<Context> = RefCell::new(Context::default());
static DEFAULT_CONTEXT: Context = Context::default();
Expand Down
Loading

0 comments on commit c579248

Please sign in to comment.