From d74867e4c4ad3f53bdddb9069677919854bca2b5 Mon Sep 17 00:00:00 2001 From: J Haigh Date: Fri, 21 Jun 2019 12:12:15 -0500 Subject: [PATCH] Add an example and README (#67) This PR adds a README and an example to the `tokio-trace-serde` crate. --- tokio-trace-serde/Cargo.toml | 4 + tokio-trace-serde/README.md | 95 ++++++++++++ .../examples/serde_shaved_yak.rs | 135 ++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 tokio-trace-serde/README.md create mode 100644 tokio-trace-serde/examples/serde_shaved_yak.rs diff --git a/tokio-trace-serde/Cargo.toml b/tokio-trace-serde/Cargo.toml index 472a7a937e..a03f4724c7 100644 --- a/tokio-trace-serde/Cargo.toml +++ b/tokio-trace-serde/Cargo.toml @@ -7,3 +7,7 @@ license = "MIT" [dependencies] serde = "1" tokio-trace-core = "0.2.0" + +[dev-dependencies] +serde_json = "1.0" +tokio-trace = "0.1" diff --git a/tokio-trace-serde/README.md b/tokio-trace-serde/README.md new file mode 100644 index 0000000000..ee7d576418 --- /dev/null +++ b/tokio-trace-serde/README.md @@ -0,0 +1,95 @@ +# tokio-trace-serde + +An adapter for serializing `tokio-trace` types using `serde`. + +[Documentation](https://docs.rs/tokio-trace-serde/0.1.0/tokio_trace_serde/index.html) + +## Overview + +`tokio-trace-serde` enables serializing `tokio-trace` types using +`serde`. `tokio-trace` is a framework for instrumenting Rust programs +to collect structured, event-based diagnostic information. + +Traditional logging is based on human-readable text messages. +`tokio-trace` gives us machine-readable structured diagnostic +information. This lets us interact with diagnostic data +programmatically. With `tokio-trace-serde`, you can implement a +`Subscriber` to serialize your `tokio-trace` types and make use of the +existing ecosystem of `serde` serializers to talk with distributed +tracing systems. + +Serializing diagnostic information allows us to do more with our logged +values. For instance, when working with logging data in JSON gives us +pretty-print when we're debugging in development and you can emit JSON +and tracing data to monitor your services in production. + +The `tokio-trace` crate provides the APIs necessary for instrumenting +libraries and applications to emit trace data. + +## Usage + +First, add this to your `Cargo.toml`: + +```toml +[dependencies] +tokio-trace = "0.1" +tokio-trace-serde = "0.1" +``` + +Next, add this to your crate: + +```rust +#[macro_use] +extern crate tokio_trace; +extern crate tokio_trace_serde; + +use tokio_trace_serde::AsSerde; +``` + +Please read the [`tokio-trace` documentation](https://docs.rs/tokio-trace/0.1.0/tokio_trace/index.html) +for more information on how to create trace data. + +This crate provides the `as_serde` function, via the `AsSerde` trait, +which enables serializing the `Attributes`, `Event`, `Id`, `Metadata`, +and `Record` `tokio-trace` values. + +For the full example, please see the [examples](../examples) folder. + +Implement a `Subscriber` to format the serialization of `tokio-trace` +types how you'd like. + +```rust +pub struct JsonSubscriber { + next_id: AtomicUsize, // you need to assign span IDs, so you need a counter +} + +impl Subscriber for JsonSubscriber { + + fn new_span(&self, attrs: &Attributes) -> Id { + let id = self.next_id.fetch_add(1, Ordering::Relaxed); + let id = Id::from_u64(id as u64); + let json = json!({ + "new_span": { + "attributes": attrs.as_serde(), + "id": id.as_serde(), + }}); + println!("{}", json); + id + } + // ... +} +``` + +After you implement your `Subscriber`, you can use your `tokio-trace` +subscriber (`JsonSubscriber` in the above example) to record serialized +trace data. + +## License + +This project is licensed under the [MIT license](LICENSE). + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in Tokio by you, shall be licensed as MIT, without any additional +terms or conditions. diff --git a/tokio-trace-serde/examples/serde_shaved_yak.rs b/tokio-trace-serde/examples/serde_shaved_yak.rs new file mode 100644 index 0000000000..f05301061c --- /dev/null +++ b/tokio-trace-serde/examples/serde_shaved_yak.rs @@ -0,0 +1,135 @@ +#[macro_use] +extern crate serde_json; + +#[macro_use] +extern crate tokio_trace; +extern crate tokio_trace_core; +extern crate tokio_trace_serde; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +use tokio_trace_core::{ + event::Event, + metadata::{Level, Metadata}, + span::{Attributes, Id, Record}, + subscriber::Subscriber, +}; + +use tokio_trace_serde::AsSerde; + +pub struct JsonSubscriber { + next_id: AtomicUsize, // you need to assign span IDs, so you need a counter +} + +impl Subscriber for JsonSubscriber { + fn enabled(&self, metadata: &Metadata) -> bool { + let json = json!({ + "enabled": { + "metadata": metadata.as_serde(), + }}); + println!("{}", json); + true + } + + fn new_span(&self, attrs: &Attributes) -> Id { + let id = self.next_id.fetch_add(1, Ordering::Relaxed); + let id = Id::from_u64(id as u64); + let json = json!({ + "new_span": { + "attributes": attrs.as_serde(), + "id": id.as_serde(), + }}); + println!("{}", json); + id + } + + fn record(&self, span: &Id, values: &Record) { + let json = json!({ + "record": { + "span": span.as_serde(), + "values": values.as_serde(), + }}); + println!("{}", json); + } + + fn record_follows_from(&self, span: &Id, follows: &Id) { + let json = json!({ + "record_follows_from": { + "span": span.as_serde(), + "follows": follows.as_serde(), + }}); + println!("{}", json); + } + + fn event(&self, event: &Event) { + let json = json!({ + "event": event.as_serde(), + }); + println!("{}", json); + } + + fn enter(&self, span: &Id) { + let json = json!({ + "enter": span.as_serde(), + }); + println!("{}", json); + } + + fn exit(&self, span: &Id) { + let json = json!({ + "exit": span.as_serde(), + }); + println!("{}", json); + } +} + +fn shave(yak: usize) -> bool { + span!(Level::TRACE, "shave", yak = yak).enter(|| { + debug!( + message = "hello! I'm gonna shave a yak.", + excitement = "yay!" + ); + if yak == 3 { + warn!(target: "yak_events", "could not locate yak!"); + false + } else { + trace!(target: "yak_events", "yak shaved successfully"); + true + } + }) +} + +fn main() { + let subscriber = JsonSubscriber { + next_id: AtomicUsize::new(1), + }; + + tokio_trace::subscriber::with_default(subscriber, || { + let number_of_yaks = 3; + let mut number_shaved = 0; + debug!("preparing to shave {} yaks", number_of_yaks); + + span!(Level::TRACE, "shaving_yaks", yaks_to_shave = number_of_yaks).enter(|| { + info!("shaving yaks"); + + for yak in 1..=number_of_yaks { + let shaved = shave(yak); + trace!(target: "yak_events", yak = yak, shaved = shaved); + + if !shaved { + error!(message = "failed to shave yak!", yak = yak); + } else { + number_shaved += 1; + } + + trace!(target: "yak_events", yaks_shaved = number_shaved); + } + }); + + debug!( + message = "yak shaving completed.", + all_yaks_shaved = number_shaved == number_of_yaks, + ); + }); +}