TraceLogging C/C++ Quick Start

The following section describes the basic steps required to add TraceLogging to C/C++ user-mode code.

Prerequisites

  • Microsoft Visual Studio 2013 or later.
  • Windows 10 Software Development Kit (SDK) is required to write a user-mode provider.
  • Windows Driver Kit (WDK) for Windows 10 is required to write a kernel-mode provider.

Important

To avoid linker errors for unresolved EventRegister, EventWriteTransfer, or EventUnregister functions, link with advapi32.lib when compiling these examples.

To collect and decode events from these examples, you will need to start a trace using a tool like tracelog or traceview, run the example, stop the trace using a tool like tracelog or traceview, and decode the trace using a decoding tool like tracefmt or traceview. For example, if my provider was defined using GUID {0205c616-cf97-5c11-9756-56a2cee02ca7}, I might view the events from these examples using Windows SDK tools tracelog and tracefmt as follows:

  • tracelog -start MyTraceSession -f MyTraceFile.etl -guid #0205c616-cf97-5c11-9756-56a2cee02ca7
  • Run the example.
  • tracelog -stop MyTraceSession
  • tracefmt -o MyTraceFile.txt MyTraceFile.etl
  • notepad MyTraceFile.txt

SimpleTraceLoggingExample.h

This example header includes the TraceLogging APIs and forward declares the provider handle that will be used to log events. Any class that wishes to use TraceLogging will include this header and can then start logging.

#pragma once

#include <windows.h> // Definitions required by TraceLoggingProvider.h
#include <TraceLoggingProvider.h> // The C/C++ TraceLogging API

// Forward-declare the g_hMyComponentProvider variable that you will use for tracing in this component
TRACELOGGING_DECLARE_PROVIDER(g_hMyComponentProvider);

The header file includes TraceLoggingProvider.h which defines the C/C++ TraceLogging API. You must include windows.h first because it defines constants used by TraceLoggingProvider.h.

The header file forward declares the provider handle g_hMyComponentProvider that you will pass to the TraceLogging APIs to log events. This handle needs to be accessible to any code that wishes to use TraceLogging.

TRACELOGGING_DECLARE_PROVIDER is a macro that creates an extern const TraceLoggingHProvider handle using the name that you provide, which in the example above is g_hMyComponentProvider. You will allocate the actual provider handle variable in a code file.

SimpleTraceLoggingExample.cpp

The following example registers the provider, logs an event, and unregisters the provider.

#include "SimpleTraceLoggingExample.h"

// Define a handle to a TraceLogging provider
TRACELOGGING_DEFINE_PROVIDER(
    g_hMyComponentProvider,
    "SimpleTraceLoggingProvider",
    // {0205c616-cf97-5c11-9756-56a2cee02ca7}
    (0x0205c616,0xcf97,0x5c11,0x97,0x56,0x56,0xa2,0xce,0xe0,0x2c,0xa7));

void main()
{

    char sampleValue[] = "Sample value";

    // Register the provider
    TraceLoggingRegister(g_hMyComponentProvider);

    // Log an event
    TraceLoggingWrite(g_hMyComponentProvider, // handle to my provider
        "HelloWorldTestEvent",              // Event Name that should uniquely identify your event.
        TraceLoggingValue(sampleValue, "TestMessage")); // Field for your event in the form of (value, field name).

    // Stop TraceLogging and unregister the provider
    TraceLoggingUnregister(g_hMyComponentProvider);
}

The example above includes SimpleTraceLoggingExample.h which contains the global provider variable that your code will use to log events.

The TRACELOGGING_DEFINE_PROVIDER macro allocates storage and defines the provider handle variable. The variable name that you provide to this macro must match the name you used in the TRACELOGGING_DECLARE_PROVIDER macro in your header file.

Register the provider handle

Before you can use the provider handle to log events you must call TraceLoggingRegister to register your provider handle. This is typically done in main() or DLLMain() but can be done at any time as long as it precedes any attempt to log an event. If you log an event before registering the provider handle, no error will occur but the event will not be logged. The following code from the example above registers the provider handle.

// Define the GUID to use in TraceLoggingProviderRegister
TRACELOGGING_DEFINE_PROVIDER(
    g_hMyComponentProvider,
    "SimpleTraceLoggingProvider",
    // {0205c616-cf97-5c11-9756-56a2cee02ca7}
    (0x0205c616,0xcf97,0x5c11,0x97,0x56,0x56,0xa2,0xce,0xe0,0x2c,0xa7));

void main()
{
    char sampleValue[] = "Sample value";

    // Register the provider
    TraceLoggingRegister(g_hMyComponentProvider);

Log a Tracelogging event

After the provider is registered, the following code logs a simple event.

    // Log an event
    TraceLoggingWrite(g_hMyComponentProvider, // handle to my provider
        "HelloWorldTestEvent",              // Event Name that should uniquely identify your event.
        TraceLoggingValue(sampleValue, "TestMessage")); // Field for your event in the form of (value, field name).

The TraceLoggingWrite macro accepts up to ninety-nine arguments. The event name is stored in UTF-8 format. You must not use embedded '\0' characters in the event name or field names. There are no other limits on permitted characters, though some event decoders or event processors might have their own limitations.

Each argument following the event name must be wrapped inside of a TraceLogging Wrapper Macro. If you are using C++, you can use the TraceLoggingValue wrapper macro to automatically deduce the type of the argument. If you are writing in C or if you want more control over the field type, you must use type-specific field macros such as TraceLoggingInt32, TraceLoggingUnicodeString, TraceLoggingString, etc.

In addition to logging single events you can also group events by activity by using the TraceLoggingWriteActivity or TraceLoggingWriteStart/TraceLoggingWriteStop macros found in TraceLoggingActivity.h. Activities correlate events and are useful for scenarios that have a beginning and an end. For example, you might use an activity to measure a scenario that starts with the launch of an application, includes the time it takes for the splash screen becomes available, and ends when the initial screen of the application becomes visible.

Activities capture single events and nest other activities that occur between the start and end of that activity. Activities have per-process scope and must be passed from thread to thread to properly nest multi-threaded events.

The scope of a provider handle is limited to the module (DLL, EXE, or SYS file) in which it is defined. The handle should not be passed to other DLLs. If a TraceLoggingWrite macro is invoked in A.DLL using a provider handle defined in B.DLL, it may cause problems. The safest and most efficient way to meet this requirement is to just always directly reference the global provider handle and never pass the provider handle as a parameter.

Unregister the provider

Before your component unloads, you must unregister the TraceLogging provider. This is especially important for DLLs and drivers. A crash is likely if a DLL or a driver unloads without unregistering the provider.

The following code unregisters the provider:

// Stop TraceLogging and unregister the provider
TraceLoggingUnregister(g_hMyComponentProvider);

Compatibility

Depending on its configuration, TraceLoggingProvider.h can be backwards-compatible (resulting program will run onWindows Vista or later), or can be optimized for later OS versions. TraceLoggingProvider.h uses WINVER (user-mode) and NTDDI_VERSION (kernel-mode) to determine whether it should be compatible with earlier OS versions or be optimized for newer OS versions.

For user-mode, if you include <windows.h> before setting WINVER, <windows.h> sets WINVER to the SDK's default target OS version. If WINVER is set to 0x602 or higher, TraceLoggingProvider.h optimizes its behavior for Windows 8 or later and your app will not run on earlier versions of Windows. If you need your program to run on Vista or Windows 7, be sure to set WINVER to the appropriate value before including <windows.h>.

Similarly, if you include <wdm.h> before setting NTDDI_VERSION, <wdm.h> sets NTDDI_VERSION to a default value. If NTDDI_VERSION is set to 0x06040000 or higher, TraceLoggingProvider.h optimizes its behavior for Windows 10 and your driver will not work on earlier versions of Windows.

This behavior can be controlled direction by setting TLG_HAVE_EVENT_SET_INFORMATION before including TraceLoggingProvider.h. Refer to the comments in the TraceLoggingProvider.h header for details on the TLG_HAVE_EVENT_SET_INFORMATION macro.

Summary and next steps

To see how to capture and view TraceLogging data using the Windows Performance Tools (WPT), see Record and Display TraceLogging Events.

See C/C++ Tracelogging Examples for more C++ TraceLogging examples.