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

Failing when macro has % in it, e.g. Tracing #5516

Open
xphoniex opened this issue Aug 23, 2022 · 9 comments
Open

Failing when macro has % in it, e.g. Tracing #5516

xphoniex opened this issue Aug 23, 2022 · 9 comments

Comments

@xphoniex
Copy link

Here's a sample code:

            TraceLayer::new_for_http()
                .make_span_with(|request: &Request<Body>| {
                    tracing::info_span!(
                        "request",
                        method = %request.method(),
                        uri = %request.uri(), status = tracing::field::Empty,
                        latency = tracing::field::Empty,
                    )
                })

this uses a macro from tracing to create the Span. However the % in there makes rustfmt fail.

@ytmimi ytmimi added the needs-mcve needs a Minimal Complete and Verifiable Example label Aug 23, 2022
@ytmimi
Copy link
Contributor

ytmimi commented Aug 23, 2022

Thanks for reaching out. What version of rustfmt are you using? (run rustfmt --version to find out). I'm using rustfmt 1.5.1-nightly (38659ec6 2022-08-18), and can't reproduce any issues. Are you using any non default config options? Could you please post the larger code snippet.

@xphoniex
Copy link
Author

$ rustfmt --version
rustfmt 1.5.1-stable (4b91a6e 2022-08-08)

reproducible examples: (add tracing = "0.1.36" to Cargo.toml)

fn main() {
    let span = tracing::info_span!("request", method = %request.method(), uri = %request.uri(), status = tracing::field::Empty, latency = tracing::field::Empty,);
}
fn main() {
    let span = tracing::info_span!(
        "request",
        method = %request.method(),
        uri = %request.uri(), status = tracing::field::Empty,
        latency = tracing::field::Empty,
    );
}

expected output:

fn main() {
    let span = tracing::info_span!(
        "request",
        method = %request.method(),
        uri = %request.uri(),
        status = tracing::field::Empty,
        latency = tracing::field::Empty,
    );
}

If you remove the % in examples, you'll get the same output as "expected", obviously without the %. Otherwise, rustfmt doesn't make any change to the two examples.

@ytmimi
Copy link
Contributor

ytmimi commented Aug 23, 2022

We actually don't need to to add tracing = "0.1.36" to Cargo.toml to reproduce. I understand now that the issue is that we're not formatting the macro at all, and unfortunately as things currently stand there's not much we can do. You're correct that the % are causing the issue.

Formatting macros is tricky as they can contain arbitrary tokens and in this case the % prevents us from parsing the macro args as valid rust code so we just emit the macro as is. rustfmt isn't programmed to handle cases where macro args don't parse as valid rust code e.g %request.

The only way to handle this would be to special case the info_span! macro and customize how we parse and format its args. Given the team's backlog this is an extremely low priority item.

@ytmimi ytmimi added p-low a-macros and removed needs-mcve needs a Minimal Complete and Verifiable Example labels Aug 23, 2022
@ytmimi
Copy link
Contributor

ytmimi commented Aug 23, 2022

I'm not familiar with the tracing::info_span! macro, and found some more details on the % by reading the Recorder Fields section of the docs. Looks like ? is also used sometimes.

@mladedav
Copy link

mladedav commented Mar 7, 2023

I'lll just add that while ? is also used, it behaves slightly differently.

One can use any of the following with the first three being equivalent and the fourth through sixth being equivalent.

  • o tracing::info!(?x, "text")
  • x tracing::info!(x = ?x, "text")
  • o tracing::info!(x = format!("{x:?}"), "text")
  • x tracing::info!(%x, "text")
  • x tracing::info!(x = %x, "text")
  • o tracing::info!(x = format!("{x}"), "text")

Only the first, third, and sixth (with o in front of them) are being considered by rustfmt. I am somewhat surprised by the first one since that does not seem like valid rust syntax either.

In case this is done by making exception for the tracing macros, these are the macros I know of where this syntax is permissible:

List of macros
  • span!
  • debug_span!
  • info_span!
  • warn_span!
  • error_span!
  • event!
  • debug!
  • info!
  • warn!
  • error!

@saffaffi
Copy link

I'm seeing this issue on rustfmt 1.7.0-nightly (f9b16149 2024-04-19), mainly with the ? sigil rather than the %. It seems like not only does the macro itself not get formatted, neither does any of the surrounding code.

@ytmimi
Copy link
Contributor

ytmimi commented Apr 20, 2024

@saffaffi the Issues with ? and % are the same. rustfmt can only format a macro calls if it's able to parse their arguments as valid rust syntax. The ?x in the macro call tracing::info!(?x) isn't valid rust.

You won't notice the impact of rustfmt failing to format tracing::info!(?x) because you probably already expect that's how it should be formatted, however, in other cases failing to format a long macro could lead to surrounding code being left unformatted.

It would be best for you to open a new issue and provide a minimal code example that demonstrates the problem you're currently experiencing.

@mkalvas
Copy link

mkalvas commented Mar 22, 2025

in other cases failing to format a long macro could lead to surrounding code being left unformatted.

I think I'm seeing this situation and wanted to add some info for others who may be searching for this like I was.

I have a tracing macro call that will cause surrounding formatting to fail whenever the line with the macro has len > 100. (I'm assuming this limit is tied to the max line length for rustfmt and changing that max would change the situation accordingly, but don't know that for a fact.)

fn do_thing() {
    // snip chain of calls ending in a `Result`
        .map_err(|err| {
            tracing::error!(err = %err, variable1 = ?variable1, variable2 = ?variable2, "========X");
            err.into()
        })
}

The formatting for everything inside of the function (e.g., do_thing) stops working. In this example the line is 101 characters long and doing anything to change that line to <= 100 makes the surrounding formatting work again. For example removing the X in the string or putting the arguments on new lines.

Other functions in this file are unaffected even when this function's formatting is broken. And as @ytmimi pointed out, the macro itself isn't formatted even when the rest of the surrounding formatting is working (which is understandable).

Here's my rustfmt version FWIW

$ rustfmt --version
rustfmt 1.8.0-stable (4eb161250e 2025-03-15)

@ytmimi
Copy link
Contributor

ytmimi commented Mar 22, 2025

@mkalvas the fact that the tracing macro fails to format is triggering #3863 in the example that you shared above (though it's not a complete reproducible example without the root of the chain).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants