Skip to content

Commit b327fc1

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 4b2ccc7 commit b327fc1

File tree

13 files changed

+251
-195
lines changed

13 files changed

+251
-195
lines changed

tracing-subscriber/src/filter/directive.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
use crate::filter::level::{self, LevelFilter};
2-
use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr};
2+
#[cfg(not(feature = "std"))]
3+
use alloc::{
4+
string::String,
5+
vec::{self, Vec},
6+
};
7+
use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr};
38
use tracing_core::Metadata;
49
/// Indicates that a string could not be parsed as a filtering directive.
510
#[derive(Debug)]
@@ -35,19 +40,21 @@ pub(in crate::filter) trait Match {
3540

3641
#[derive(Debug)]
3742
enum ParseErrorKind {
38-
Field(Box<dyn Error + Send + Sync>),
43+
#[cfg(feature = "std")]
44+
Field(Box<dyn std::error::Error + Send + Sync>),
3945
Level(level::ParseError),
4046
Other(Option<&'static str>),
4147
}
4248

4349
// === impl DirectiveSet ===
4450

4551
impl<T> DirectiveSet<T> {
52+
#[cfg(feature = "std")]
4653
pub(crate) fn is_empty(&self) -> bool {
4754
self.directives.is_empty()
4855
}
4956

50-
pub(crate) fn iter(&self) -> std::slice::Iter<'_, T> {
57+
pub(crate) fn iter(&self) -> slice::Iter<'_, T> {
5158
self.directives.iter()
5259
}
5360
}
@@ -118,7 +125,7 @@ impl<T> IntoIterator for DirectiveSet<T> {
118125
#[cfg(feature = "smallvec")]
119126
type IntoIter = smallvec::IntoIter<[T; 8]>;
120127
#[cfg(not(feature = "smallvec"))]
121-
type IntoIter = std::vec::IntoIter<T>;
128+
type IntoIter = vec::IntoIter<T>;
122129

123130
fn into_iter(self) -> Self::IntoIter {
124131
self.directives.into_iter()
@@ -353,6 +360,7 @@ impl FromStr for StaticDirective {
353360
// === impl ParseError ===
354361

355362
impl ParseError {
363+
#[cfg(feature = "std")]
356364
pub(crate) fn new() -> Self {
357365
ParseError {
358366
kind: ParseErrorKind::Other(None),
@@ -372,17 +380,19 @@ impl fmt::Display for ParseError {
372380
ParseErrorKind::Other(None) => f.pad("invalid filter directive"),
373381
ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg),
374382
ParseErrorKind::Level(ref l) => l.fmt(f),
383+
#[cfg(feature = "std")]
375384
ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e),
376385
}
377386
}
378387
}
379388

380-
impl Error for ParseError {
389+
#[cfg(feature = "std")]
390+
impl std::error::Error for ParseError {
381391
fn description(&self) -> &str {
382392
"invalid filter directive"
383393
}
384394

385-
fn source(&self) -> Option<&(dyn Error + 'static)> {
395+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
386396
match self.kind {
387397
ParseErrorKind::Other(_) => None,
388398
ParseErrorKind::Level(ref l) => Some(l),
@@ -391,8 +401,9 @@ impl Error for ParseError {
391401
}
392402
}
393403

394-
impl From<Box<dyn Error + Send + Sync>> for ParseError {
395-
fn from(e: Box<dyn Error + Send + Sync>) -> Self {
404+
#[cfg(feature = "std")]
405+
impl From<Box<dyn std::error::Error + Send + Sync>> for ParseError {
406+
fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
396407
Self {
397408
kind: ParseErrorKind::Field(e),
398409
}

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: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,44 @@
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_plf_stubs::*;
2429

25-
#[cfg(feature = "registry")]
26-
#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
27-
pub use self::subscriber_filters::*;
28-
2930
pub use self::level::{LevelFilter, ParseError as LevelParseError};
3031

31-
#[cfg(feature = "env-filter")]
32-
#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
33-
pub use self::env::*;
32+
#[cfg(not(all(feature = "registry", feature = "std")))]
33+
pub(crate) use self::has_plf_stubs::*;
34+
35+
feature! {
36+
#![any(feature = "std", feature = "alloc")]
37+
pub mod targets;
38+
pub use self::targets::Targets;
3439

35-
pub use self::targets::Targets;
40+
mod directive;
41+
pub use self::directive::ParseError;
42+
}
3643

3744
/// Stub implementations of the per-layer-fitler detection functions for when the
3845
/// `registry` feature is disabled.
39-
#[cfg(not(feature = "registry"))]
46+
#[cfg(not(all(feature = "registry", feature = "std")))]
4047
mod has_plf_stubs {
41-
pub(crate) fn is_plf_downcast_marker(_: std::any::TypeId) -> bool {
48+
pub(crate) fn is_plf_downcast_marker(_: core::any::TypeId) -> bool {
4249
false
4350
}
4451

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)