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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
//! # A [tracing](https://crates.io/crates/tracing) layer for ETW and Linux user_events
//!
//! ## Overview
//!
//! This layer emits tracing events as Windows ETW events or Linux user-mode tracepoints
//! (user_events with the [EventHeader](https://github.com/microsoft/LinuxTracepoints/tree/main/libeventheader-tracepoint)
//! encoding; requires a Linux 6.4+ kernel).
//! *Note*: Linux kernels without user_events support will not log any events.
//!
//! ### ETW
//!
//! ETW is a Windows-specific system wide, high performance, lossy tracing API built into the
//! Windows kernel. Events can be correlated alongside other system activity, such as disk IO,
//! memory allocations, sample profiling, network activity, or any other event logged by the
//! thousands of ETW providers built into Windows and 3rd party software and drivers.
//!
//! ETW is not designed to be a transport mechanism or message passing interface for
//! forwarding data. These scenarios are better covered by other technologies
//! such as RPC or socket-based transports.
//!
//! Users unfamiliar with the basics of ETW may find the following links helpful.
//! The rest of the documentation for this exporter will assume familiarity
//! with ETW and trace processing tools such as WPA, PerfView, or TraceView.
//! - <https://learn.microsoft.com/windows/win32/etw/about-event-tracing>
//! - <https://learn.microsoft.com/windows-hardware/test/weg/instrumenting-your-code-with-etw>
//!
//! This layer uses [TraceLogging](https://learn.microsoft.com/windows/win32/tracelogging/trace-logging-about)
//! to log events. The ETW provider ID is generated from a hash of the specified provider name.
//!
//! ### Linux user_events
//!
//! User-mode event tracing [(user_events)](https://docs.kernel.org/trace/user_events.html)
//! is new to the Linux kernel starting with version 6.4. For the purposes of this exporter,
//! its functionality is nearly identical to ETW. Any differences between the two will be explicitly
//! called out in these docs.
//!
//! The [perf](https://perf.wiki.kernel.org/index.php/Tutorial) tool can be used on Linux to
//! collect user_events events to a file on disk that can then be processed into a readable
//! format. Because these events are encoded in the new [EventHeader](https://github.com/microsoft/LinuxTracepoints/)
//! format, you will need a tool that understands this encoding to process the perf.dat file.
//! The [decode_perf](https://github.com/microsoft/LinuxTracepoints/tree/main/libeventheader-decode-cpp)
//! sample tool can be used to do this currently; in the future support will be added to additional tools.
//!
//! ## Example
//!
//! ```no_run
//! use tracing::{event, Level};
//! use tracing_subscriber::{self, prelude::*};
//!
//! tracing_subscriber::registry()
//! .with(tracing_etw::LayerBuilder::new("SampleProviderName").build().unwrap())
//! .init();
//!
//! event!(Level::INFO, fieldB = b'x', fieldA = 7, "Event Message!");
//! ```
//!
//! ## etw_event macro
//!
//! **Despite the name, this macro works for both ETW and user_events.**
//!
//! The `etw_event!` macro is an **optional** additional logging macro
//! based on `event!` that adds keyword, tags, and event-name support.
//! 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 [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
//! compiled binary, and relies on linker support to work properly.
//! It has been tested with Microsoft's, GCC's, and LLVM's linker.
//!
//! ## Performance Details
//!
//! Performance will vary from machine to machine, but this crate should be
//! fast enough to log tens of thousands of events per second.
//!
//! There are benchmarks available in the code, but they currently rely on
//! an unpublished crate to start and stop an ETW tracing session
//! (and rely on the user to manually start collecting events with the
//! `perf` tool on Linux). Future work will make these easier to run locally.
//!
//! ### Disabled Events
//!
//! When an event is not enabled by a ETW session or Linux tracing session,
//! the cost of logging an event should be effectively zero. This is a few
//! instructions to check the process-local enablement mask
//! and skip over performing any further work if the event is not enabled.
//!
//! ### Enabled Events
//!
//! When an event is enabled by a collector, in addition to the unavoidable
//! overhead from the `tracing` crate (which is itself quite minimal), this
//! crate will dynamically convert the `tracing` event into a Tracelogging/EventHeader
//! event. Users of the C macros for these logging APIs may be familiar with
//! how they generate the event metadata statically at compile time and copied
//! directly to the kernel with the event payload values. While the `tracing`
//! logging macros are able to generate metadata statically at compile time
//! too, this metadata is not in the correct format for ETW/user_events.
//! Instead, when an event is logged, this crate will iterate over the
//! fields in the event and dynamically generate an equivalent field in the
//! new event. This is in line with how every other `tracing` layer works,
//! however, it may be unexpected for those coming from Tracelogging for C/C++.
//! For the most part this translation is quite fast and should not be any slower
//! than any other `tracing` layer.
//!
//! Events are logged synchronously through to the kernel. They are then delivered
//! to consumers asynchronously per the platform design.
//!
//! ### Heap Allocations
//!
//! The goal of this crate is to have no heap allocations in the hot path (when an event is logged).
//! Currently there are a few circumstances when logging an event requires a heap
//! allocation, outlined below.
//!
//! Total memory usage by this crate will vary based on workload, but will usually
//! stay in the 10s or low 100s of kilobytes, and should remain stable once the
//! first event has been logged.
//!
//! - Each `tracing-etw::Layer` that is added will heap allocate some internal state
//! when `build` is called.
//!
//! - Logging events with the [Debug][std::fmt::Debug] format specifier (`:?`) will
//! necessitate a heap allocation to format the value into a string.
//!
//! <div class="warning">
//!
//! All event messages (the format strings to the event) are, for reasons
//! outside of the control of this crate, formatted as debug formats rather than as
//! a string. This effectively means every single event is forced to perform at least
//! one heap allocation. In benchmarking, this does not seem to be a major performance issue.
//!
//! </div>
//!
//! - Logging strings copies them to the heap first. This is a side-effect of how
//! `tracing` presents the strings to each layer; the lifetime of the string is
//! too short for what this crate currently needs, but it may be possible to improve
//! this in the future.
//!
//! - Logging a span allocates a copy of the span's fields on the heap. This is needed
//! so the values can be updated during execution and the final payload values logged
//! when the span ends. This allocation is freed when the span ends.
//!
//! - The first time an event is logged (the event is enabled at the platform layer and
//! the logging code is run), this crate will scan the binary for any metadata left
//! by the `etw_event!` macro. This information will be cached in a single heap
//! allocation for later use by other logging calls. This cached memory is never freed
//! until the process exits; if this crate is used in a dynamic library that unloads
//! before the process exits, the memory will be leaked.
//!
//! - A thread-local event builder is allocated for each thread that logs an event.
//! This allows for complete thread safety when logging events. This allocation
//! will stay alive until the thread ends. Additionally, the builder itself will allocate
//! scratch space for constructing the event. This scratch space will grow to fit the
//! very largest event that has been logged so far, but will not shrink. Generally,
//! this should not be much more than a few kilobytes per-thread.
//!
//! ### Miscellaneous
//!
//! This crate attempts to avoid dynamic dispatch in the critical logging path.
//! This may result in some very large type names, especially for the global
//! subscriber.
//!
//! ## Alternative Crates
//!
//! There are a few other crates that also convert `tracing` events to ETW and/or user_events.
//! - [win_etw_tracing](https://crates.io/crates/win_etw_tracing): This crate should be
//! considered superseded by `tracing-etw` when it comes to utilizing it with `tracing`.
//! For logging directly to ETW without going through `tracing` it may still be used, though
//! the ETW team recommends using the [tracelogging] crate for this instead, as it allows the use
//! of features such as keywords, which are considered best practice for ETW events.
//! - OpenTelemetry ETW / user_events exporters with `tracing-opentelemetry` layer: These crates
//! have significant performance overhead, and sending events through OpenTelemetry adds additional
//! overhead and latency. Events that are disabled at the platform layer still appear as
//! enabled to `tracing`, and will always have the performance cost of being sent through the
//! OpenTelemetry export pipeline even if they will then be thrown away. Some implementations
//! of this export pipeline double-buffer events for processing, and the event may not have
//! been delivered to the kernel by the time the event logging function has returned. Some
//! export pipelines are also strictly single-threaded when it comes to exporting events in
//! the queue.
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
// 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.
// Module holding internal details that need to be public but should not be directly used by consumers of the crate.
// OpenTelemetry integration module - only available with the "opentelemetry" feature
pub
pub use LayerBuilder;