Crate win_etw_macros[][src]

Expand description

Provides the #[trace_logging_provider] macro, which allows you to define a Trace Logging Provider for use with the Event Tracing for Windows (ETW) framework.

This macro is intended for use only when targeting Windows. When targeting other platforms, this macro will still work, but will generate code that does nothing.

This framework allows applications to log schematized events, rather than textual strings. ETW analysis tools can reliably identify fields within your events, and treat them as strongly-typed data, rather than text strings.

How to create and use an event provider

In ETW, an event provider is a software object that generates events. Event controllers set up event logging sessions, and event consumers read and interpret event data. This crate focuses on enabling applications to create event providers.

Add crate dependencies

Add these dependencies to your Cargo.toml file:

win_etw_macros = "0.1.*"
win_etw_provider = "0.1.*"

win_etw_macros contains the procedural macro that generates eventing code. win_etw_provider contains library code that is called by the code that is generated by win_etw_macros.

Create a new GUID for your event provider

You must assign a new, unique GUID to each event provider. ETW uses this GUID to identify your events that are generated by your provider. Windows contains many event providers, so it is important to be able to select only the events generated by your application. This GUID is also used internally by ETW to identify event metadata (field types), so it is important that your GUID be unique. Otherwise, events from conflicting sources that use the same GUID may be incorrectly interpreted.

There are many tools which can create a GUID, such as:

  • In Visual Studio, in the Tools menu, select “Create GUID”.
  • From a Visual Studio command line, run uuidgen.exe.
  • From an Ubuntu shell, run uuidgen.

Define the event provider and its events

Add a trait definition to your source code and annotate it with the #[trace_logging_provider(guid = "...")] macro, using the GUID that you just created. The trait definition is only used as input to the procedural macro; the trait is not emitted into your crate, and cannot be used as a normal trait.

Defining event types (event methods)

In the trait definition, add method signatures. Each method signature defines an event type. The parameters of each method define the fields of the event type. Only a limited set of field types are supported (enumerated below).

The #[trace_logging_provider] macro consumes the trait definition and produces a struct definition with the same name and the same method signatures. (The trait is not available for use as an ordinary trait.)

#[trace_logging_provider(guid = "... your guid here ...")]
pub trait MyAppEvents {
    fn http_request(client_address: &SockAddr, is_https: bool, status_code: u32, status: &str);
    fn database_connection_created(connection_id: u64, server: &str);
    fn database_connection_closed(connection_id: u64);
    // ...

Create an instance of the event provider

At initialization time (in your fn main(), etc.), create an instance of the event provider:

let my_app_events = MyAppEvents::new();

Your application should only create a single instance of each event provider, per process. That is, you should create a single instance of your event provider and share it across your process. Typically, an instance is stored in static variable, using a lazy / atomic assignment. There are many crates and types which can support this usage pattern.

Call event methods to report events

To report an event, call one of the methods defined on the event provider. The method will call into ETW to report the event, but there is no guarantee that the event is stored or forwarded; events can be dropped if event buffer resources are scarce.

my_app_events.client_connected(&"".parse(), false, 100, "OK");

Supported field types

Only a limited set of field types are supported.

  • Integer primitives up to 64 bits: i8, i16, i32, i64, u8, u16, u32, u64
  • Floating point primitives: f32, f64
  • Architecture-dependent sizes: usize, isize.
  • Boolean: bool
  • Slices of all of the supported primitives, except for bool: &[u8], &[u16], etc. &[bool] is not supported because bool does not have a guaranteed stable representation.
  • Windows [FILETIME]( The type must be declared exactly as FILETIME; type aliases or fully-qualified paths (such as winapi::shared::minwindef::FILETIME) will not work. The parameter type in the generated code will be win_etw_provider::FILETIME, which is a newtype over u64.
  • std::time::SystemTime is supported, but it must be declared exactly as SystemTime; type aliases or fully-qualified paths (such as std::time::SystemTime) will not work.
  • SockAddr, SockAddrV4, and SockAddrV6 are supported. They must be declared exactly as shown, not using fully-qualified names or type aliases.

Provider groups

When creating an ETW provider, you can place ETW providers into provider groups. A provider group can be enabled or disabled as a unit. To do so, specify the GUID of the provider group when declaring the ETW provider. For example:

    guid = "...",                   // GUID of this provider
    provider_group_guid = "..."     // GUID of the provider group that this provider belongs to
pub trait MyEvents {
    // ...

The #[event] attribute

The #[event] atttribute allows you to control various aspects of each event type. It is not necessary to use the #[event] attribute; if it is not specified, then reasonable defaults will be chosen. You can use the #[event] attribute to control these aspects of each event type:

  • #[event(id = NN)] - Specifies the event ID. All event types declared on a specific event provider must have unique event IDs. See EVENT_DESCRIPTOR::Id.
  • #[event(level = NN)] or #[event(level = "...")] - Specifies the event level. See EVENT_DESCRIPTOR::Level. This can either be a numeric value, or one of the following literal strings: "critical", "error", "warn", "info", "verbose".
  • #[event(opcode = NN)] - Specifies the EVENT_DESCRIPTOR::Opcode field.
  • #[event(task = NN) - Specifies the EVENT_DESCRIPTOR::Task field.
  • #[event(keyword = NN) - Specifies the EVENT_DESCRIPTOR::Keyword field.

You can use a single #[event] attribute with multiple values, or you can use multiple #[event] attributes.

How to capture and view events

There are a variety of tools which can be used to capture and view ETW events. The simplest tool is the TraceView tool from the Windows SDK. Typically it is installed at this path: C:\Program Files (x86)\Windows Kits\10\bin\10.0.<xxxxx>.0\x64\traceview.exe, where <xxxxx> is the release number of the Windows SDK.

Run TraceView, then select “File”, then “Create New Log Session”. Select “Manually Entered GUID or Hashed Name” and enter the GUID that you have assigned to your event provider. Click OK. The next dialog will prompt you to choose a source of WPP format information; select Auto and click OK.

At this point, TraceView should be capturing events (for your assigned GUID) and displaying them in real time, regardless of which process reported the events.

These tools can also be used to capture ETW events:

  • Windows Performance Recorder This tool is intended for capturing system-wide event streams. It is not useful for capturing events for a specific event provider.
  • logman is a command-line tool for managing events.
  • Tracelog

There are other tools, such as the Windows Performance Recorder, which can capture ETW events.


Attribute Macros

Allows you to create ETW Trace Logging Providers. See the module docs for more detailed instructions for this macro.