Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Backports to v0.1.x #2298

Merged
merged 11 commits into from Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/CI.yml
Expand Up @@ -214,6 +214,7 @@ jobs:
--exclude=tracing-appender
--exclude=tracing-examples
--exclude=tracing-futures
--exclude=tracing-opentelemetry
toolchain: ${{ env.MSRV }}

# TODO: remove this once tracing's MSRV is bumped.
Expand Down
7 changes: 6 additions & 1 deletion README.md
Expand Up @@ -241,7 +241,7 @@ my_future
`Future::instrument` attaches a span to the future, ensuring that the span's lifetime
is as long as the future's.

Under the hood, the [`#[instrument]`][instrument] macro performs same the explicit span
Under the hood, the [`#[instrument]`][instrument] macro performs the same explicit span
attachment that `Future::instrument` does.

[std-future]: https://doc.rust-lang.org/stable/std/future/trait.Future.html
Expand Down Expand Up @@ -390,6 +390,8 @@ are not maintained by the `tokio` project. These include:
_inside_ of functions.
- [`tracing-wasm`] provides a `Subscriber`/`Layer` implementation that reports
events and spans via browser `console.log` and [User Timing API (`window.performance`)].
- [`tracing-web`] provides a layer implementation of level-aware logging of events
to web browsers' `console.*` and span events to the [User Timing API (`window.performance`)].
- [`test-log`] takes care of initializing `tracing` for tests, based on
environment variables with an `env_logger` compatible syntax.
- [`tracing-unwrap`] provides convenience methods to report failed unwraps on `Result` or `Option` types to a `Subscriber`.
Expand All @@ -403,6 +405,7 @@ are not maintained by the `tokio` project. These include:
grouping together logs from the same spans during writing.
- [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
- [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
- [`tracing-chrome`] provides a layer that exports trace data that can be viewed in `chrome://tracing`.

(if you're the maintainer of a `tracing` ecosystem crate not in this list,
please let us know!)
Expand All @@ -424,6 +427,7 @@ please let us know!)
[`color-eyre`]: https://docs.rs/color-eyre
[`spandoc`]: https://docs.rs/spandoc
[`tracing-wasm`]: https://docs.rs/tracing-wasm
[`tracing-web`]: https://crates.io/crates/tracing-web
[`test-log`]: https://crates.io/crates/test-log
[User Timing API (`window.performance`)]: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
[`tracing-unwrap`]: https://docs.rs/tracing-unwrap
Expand All @@ -441,6 +445,7 @@ please let us know!)
[`tracing-loki`]: https://crates.io/crates/tracing-loki
[Grafana Loki]: https://grafana.com/oss/loki/
[`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
[`tracing-chrome`]: https://crates.io/crates/tracing-chrome

**Note:** that some of the ecosystem crates are currently unreleased and
undergoing active development. They may be less stable than `tracing` and
Expand Down
4 changes: 2 additions & 2 deletions examples/Cargo.toml
Expand Up @@ -52,8 +52,8 @@ inferno = "0.11.6"
tempfile = "3"

# opentelemetry example
opentelemetry = { version = "0.17.0", default-features = false, features = ["trace"] }
opentelemetry-jaeger = "0.16.0"
opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] }
opentelemetry-jaeger = "0.17.0"

# fmt examples
snafu = "0.6.10"
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/opentelemetry.rs
Expand Up @@ -19,7 +19,7 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
// Install an otel pipeline with a simple span processor that exports data one at a time when
// spans end. See the `install_batch` option on each exporter's pipeline builder to see how to
// export in batches.
let tracer = opentelemetry_jaeger::new_pipeline()
let tracer = opentelemetry_jaeger::new_agent_pipeline()
.with_service_name("report_example")
.install_simple()?;
let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer);
Expand Down
2 changes: 2 additions & 0 deletions tracing-attributes/Cargo.toml
Expand Up @@ -50,6 +50,8 @@ tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", featur
tokio-test = { version = "0.3.0" }
tracing-core = { path = "../tracing-core", version = "0.1.28"}
async-trait = "0.1.56"
trybuild = "1.0.64"
rustversion = "1.0.9"

[badges]
maintenance = { status = "experimental" }
89 changes: 69 additions & 20 deletions tracing-attributes/src/expand.rs
Expand Up @@ -2,10 +2,11 @@ use std::iter;

use proc_macro2::TokenStream;
use quote::{quote, quote_spanned, ToTokens};
use syn::visit_mut::VisitMut;
use syn::{
punctuated::Punctuated, spanned::Spanned, Block, Expr, ExprAsync, ExprCall, FieldPat, FnArg,
Ident, Item, ItemFn, Pat, PatIdent, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType,
Path, Signature, Stmt, Token, TypePath,
Path, ReturnType, Signature, Stmt, Token, Type, TypePath,
};

use crate::{
Expand All @@ -18,7 +19,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
input: MaybeItemFnRef<'a, B>,
args: InstrumentArgs,
instrumented_function_name: &str,
self_type: Option<&syn::TypePath>,
self_type: Option<&TypePath>,
) -> proc_macro2::TokenStream {
// these are needed ahead of time, as ItemFn contains the function body _and_
// isn't representable inside a quote!/quote_spanned! macro
Expand All @@ -31,7 +32,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
} = input;

let Signature {
output: return_type,
output,
inputs: params,
unsafety,
asyncness,
Expand All @@ -49,8 +50,35 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(

let warnings = args.warnings();

let (return_type, return_span) = if let ReturnType::Type(_, return_type) = &output {
(erase_impl_trait(return_type), return_type.span())
} else {
// Point at function name if we don't have an explicit return type
(syn::parse_quote! { () }, ident.span())
};
// Install a fake return statement as the first thing in the function
// body, so that we eagerly infer that the return type is what we
// declared in the async fn signature.
// The `#[allow(..)]` is given because the return statement is
// unreachable, but does affect inference, so it needs to be written
// exactly that way for it to do its magic.
let fake_return_edge = quote_spanned! {return_span=>
#[allow(unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value)]
if false {
let __tracing_attr_fake_return: #return_type =
unreachable!("this is just for type inference, and is unreachable code");
return __tracing_attr_fake_return;
}
};
let block = quote! {
{
#fake_return_edge
#block
}
};

let body = gen_block(
block,
&block,
params,
asyncness.is_some(),
args,
Expand All @@ -60,7 +88,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(

quote!(
#(#attrs) *
#vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #return_type
#vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #output
#where_clause
{
#warnings
Expand All @@ -76,7 +104,7 @@ fn gen_block<B: ToTokens>(
async_context: bool,
mut args: InstrumentArgs,
instrumented_function_name: &str,
self_type: Option<&syn::TypePath>,
self_type: Option<&TypePath>,
) -> proc_macro2::TokenStream {
// generate the span's name
let span_name = args
Expand Down Expand Up @@ -393,11 +421,11 @@ impl RecordType {
"Wrapping",
];

/// Parse `RecordType` from [syn::Type] by looking up
/// Parse `RecordType` from [Type] by looking up
/// the [RecordType::TYPES_FOR_VALUE] array.
fn parse_from_ty(ty: &syn::Type) -> Self {
fn parse_from_ty(ty: &Type) -> Self {
match ty {
syn::Type::Path(syn::TypePath { path, .. })
Type::Path(TypePath { path, .. })
if path
.segments
.iter()
Expand All @@ -410,9 +438,7 @@ impl RecordType {
{
RecordType::Value
}
syn::Type::Reference(syn::TypeReference { elem, .. }) => {
RecordType::parse_from_ty(elem)
}
Type::Reference(syn::TypeReference { elem, .. }) => RecordType::parse_from_ty(elem),
_ => RecordType::Debug,
}
}
Expand Down Expand Up @@ -471,7 +497,7 @@ pub(crate) struct AsyncInfo<'block> {
// statement that must be patched
source_stmt: &'block Stmt,
kind: AsyncKind<'block>,
self_type: Option<syn::TypePath>,
self_type: Option<TypePath>,
input: &'block ItemFn,
}

Expand Down Expand Up @@ -606,11 +632,11 @@ impl<'block> AsyncInfo<'block> {
if ident == "_self" {
let mut ty = *ty.ty.clone();
// extract the inner type if the argument is "&self" or "&mut self"
if let syn::Type::Reference(syn::TypeReference { elem, .. }) = ty {
if let Type::Reference(syn::TypeReference { elem, .. }) = ty {
ty = *elem;
}

if let syn::Type::Path(tp) = ty {
if let Type::Path(tp) = ty {
self_type = Some(tp);
break;
}
Expand Down Expand Up @@ -722,7 +748,7 @@ struct IdentAndTypesRenamer<'a> {
idents: Vec<(Ident, Ident)>,
}

impl<'a> syn::visit_mut::VisitMut for IdentAndTypesRenamer<'a> {
impl<'a> VisitMut for IdentAndTypesRenamer<'a> {
// we deliberately compare strings because we want to ignore the spans
// If we apply clippy's lint, the behavior changes
#[allow(clippy::cmp_owned)]
Expand All @@ -734,11 +760,11 @@ impl<'a> syn::visit_mut::VisitMut for IdentAndTypesRenamer<'a> {
}
}

fn visit_type_mut(&mut self, ty: &mut syn::Type) {
fn visit_type_mut(&mut self, ty: &mut Type) {
for (type_name, new_type) in &self.types {
if let syn::Type::Path(TypePath { path, .. }) = ty {
if let Type::Path(TypePath { path, .. }) = ty {
if path_to_string(path) == *type_name {
*ty = syn::Type::Path(new_type.clone());
*ty = Type::Path(new_type.clone());
}
}
}
Expand All @@ -751,10 +777,33 @@ struct AsyncTraitBlockReplacer<'a> {
patched_block: Block,
}

impl<'a> syn::visit_mut::VisitMut for AsyncTraitBlockReplacer<'a> {
impl<'a> VisitMut for AsyncTraitBlockReplacer<'a> {
fn visit_block_mut(&mut self, i: &mut Block) {
if i == self.block {
*i = self.patched_block.clone();
}
}
}

// Replaces any `impl Trait` with `_` so it can be used as the type in
// a `let` statement's LHS.
struct ImplTraitEraser;

impl VisitMut for ImplTraitEraser {
fn visit_type_mut(&mut self, t: &mut Type) {
if let Type::ImplTrait(..) = t {
*t = syn::TypeInfer {
underscore_token: Token![_](t.span()),
}
.into();
} else {
syn::visit_mut::visit_type_mut(self, t);
}
}
}

fn erase_impl_trait(ty: &Type) -> Type {
let mut ty = ty.clone();
ImplTraitEraser.visit_type_mut(&mut ty);
ty
}
7 changes: 7 additions & 0 deletions tracing-attributes/tests/ui.rs
@@ -0,0 +1,7 @@
// Only test on nightly, since UI tests are bound to change over time
#[rustversion::stable]
#[test]
fn async_instrument() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/async_instrument.rs");
}
46 changes: 46 additions & 0 deletions tracing-attributes/tests/ui/async_instrument.rs
@@ -0,0 +1,46 @@
#![allow(unreachable_code)]

#[tracing::instrument]
async fn unit() {
""
}

#[tracing::instrument]
async fn simple_mismatch() -> String {
""
}

// FIXME: this span is still pretty poor
#[tracing::instrument]
async fn opaque_unsatisfied() -> impl std::fmt::Display {
("",)
}

struct Wrapper<T>(T);

#[tracing::instrument]
async fn mismatch_with_opaque() -> Wrapper<impl std::fmt::Display> {
""
}

#[tracing::instrument]
async fn early_return_unit() {
if true {
return "";
}
}

#[tracing::instrument]
async fn early_return() -> String {
if true {
return "";
}
String::new()
}

#[tracing::instrument]
async fn extra_semicolon() -> i32 {
1;
}

fn main() {}