Fixes a security vulnerability where ANSI escape sequences in user input
could be injected into terminal output, potentially allowing attackers to
manipulate terminal behavior through log messages and error displays.
The vulnerability occurred when user-controlled content was formatted using
Display (`{}`) instead of Debug (`{:?}`) formatting, allowing raw ANSI
sequences to pass through unescaped.
Changes:
- Add streaming ANSI escape wrapper to avoid string allocations
- Escape message content in default and pretty formatters
- Escape error Display content in all error formatting paths
- Add comprehensive integration tests for all formatter types
The fix specifically targets untrusted user input while preserving the
ability for applications to deliberately include formatting in trusted
contexts like thread names.
Security impact: Prevents terminal injection attacks such as title bar
manipulation, screen clearing, and other malicious terminal control
sequences that could be injected through log messages.
This is useful when using `EnvFilter` for multiple identical per-layer
filters, as well as with clap and similar libraries that have `Clone`
bounds.
We generally expect users to be cloning an `EnvFilter` before attaching it
to a subscriber, rather than cloning `EnvFilters` that are already
attached. Because of this, we reset all the accumulated dynamic state
when cloning. This means that some spans and callsites might be missed
when an already-attached `EnvFilter` is cloned, but the presence of the
dynamic state mean that detaching and attaching `EnvFilter`s to existing
subscribers (e.g. with `reload`) already doesn't work very well. This
isn't a new class of problem.
There was a previous implementation of this in #2398, that shared the
dynamic state between all cloned filters behind an `Arc`. I chose
not do go for that approach because it causes inconsistencies if the
cloned filters are attached to different subscribers.
Fixes: #2360
* Implement PartialOrd for MatchPattern, MatchDebug in terms of Ord
* Fix missing link to supported regex syntax
* Update expected trybuild output
* Fix tracing_subscriber::Layer links
The change to allow the `dead_code` lint to work on the
`#[instrument]` attribute (#3108) introduced a shadowing
problem which caused compilation failure in certain cases,
as reported in #3306.
The body of the original function was not given its own scope
and `use` statements made there could leak out into the
code inserted by the `#[instrument]`.
This change fixes this problem by explicitly re-introducing the
parenthesis to give the original function body its own scope.
Closes#3306
## Motivation
The new `main` branch is forked from the `v0.1.x` branch. It will be
made the default branch and going forward we will merge PRs to this
branch first (and then forward port to a new `v0.2.x` branch forked from
`master`).
It looks like Netlify jobs weren't running on the `v0.1.x` branch, and so
there were quite a few errors in the docs on that branch (which isn't great
because those are the ones that get published to docs.rs).
## Solution
Separate to this PR, we've enabled Netlify on the `main` branch, and this
change fixes all the errors that were present in the docs.
This change sets the GitHub actions to run on the `main` branch instead
of `v0.1.x`. It also adds some text in the root README.md which
describes the branch set-up.
Refs: #3294
## Motivation
In the case of an event with a name and a parent, the change in #2083
to use a free function instead of a method for `is_enabled` was not
applied. This particular variant was also not covered by any tests,
which is how this error slipped through CI.
## Solution
This change fixes the `is_enabled` call and adds additional test
coverage for this macros case.
Co-authored-by: Hayden Stainsby <hds@caffeineconcepts.com>
## Motivation
While configuring tracing-appender, I wanted to specify a weekly log
rotation interval. I was unable to do so, as the largest rotation
interval was daily.
## Solution
Before my introduction of weekly log rotation, rounding the current
`OffsetDateTime` was straightforward: we could simply keep the current
date and truncate part or all of the time component. However, we cannot
simply truncate the time with weekly rotation; the date must now be
modified.
To round the date, we roll logs at 00:00 UTC on Sunday. This gives us
consistent date-times that only change weekly.
`self` and `other` are references, and the `ptr::eq()` call intends to
determine if they designate the same object. Putting them behind another
level of reference will always return `false`, as those short-lived
references will be compared instead.
It can be useful to have a TestWriter that does not log to stdout but
stderr instead. For example, that allows for potentially easier
filtering of tracing output (because the remaining output of, say, cargo
test goes to stdout) or to mirror behavior of env_logger, which by
default logs to stderr.
Introduce the TestWriter::with_stderr() constructor to enable such
usage. The default is left unchanged.
Co-authored-by: David Barsky <me@davidbarsky.com>
This example demonstrates how to use the `tracing-subscriber` crate's
`EnvFilter` type to filter log messages based on their metadata. The
example provides a text area where users can input an environment filter
string, and displays the log messages that would be captured by that
filter.
There is a report in #3174 that even in release mode, building the regex
used to parse `EnvFilter` directives can take a relatively large amount
of time (600us).
This change replaces the `regex` based parsing of the directives with a
state machine implementation that is faster and also easier to reason
about.
Fixes: #3174
## Motivation
Currently, Span.record_all() is part of the public API and accepts
ValueSet as a parameter. However, constructing a ValueSet is both
verbose and undocumented, making it not so practical.
## Solution
To make recording multiple values easier, we introduce a new macro:
record_all!, which wraps the Span.record_all() function.
As we don't intend anyone to call Span.record_all() directly, we hide
it from the documentation. We reference the new macro from Span.record()
doc comment instead.
The new record_all! macro supports optional formatting sigils % and ?,
ensuring a consistent DevEx with the other value-recording macros.
Co-authored-by: Hayden Stainsby <hds@caffeineconcepts.com>
## Motivation
The current behaviour of `DefaultVisitor` is that it will write
padding even if it is going to skip writing a value, which results
in extraneous padding being added when values are skipped
by the `tracing-log` integration.
## Solution
With this change, `DefaultVisitor` will only insert padding if it is
actually going to write a value.
Closes: #2979
@jonhoo recorded a great resource about the crate's inner
workings and included practical suggestions about patterns
to follow when annotating one's code.
I added the link to the YouTube video under the "Talks" header
as that seemed appropriate enough.
## Motivation
I was checking the example from `/examples/` and saw that
some of the module names were incorrect in README, so I
fixed them.
## Solution
Changed the incorrect name to the correct name.
We had some broken link formatting in the `tracing-journald` docs which
clippy picked up (the text looked like a link definition, but wasn't
meant to be).
The incorrect links have now been corrected. They have to link to the
`tracing-core` crate because `tracing-journald` doesn't depend on
`tracing` directly.
Fixes for a broken link in the `tracing-subscriber` main page and
correcting the link to `Collect` from `tracing-log` (which also doesn't
depend on `tracing` directly) were also included.
The change #2954 was released in 0.3.19 (#3162).
Notably, it relied on features from tracing-core 0.1.33, however, the
version was never bumped. Users of the `tracing` feature of
`tracing-subscriber` would have no issue since it pulls in the higher
version transitively.
The specific feature used was implementing trait method `record_bytes`
from the `field::Visit` trait on `JsonVisitor` from the
tracing-subscriber json format module. (see linked #2945, or [ `impl
field::Visit for
JsonVisitor<'_>`](https://github.com/tokio-rs/tracing/blame/master/tracing-subscriber/src/fmt/format/json.rs#L491))
I believe this dependency mismatch requires users to manually select the
higher tracing-core version or suffer compilation failure.
This probably reflects some failure in how the tests utilize features
and intra-workspace dependencies, but, a resolution for that is beyond
my current comprehension of the project.
There was only a single case of the new `needless_as_bytes` lint which
was triggered and needed to be fixed.
There was also a "UI" test in `tracing-attributes` that needed to be
updated because the error text has changed (it gives more details of
course).
Most of these changes are places where lifetimes were named, but can be
elided. Then a few cases where a lifetime was elided, but actually
resolves to a named lifetime. So lots of lifetimes.
This is the `v0.1.x` branch sister PR to #3164 (for the `master`
branch), since `clippy --fix` on another branch is a much better way to
apply these changes than backporting.
The commit 1cb523b87d3d removed this cfg gate on master. However, when
the change was backported in 1cb523b87d3d the docs were updated but the
cfg change was omitted.
This made the docs misleading, since they say "This method itself is
still available without the feature flag."
There are 2 triggers which will cause a subscriber to receive a call to
`Subscriber::register_callsite` for a specific callsite.
1. The first time the event or span at that callsite is executed.
2. When a new subscriber is added or removed (for example, calls to
`set_default` or `with_default`)
It is trigger (2) that will cause a new subscriber to receive
`Subscriber::register_callsite` for all the callsites which had already
been registered before it became active.
When a callsite is registered for trigger (1), the callsite starts in
state `UNREGISTERED`.
The first thread to encounter the callsite will transition it to
`REGISTERING` and determine the overall interest for the callsite by
registering with all known dispatchers (which will call into
`Subscriber::register_callsite`).
Once that is complete, the callsite is added to the list of all known
callsites and its state is transitioned to `REGISTERED`.
is (re)built for all known dispatchers. The callsite starts in state
`UNREGISTERED`. The This calls down into
`Subscriber::register_callsite` for each subscriber. Once that is
complete, the callsite is added to the global list of known callsites.
While the callsite interest is being rebuilt, other threads that
encounter the callsite will be given `Interest::sometimes()` until the
registration is complete. However, if a new subscriber is added during
this window, all the interest for all callsites will be rebuilt, but
because the new callsite (in state `REGISTERING`) won't be included
because it isn't yet in the global list of callsites.
This can cause a case where that new subscriber being added won't
receive `Subscriber::register_callsite` before it receives the subsequent
call to `Subscriber::event` or `Subscriber::new_span`.
The documentation on [Registering Callsites] is not very explicit on
this point, but it does suggest that `Subscriber::register_callsite`
will be called before the call to either `Subscriber::event` or
`Subscriber::new_span`, and the current behavior can break this implicit
contract.
[Registering Callsites]: https://docs.rs/tracing-core/0.1.32/tracing_core/callsite/index.html#registering-callsites
This change swaps the order of rebuilding the callsite interest and
adding the callsite to the global list so that the callsite gets pushed
first, avoiding this window in which a subscriber won't get a call to
`register_callsite`.
As such, a callsite may have its interest read before it is set. In this
case, the existing implementation will return `Interest::sometimes()`
for the `DefaultCallsite` implementation. Other implementations (outside
of the `tracing` project) may perform this differently, but in this
case, there is no documented guarantee regarding the ordering.
A regression test is included which provokes the race condition 100% of
the time before the changes in this fix.
Fixes: #2743
Co-authored-by: David Barsky <me@davidbarsky.com>
This modifies the `tracing_subscriber::reload` layer to also set the
`log` crate's max level with the current max `tracing` level filter
after reloading. If reloading the subscriber caused the max `tracing`
level to change, this ensures that the change is propagated to the `log`
crate as well. In the case where the max level was made more verbose,
this will ensure that `log` records which were previously disabled are
enabled correctly; in the case where it was made less verbose, this
improve performance by not having to perfrom more costly filtering for
those `log` records.
The `log` max level is set only after rebuilding the callsite interest
cache, which is what sets the max `tracing` level filter. This ensures
that we pass the latest state to the `log` crate.
Signed-off-by: Eliza Weisman <eliza@buoyant.io>