1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//! # TraceLoggingDynamic for Rust
//!
//! `tracelogging_dynamic` provides a flexible way to log
//! [TraceLogging](https://docs.microsoft.com/windows/win32/tracelogging/trace-logging-portal)
//! (manifest-free) events to
//! [ETW](https://docs.microsoft.com/windows/win32/etw/event-tracing-portal)
//! (Event Tracing for Windows). The events can be generated and collected on Windows
//! Vista or later. The events can be decoded on Windows 10 or later.
//!
//! This "dynamic" implementation of TraceLogging supports more functionality than the
//! implementation in the [`tracelogging`] crate. For example, it supports
//! runtime-defined schema and can easily log arrays of strings. However, it is harder to
//! use, has higher runtime costs, and has additional runtime dependencies than the
//! `tracelogging` crate. This dynamic implementation is intended for use only when the
//! set of events cannot be determined at compile-time. For example,
//! `traceloggingdynamic` might be used to implement a middle-layer library providing
//! ETW support to a scripting language like JavaScript or Perl.
//!
//! # Overview
//!
//! - Create a pinned [Provider] object, e.g.
//!
//! `let mut provider = Box::pin(Provider::new());`
//!
//! - See the documentation for the [Provider] class for ways to pin the provider
//! without a heap allocation.
//!
//! - Call [Provider::register] to open the connection to ETW, e.g.
//!
//! `unsafe { provider.as_mut().register(provider_name, options); }`
//!
//! - Safety: [Provider::register] is unsafe because a registered provider **must** be
//! properly unregistered. This happens automatically when the provider is dropped,
//! so you usually don't need to worry about this, but it can be an issue in cases
//! where dropping does not occur such as with static variables.
//!
//! - As needed, use an [EventBuilder] to construct and write events.
//!
//! - The provider will automatically unregister when it is dropped. You can manually call
//! [Provider::unregister] if you want to unregister sooner or if the provider is a
//! static variable.
//!
//! # Example
//!
//! ```
//! use tracelogging_dynamic as tld;
//!
//! // Pinning is required because the register() method sets up a callback with ETW.
//! let mut provider = Box::pin(tld::Provider::new());
//!
//! // Register the provider with name "MyCompany.MyComponent". If you don't register (or
//! // if register fails) then enabled() will always return false and write() will be a
//! // no-op.
//! unsafe {
//! provider.as_mut().register("MyCompany.MyComponent", &tld::Provider::options());
//! }
//!
//! // If provider is not enabled for a given level + keyword, the write() call will do
//! // nothing. Check enabled(level, keyword) before building the event so we don't waste
//! // time on an event that nobody will receive.
//! let my_event_level = tld::Level::Verbose; // Severity level.
//! let my_event_keyword = 0x123; // User-defined category bits.
//! if provider.enabled(my_event_level, my_event_keyword) {
//! let field1_value = "FieldValue";
//! let field2_value = b'A';
//! // Create and write an event with two fields:
//! tld::EventBuilder::new()
//! // Most events specify 0 for event tag.
//! .reset("MyEventName", my_event_level, my_event_keyword, 0)
//! // Most fields use Default for event format and 0 for field tag.
//! .add_str8("FieldName1", field1_value, tld::OutType::Default, 0)
//! .add_u8("FieldName2", field2_value, tld::OutType::String, 0)
//! // If activity_id is None, event uses the current thread's activity.
//! // If related_id is None, event will not have a related activity.
//! .write(&provider, None, None);
//! }
//! ```
//!
//! # Notes
//!
//! The [EventBuilder] object is reusable. You may get a small performance benefit by
//! reusing an EventBuilder object for multiple events rather than using a new
//! EventBuilder for each event.
//!
//! ETW events are limited in size (event size = headers + metadata + data). Windows will
//! ignore any event that is larger than 64KB and will ignore any event that is larger
//! than the buffer size of the recording session.
//!
//! Most ETW decoding tools are unable to decode an event with more than 128 fields.
//!
//! Collect the events using Windows SDK tools like
//! [traceview](https://docs.microsoft.com/windows-hardware/drivers/devtest/traceview) or
//! [tracelog](https://docs.microsoft.com/windows-hardware/drivers/devtest/tracelog).
//! Decode the events using Windows SDK tools like
//! [traceview](https://docs.microsoft.com/windows-hardware/drivers/devtest/traceview) or
//! [tracefmt](https://docs.microsoft.com/windows-hardware/drivers/devtest/tracefmt).
//! For example, to collect events from a provider that was registered as
//! `provider.register("MyCompany.MyComponent", ...)`:
//!
//! ```text
//! tracelog -start MyTrace -f MyTraceFile.etl -guid *MyCompany.MyComponent -level 5 -matchanykw 0xf
//! <run your program>
//! tracelog -stop MyTrace
//! tracefmt -o MyTraceData.txt MyTraceFile.etl
//! ```
// Re-exports from tracelogging:
pub use Channel;
pub use Guid;
pub use InType;
pub use Level;
pub use NativeImplementation;
pub use Opcode;
pub use OutType;
pub use ProviderEnableCallback;
pub use NATIVE_IMPLEMENTATION;
// Exports from tracelogging_dynamic:
pub use EventBuilder;
pub use Provider;
pub use ProviderOptions;
extern crate alloc;