Skip to content

Commit 77ddb0a

Browse files
committed
subscriber: add minimal #![no_std] support (#1648)
Backports #1648 from `master`. Depends on #1649 ## Motivation Presently, the `tracing-subscriber` crate requires the Rust standard library and doesn't build with `#![no_std]` targets. For the most part, this is fine, as much of `tracing-subscriber` inherently depends on `std` APIs. However, `tracing-subscriber` also contains some key abstractions that are necessary for interoperability: the `Layer` and `LookupSpan` traits. Since these traits are in `tracing-subscriber`, `no-std` users cannot currently access them. Some of the other utilities in this crate, such as the field visitor combinators, may also be useful for `#![no_std]` projects. ## Solution This branch adds "std" and "alloc" feature flags to `tracing-subscriber`, for conditionally enabling `libstd` and `liballoc`, respectively. The `registry`, `fmt`, `EnvFilter`, and `reload` APIs all require libstd, and cannot be implemented without it, but the core `Layer` and `LookupSpan` traits are now available with `#![no_std]`. Fixes #999
1 parent 0d01569 commit 77ddb0a

File tree

13 files changed

+279
-193
lines changed

13 files changed

+279
-193
lines changed

tracing-subscriber/src/filter/directive.rs

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::filter::level::{self, LevelFilter};
2-
use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr};
3-
use tracing_core::Metadata;
2+
use alloc::{string::String, vec::Vec};
3+
use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr};
4+
use tracing_core::{Level, Metadata};
45
/// Indicates that a string could not be parsed as a filtering directive.
56
#[derive(Debug)]
67
pub struct ParseError {
@@ -35,19 +36,21 @@ pub(in crate::filter) trait Match {
3536

3637
#[derive(Debug)]
3738
enum ParseErrorKind {
38-
Field(Box<dyn Error + Send + Sync>),
39+
#[cfg(feature = "std")]
40+
Field(Box<dyn std::error::Error + Send + Sync>),
3941
Level(level::ParseError),
4042
Other(Option<&'static str>),
4143
}
4244

4345
// === impl DirectiveSet ===
4446

4547
impl<T> DirectiveSet<T> {
48+
#[cfg(feature = "std")]
4649
pub(crate) fn is_empty(&self) -> bool {
4750
self.directives.is_empty()
4851
}
4952

50-
pub(crate) fn iter(&self) -> std::slice::Iter<'_, T> {
53+
pub(crate) fn iter(&self) -> slice::Iter<'_, T> {
5154
self.directives.iter()
5255
}
5356
}
@@ -118,7 +121,7 @@ impl<T> IntoIterator for DirectiveSet<T> {
118121
#[cfg(feature = "smallvec")]
119122
type IntoIter = smallvec::IntoIter<[T; 8]>;
120123
#[cfg(not(feature = "smallvec"))]
121-
type IntoIter = std::vec::IntoIter<T>;
124+
type IntoIter = alloc::vec::IntoIter<T>;
122125

123126
fn into_iter(self) -> Self::IntoIter {
124127
self.directives.into_iter()
@@ -135,6 +138,22 @@ impl DirectiveSet<StaticDirective> {
135138
None => false,
136139
}
137140
}
141+
142+
/// Same as `enabled` above, but skips `Directive`'s with fields.
143+
pub(crate) fn target_enabled(&self, target: &str, level: &Level) -> bool {
144+
match self.directives_for_target(target).next() {
145+
Some(d) => d.level >= *level,
146+
None => false,
147+
}
148+
}
149+
150+
pub(crate) fn directives_for_target<'a>(
151+
&'a self,
152+
target: &'a str,
153+
) -> impl Iterator<Item = &'a StaticDirective> + 'a {
154+
self.directives()
155+
.filter(move |d| d.cares_about_target(target))
156+
}
138157
}
139158

140159
// === impl StaticDirective ===
@@ -151,6 +170,22 @@ impl StaticDirective {
151170
level,
152171
}
153172
}
173+
174+
pub(in crate::filter) fn cares_about_target(&self, to_check: &str) -> bool {
175+
// Does this directive have a target filter, and does it match the
176+
// metadata's target?
177+
if let Some(ref target) = self.target {
178+
if !to_check.starts_with(&target[..]) {
179+
return false;
180+
}
181+
}
182+
183+
if !self.field_names.is_empty() {
184+
return false;
185+
}
186+
187+
true
188+
}
154189
}
155190

156191
impl Ord for StaticDirective {
@@ -353,6 +388,7 @@ impl FromStr for StaticDirective {
353388
// === impl ParseError ===
354389

355390
impl ParseError {
391+
#[cfg(feature = "std")]
356392
pub(crate) fn new() -> Self {
357393
ParseError {
358394
kind: ParseErrorKind::Other(None),
@@ -372,17 +408,19 @@ impl fmt::Display for ParseError {
372408
ParseErrorKind::Other(None) => f.pad("invalid filter directive"),
373409
ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg),
374410
ParseErrorKind::Level(ref l) => l.fmt(f),
411+
#[cfg(feature = "std")]
375412
ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e),
376413
}
377414
}
378415
}
379416

380-
impl Error for ParseError {
417+
#[cfg(feature = "std")]
418+
impl std::error::Error for ParseError {
381419
fn description(&self) -> &str {
382420
"invalid filter directive"
383421
}
384422

385-
fn source(&self) -> Option<&(dyn Error + 'static)> {
423+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
386424
match self.kind {
387425
ParseErrorKind::Other(_) => None,
388426
ParseErrorKind::Level(ref l) => Some(l),
@@ -391,8 +429,9 @@ impl Error for ParseError {
391429
}
392430
}
393431

394-
impl From<Box<dyn Error + Send + Sync>> for ParseError {
395-
fn from(e: Box<dyn Error + Send + Sync>) -> Self {
432+
#[cfg(feature = "std")]
433+
impl From<Box<dyn std::error::Error + Send + Sync>> for ParseError {
434+
fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
396435
Self {
397436
kind: ParseErrorKind::Field(e),
398437
}

tracing-subscriber/src/filter/env/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ use tracing_core::{
102102
/// [`Metadata`]: tracing_core::Metadata
103103
/// [`Targets`]: crate::filter::Targets
104104
#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))]
105-
#[cfg(feature = "env-filter")]
106105
#[derive(Debug)]
107106
pub struct EnvFilter {
108107
statics: directive::Statics,

tracing-subscriber/src/filter/filter_fn.rs

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#[cfg(feature = "registry")]
2-
use crate::subscribe::Filter;
31
use crate::{
42
filter::LevelFilter,
53
subscribe::{Context, Subscribe},
@@ -323,25 +321,6 @@ where
323321
}
324322
}
325323

326-
#[cfg(feature = "registry")]
327-
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
328-
impl<C, F> Filter<C> for FilterFn<F>
329-
where
330-
F: Fn(&Metadata<'_>) -> bool,
331-
{
332-
fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, C>) -> bool {
333-
self.is_enabled(metadata)
334-
}
335-
336-
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
337-
self.is_callsite_enabled(metadata)
338-
}
339-
340-
fn max_level_hint(&self) -> Option<LevelFilter> {
341-
self.max_level_hint
342-
}
343-
}
344-
345324
impl<C, F> Subscribe<C> for FilterFn<F>
346325
where
347326
F: Fn(&Metadata<'_>) -> bool + 'static,
@@ -661,26 +640,6 @@ where
661640
}
662641
}
663642

664-
#[cfg(feature = "registry")]
665-
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
666-
impl<C, F, R> Filter<C> for DynFilterFn<C, F, R>
667-
where
668-
F: Fn(&Metadata<'_>, &Context<'_, C>) -> bool,
669-
R: Fn(&'static Metadata<'static>) -> Interest,
670-
{
671-
fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, C>) -> bool {
672-
self.is_enabled(metadata, cx)
673-
}
674-
675-
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
676-
self.is_callsite_enabled(metadata)
677-
}
678-
679-
fn max_level_hint(&self) -> Option<LevelFilter> {
680-
self.max_level_hint
681-
}
682-
}
683-
684643
impl<C, F, R> Subscribe<C> for DynFilterFn<C, F, R>
685644
where
686645
F: Fn(&Metadata<'_>, &Context<'_, C>) -> bool + 'static,
@@ -741,6 +700,48 @@ where
741700
}
742701
}
743702

703+
// === PLF impls ===
704+
705+
feature! {
706+
#![all(feature = "registry", feature = "std")]
707+
use crate::subscribe::Filter;
708+
709+
impl<C, F> Filter<C> for FilterFn<F>
710+
where
711+
F: Fn(&Metadata<'_>) -> bool,
712+
{
713+
fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, C>) -> bool {
714+
self.is_enabled(metadata)
715+
}
716+
717+
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
718+
self.is_callsite_enabled(metadata)
719+
}
720+
721+
fn max_level_hint(&self) -> Option<LevelFilter> {
722+
self.max_level_hint
723+
}
724+
}
725+
726+
impl<C, F, R> Filter<C> for DynFilterFn<C, F, R>
727+
where
728+
F: Fn(&Metadata<'_>, &Context<'_, C>) -> bool,
729+
R: Fn(&'static Metadata<'static>) -> Interest,
730+
{
731+
fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, C>) -> bool {
732+
self.is_enabled(metadata, cx)
733+
}
734+
735+
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
736+
self.is_callsite_enabled(metadata)
737+
}
738+
739+
fn max_level_hint(&self) -> Option<LevelFilter> {
740+
self.max_level_hint
741+
}
742+
}
743+
}
744+
744745
fn is_below_max_level(hint: &Option<LevelFilter>, metadata: &Metadata<'_>) -> bool {
745746
hint.as_ref()
746747
.map(|hint| metadata.level() <= hint)

tracing-subscriber/src/filter/mod.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,42 @@
88
//!
99
//! [`subscribe` module documentation]: crate::subscribe#filtering-with-subscribers
1010
//! [`Subscribe`]: crate::subscribe
11-
mod directive;
12-
#[cfg(feature = "env-filter")]
13-
mod env;
1411
mod filter_fn;
1512
mod level;
16-
#[cfg(feature = "registry")]
17-
mod subscriber_filters;
18-
pub mod targets;
1913

20-
pub use self::directive::ParseError;
14+
feature! {
15+
#![all(feature = "env-filter", feature = "std")]
16+
mod env;
17+
pub use self::env::*;
18+
}
19+
20+
feature! {
21+
#![all(feature = "registry", feature = "std")]
22+
mod subscriber_filters;
23+
pub use self::subscriber_filters::*;
24+
}
25+
2126
pub use self::filter_fn::*;
2227
#[cfg(not(feature = "registry"))]
2328
pub(crate) use self::has_psf_stubs::*;
24-
#[cfg(feature = "registry")]
25-
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
26-
pub use self::subscriber_filters::*;
2729

2830
pub use self::level::{LevelFilter, ParseError as LevelParseError};
2931

30-
#[cfg(feature = "env-filter")]
31-
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
32-
pub use self::env::*;
32+
#[cfg(not(all(feature = "registry", feature = "std")))]
33+
pub(crate) use self::has_plf_stubs::*;
3334

34-
pub use self::targets::Targets;
35+
feature! {
36+
#![any(feature = "std", feature = "alloc")]
37+
pub mod targets;
38+
pub use self::targets::Targets;
39+
40+
mod directive;
41+
pub use self::directive::ParseError;
42+
}
3543

3644
/// Stub implementations of the per-subscriber-fitler detection functions for
3745
/// when the `registry` feature is disabled.
38-
#[cfg(not(feature = "registry"))]
46+
#[cfg(not(all(feature = "registry", feature = "std")))]
3947
mod has_psf_stubs {
4048
pub(crate) fn is_psf_downcast_marker(_: std::any::TypeId) -> bool {
4149
false

tracing-subscriber/src/filter/targets.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ use crate::{
1313
},
1414
subscribe,
1515
};
16-
use std::{
16+
#[cfg(not(feature = "std"))]
17+
use alloc::string::String;
18+
use core::{
1719
iter::{Extend, FilterMap, FromIterator},
20+
slice,
1821
str::FromStr,
1922
};
2023
use tracing_core::{Collect, Interest, Metadata};
@@ -462,7 +465,7 @@ impl Iterator for IntoIter {
462465
#[derive(Debug)]
463466
pub struct Iter<'a>(
464467
FilterMap<
465-
std::slice::Iter<'a, StaticDirective>,
468+
slice::Iter<'a, StaticDirective>,
466469
fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>,
467470
>,
468471
);
@@ -494,6 +497,17 @@ impl<'a> Iterator for Iter<'a> {
494497
mod tests {
495498
use super::*;
496499

500+
feature! {
501+
#![not(feature = "std")]
502+
use alloc::{vec, vec::Vec, string::ToString};
503+
504+
// `dbg!` is only available with `libstd`; just nop it out when testing
505+
// with alloc only.
506+
macro_rules! dbg {
507+
($x:expr) => { $x }
508+
}
509+
}
510+
497511
fn expect_parse(s: &str) -> Targets {
498512
match dbg!(s).parse::<Targets>() {
499513
Err(e) => panic!("string {:?} did not parse successfully: {}", s, e),
@@ -643,6 +657,8 @@ mod tests {
643657
}
644658

645659
#[test]
660+
// `println!` is only available with `libstd`.
661+
#[cfg(feature = "std")]
646662
fn size_of_filters() {
647663
fn print_sz(s: &str) {
648664
let filter = s.parse::<Targets>().expect("filter should parse");

tracing-subscriber/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
//! [`time` crate]: https://crates.io/crates/time
130130
//! [`liballoc`]: https://doc.rust-lang.org/alloc/index.html
131131
//! [`libstd`]: https://doc.rust-lang.org/std/index.html
132-
#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.2.12")]
132+
#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.2.25")]
133133
#![doc(
134134
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
135135
html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico",

0 commit comments

Comments
 (0)