diff --git a/Cargo.toml b/Cargo.toml index 0872a64..87c02b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,3 +48,7 @@ harness = false [[bench]] name = "user_events" harness = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/benches/etw.rs b/benches/etw.rs index 73a028d..8d3f832 100644 --- a/benches/etw.rs +++ b/benches/etw.rs @@ -42,7 +42,7 @@ pub fn etw_benchmark(c: &mut Criterion) { } session - .enable_provider(&provider_id.into(), 0xFF) + .enable_provider(&provider_id.to_u128().into(), 0xFF) .expect("can't enable provider to session"); // Spans diff --git a/src/_details.rs b/src/_details.rs index d430c93..0d0b295 100644 --- a/src/_details.rs +++ b/src/_details.rs @@ -28,7 +28,7 @@ where // This struct needs to be public as it implements the tracing_subscriber::Layer::Filter trait. #[doc(hidden)] -#[cfg(not(feature = "global_filter"))] +#[cfg(any(not(feature = "global_filter"), docsrs))] pub struct EtwFilter where Mode::Provider: crate::native::EventWriter + 'static diff --git a/src/layer.rs b/src/layer.rs index a661c8c..8d56522 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -1,5 +1,6 @@ use std::marker::PhantomData; use std::time::SystemTime; +#[allow(unused_imports)] use std::{pin::Pin, sync::Arc}; #[allow(unused_imports)] // Many imports are used exclusively by feature-gated code @@ -11,12 +12,29 @@ use tracing_subscriber::filter::{combinator::And, FilterExt, Filtered, Targets}; use tracing_subscriber::layer::Filter; use tracing_subscriber::{registry::LookupSpan, Layer}; -use crate::_details::{EtwFilter, EtwLayer}; +#[cfg(any(not(feature = "global_filter"), docsrs))] +use crate::_details::EtwFilter; +use crate::_details::EtwLayer; use crate::native::{EventWriter, GuidWrapper, ProviderTypes}; use crate::native; use crate::values::*; use crate::statics::*; +/// Builds a [tracing_subscriber::Layer] that will logs events from a single +/// ETW or user_events provider. Use [LayerBuilder::new] to construct a new +/// builder for the given provider name. Use the `with_*` methods to set +/// additional properties for the provider, such as the keyword to use +/// for events (default: 1) or a specific provider GUID (default: a hash of +/// the provider name). +/// +/// Use [LayerBuilder::new_common_schema_events] to create a layer that +/// will log events in the Common Schema 4.0 mapping. Only use this if +/// you know that you need events in this format. +/// +/// Multiple `tracing_etw` layers can be created at the same time, +/// with different provider names/IDs, keywords, or output formats. +/// (Target filters)[tracing_subscriber::filter] can then be used to direct +/// specific events to specific layers. pub struct LayerBuilder where Mode: ProviderTypes @@ -48,7 +66,7 @@ impl LayerBuilder { /// Most ETW consumers will not benefit from events in this schema, and /// may perform worse. Common Schema events are much slower to generate /// and should not be enabled unless absolutely necessary. - #[cfg(feature = "common_schema")] + #[cfg(any(feature = "common_schema", docsrs))] pub fn new_common_schema_events( name: &str, ) -> LayerBuilder { @@ -80,10 +98,21 @@ where /// Get the current provider ID that will be used for the ETW provider. /// This is a convenience function to help with tools that do not implement /// the standard provider name to ID algorithm. - pub fn get_provider_id(&self) -> &GuidWrapper { - &self.provider_id + pub fn get_provider_id(&self) -> GuidWrapper { + self.provider_id.clone() } + + /// Set the keyword used for events that do not explicitly set a keyword. + /// + /// Events logged with the [crate::etw_event!] macro specify a keyword for the event. + /// Events and spans logged with the [tracing::event!], [tracing::span!], + /// or other similar `tracing` macros will use the default keyword. + /// + /// If this method is not called, the default keyword will be `1`. + /// + /// Keyword value `0` is special in ETW (but not user_events), and should + /// not be used. pub fn with_default_keyword(mut self, kw: u64) -> Self { self.default_keyword = kw; self @@ -116,7 +145,7 @@ where } } - #[cfg(not(feature = "global_filter"))] + #[cfg(any(not(feature = "global_filter"), docsrs))] fn build_target_filter(&self, target: &'static str) -> Targets { let mut targets = Targets::new().with_target(&self.provider_name, LevelFilter::TRACE); @@ -166,41 +195,45 @@ where } } - #[allow(clippy::type_complexity)] - #[cfg(not(feature = "global_filter"))] - pub fn build_with_target( - self, - target: &'static str, - ) -> Filtered, And, Targets, S>, S> + #[cfg_attr(docsrs, doc(cfg(feature = "global_filter")))] + #[cfg(any(feature = "global_filter", docsrs))] + pub fn build(self) -> EtwLayer where S: Subscriber + for<'a> LookupSpan<'a>, Mode::Provider: EventWriter + 'static, { self.validate_config(); - let layer = self.build_layer(); - - let filter = self.build_filter(layer.provider.clone()); - - let targets = self.build_target_filter(target); - - layer.with_filter(filter.and(targets)) + self.build_layer() } - #[cfg(feature = "global_filter")] - pub fn build(self) -> EtwLayer + #[allow(clippy::type_complexity)] + #[cfg_attr(docsrs, doc(cfg(not(feature = "global_filter"))))] + #[cfg(any(not(feature = "global_filter"), docsrs))] + pub fn build(self) -> Filtered, EtwFilter, S> where S: Subscriber + for<'a> LookupSpan<'a>, - Mode::Provider: EventWriter + 'static, + Mode::Provider: EventWriter + 'static, { self.validate_config(); - self.build_layer() + let layer = self.build_layer(); + + let filter = self.build_filter(layer.provider.clone()); + + layer.with_filter(filter) } + /// Constructs the configured layer with a target [tracing_subscriber::filter] applied. + /// This can be used to target specific events to specific layers, and in effect allow + /// specific events to be logged only from specific ETW/user_event providers. #[allow(clippy::type_complexity)] - #[cfg(not(feature = "global_filter"))] - pub fn build(self) -> Filtered, EtwFilter, S> + #[cfg_attr(docsrs, doc(cfg(not(feature = "global_filter"))))] + #[cfg(any(not(feature = "global_filter"), docsrs))] + pub fn build_with_target( + self, + target: &'static str, + ) -> Filtered, And, Targets, S>, S> where S: Subscriber + for<'a> LookupSpan<'a>, Mode::Provider: EventWriter + 'static, @@ -211,11 +244,13 @@ where let filter = self.build_filter(layer.provider.clone()); - layer.with_filter(filter) + let targets = self.build_target_filter(target); + + layer.with_filter(filter.and(targets)) } } -#[cfg(not(feature = "global_filter"))] +#[cfg(any(not(feature = "global_filter"), docsrs))] impl Filter for EtwFilter where S: Subscriber + for<'a> LookupSpan<'a>, @@ -300,7 +335,7 @@ where // Late init when the layer is attached to a subscriber } - #[cfg(feature = "global_filter")] + #[cfg(any(feature = "global_filter", docsrs))] fn register_callsite( &self, metadata: &'static tracing::Metadata<'static>, @@ -312,7 +347,7 @@ where self.default_keyword }; - if P::supports_enable_callback() { + if Mode::supports_enable_callback() { if self.provider.enabled(metadata.level(), keyword) { tracing::subscriber::Interest::always() } else { @@ -325,7 +360,7 @@ where } } - #[cfg(feature = "global_filter")] + #[cfg(any(feature = "global_filter", docsrs))] fn enabled( &self, metadata: &tracing::Metadata<'_>, @@ -341,7 +376,7 @@ where self.provider.enabled(metadata.level(), keyword) } - #[cfg(feature = "global_filter")] + #[cfg(any(feature = "global_filter", docsrs))] fn event_enabled( &self, event: &tracing::Event<'_>, diff --git a/src/lib.rs b/src/lib.rs index 77737d3..78814da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ //! Keywords are a fundamental part of efficient event filtering in ETW, //! and naming events make them easier to understand in tools like WPA. //! It is highly recommended that every event have a non-zero keyword; -//! the [default_keyword] function can set the default keyword assigned +//! the [LayerBuilder::with_default_keyword] function can set the default keyword assigned //! to every event logged through the `tracing` macros (e.g. `event!`). //! //! This extra information is stored as static metadata in the final @@ -71,6 +71,10 @@ //! It has been tested with Microsoft's, GCC's, and LLVM's linker. //! +// only enables the `doc_cfg` feature when +// the `docsrs` configuration attribute is defined +#![cfg_attr(docsrs, feature(doc_cfg))] + mod layer; // Module that abstracts the native ETW and Linux user_events APIs, depending on the target platform. // Consumers of the crate should not need to use this module directly. diff --git a/src/native/mod.rs b/src/native/mod.rs index a5fccdb..e36a239 100644 --- a/src/native/mod.rs +++ b/src/native/mod.rs @@ -36,6 +36,7 @@ pub(crate) use tracelogging_dynamic::Guid as native_guid; pub(crate) use eventheader::Guid as native_guid; #[doc(hidden)] +#[derive(Copy, Clone)] pub struct GuidWrapper(u128); impl From<&native_guid> for GuidWrapper { @@ -68,10 +69,20 @@ impl From<&GuidWrapper> for u128 { } } +impl AsRef for GuidWrapper { + fn as_ref(&self) -> &u128 { + &self.0 + } +} + impl GuidWrapper { pub fn from_name(name: &str) -> Self { Self(native_guid::from_name(name).to_u128()) } + + pub fn to_u128(&self) -> u128 { + self.0 + } } #[doc(hidden)]