/// <summary> /// This method demonstrates the LoggingChannel and LoggingActivity APIs. /// </summary> /// <param name="channel"> /// The channel to use for the demonstration. This channel may have been /// constructed using a Windows 8.1 constructor or a Windows 10 constructor. /// The same APIs are supported in both cases, but the ETL events will be /// formatted a bit differently depending on how the channel was constructed. /// </param> private void DemonstrateLogging(LoggingChannel channel) { // Whenever any ETW session changes the way it is listening to this // channel, the LoggingEnable event is fired. For example, this might // be called when a session begins listening, changes the level at // which it is listening, or stops listening. channel.LoggingEnabled += this.OnLoggingEnabled; // Log simple string events channel.LogMessage("Simple message"); // default level is Verbose channel.LogMessage("Simple error", LoggingLevel.Error); // Log simple string + integer events. channel.LogValuePair("Simple message", 123); // default level is Verbose channel.LogValuePair("Simple error", 456, LoggingLevel.Error); // The channel.Name property returns the name that was used when the // channel was constructed. When running in Windows 10 mode, the name // is already set as the provider name, so no LoggingChannelName is // automatically added to the event. channel.LogMessage(channel.Name); // The channel.Id property is new to Windows 10. channel.LogMessage(channel.Id.ToString()); // If you want to avoid the overhead of collecting data when nobody is // listening to your channel, check the Enabled property before logging. if (channel.Enabled) { channel.LogMessage(this.CollectExpensiveData()); } // The IsEnabled() method is exactly the same as the Enabled property, // except that it is a new Windows 10 API. if (channel.IsEnabled()) { channel.LogMessage(this.CollectExpensiveData()); } // If you want to only collect data if somebody is listening at a specific // level, you need to check both Enabled and Level. Note that the value of // the Level is unspecified when Enabled is false. if (channel.Enabled && channel.Level <= LoggingLevel.Warning) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // The IsEnabled(LoggingLevel) method is a bit nicer than checking both // Enabled and Level, but it is only available on Windows 10 or later. if (channel.IsEnabled(LoggingLevel.Warning)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // You can also use IsEnabled to check for keywords. if (channel.IsEnabled(LoggingLevel.Information, 0x10)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Information); } // Use LoggingFields with the LogEvent method to write complex events. var fields = new LoggingFields(); fields.AddDouble("pi", 3.14159); channel.LogEvent( "ComplexEvent", fields, LoggingLevel.Verbose, new LoggingOptions(0x10)); // Keywords = 0x10 // You can add any number of name-value pairs to a fields object, though // you may encounter ETW limitations if you add too many. For example, // ETW is limited to a maximum event size of 64KB, and the current // TraceLogging decoder can handle no more than 128 fields. // Performance optimization: You can reuse a LoggingFields object to // avoid unnecessary allocations. Don't forget to call Clear() // between uses, and don't try to share a LoggingFields object between // threads. fields.Clear(); fields.AddDateTime("Date", DateTimeOffset.Now); channel.LogEvent("Now", fields); fields.Clear(); // You can add a formatting hint to affect the way a value is decoded. // Not all combinations are useful, and the hint may be ignored. // For example, you can encode an MBCS string by writing a byte array // with a String hint. fields.AddUInt8Array( "AnsiString", new byte[] { 65, 66, 67, 49, 50, 51 }, // "ABC123" LoggingFieldFormat.String); // You can add "tag" bits to a field. These are user-defined bits that // can be used to communicate with an event processing tool. For example, // you might define a tag bit to indicate that a field contains private // data that should not be displayed on-screen. fields.AddString("Password", "12345", LoggingFieldFormat.Default, 0x10); // You can add a "structure" to an event. A structure is a name for a // group of fields. Structures can nest. Call BeginStruct to add a level // of nesting, and call EndStruct after the last field of the structure. fields.BeginStruct("Nested"); fields.AddInt16("Nested-1", 1); fields.AddInt16("Nested-2", 2); fields.BeginStruct("Nested-Nested"); fields.AddInt16("Nested-Nested-3", 3); fields.EndStruct(); fields.AddInt16("Nested-4", 4); fields.EndStruct(); // Advanced scenarios: you can use a LoggingOptions object to control // detailed event settings such as keywords, opcodes, and activity Ids. // These have their normal ETW semantics. You can also set event tags, // which are bit values that can be used to communicate with the event // processor. channel.LogEvent( "VeryComplexEvent", fields, LoggingLevel.Information, new LoggingOptions { Keywords = 0x123, Tags = 0x10 }); // Windows 10 introduces the ILoggingTarget interface. LoggingChannel // implements this interface. This interface allows components to accept // a logger as an parameter. this.DoSomething(channel); /* * If a LoggingActivity is created using a LoggingActivity constructor, * it will use Windows 8.1 semantics: * * - If an activity is destroyed (garbage-collected) without being closed * and the associated LoggingChannel is still open, the activity will * write a default Stop event. * - The default Stop event (written by the destructor or by the Close() * method) is encoded as a "simple" event. * * The 8.1 semantics are deprecated because the automatic generation of * a Stop event at garbage-collection can be misleading. The Stop event * is intended to mark the a precise point at which an activity is * completed, while the garbage-collection of an abandoned activity is * inherently imprecise and unpredictable. * * If a LoggingActivity is created using a StartActivity method, it will * use Windows 10 semantics: * * - If an activity is destroyed (garbage-collected) without being closed, * there will be no Stop event for the activity. * - The default Stop event (written by the Close() method) is encoded as * a TraceLogging event with name "ActivityClosed". */ // This activity is created with Windows 8.1 semantics. using (var a1 = new LoggingActivity("Activity1", channel)) { // The activity Start event is written by the LoggingActivity constructor. // You would do your activity's work here. // The activity Stop event is written when the activity is closed (disposed). // The Windows 10 LoggingActivity adds new methods for writing events // that are marked as associated with the activity. a1.LogEvent("Activity event"); // LoggingActivity also implements the ILoggingTarget interface, so you can // use either a channel or an activity as a logging target. this.DoSomething(a1); // The Windows 10 LoggingActivity adds new methods for creating nested activities. // Note that nested activities are always created with Windows 10 semantics, // even when nested under an activity that is using Windows 8.1 semantics. using (var a2 = a1.StartActivity("Activity2")) { // Nested task occurs here. // The Windows 10 LoggingActivity allows you to customize the Stop event. a2.StopActivity("Activity 2 stop"); } // Because a1 is using Windows 8.1 semantics, if we did not call Dispose(), // it would attempt to write a Stop event when it is garbage collected. // Writing Stop events during garbage collection is not useful, so be sure // to properly stop, close, or dispose activities. } // The Windows 10 StartActivity method creates a new activity, optionally with // specified fields and characteristics. // This activity is created with Windows 10 semantics. using (var a3 = channel.StartActivity("Activity3")) { // Because a3 is using Windows 10 semantics, if we did not call Dispose(), // there would be no Stop event (not even when the activity is garbage // collected). To get a Stop event, be sure to stop, close, or dispose the // activity. } }
/// <summary> /// This method demonstrates the LoggingChannel and LoggingActivity APIs. /// </summary> /// <param name="channel"> /// The channel to use for the demonstration. This channel may have been /// constructed using a Windows 8.1 constructor or a Windows 10 constructor. /// The same APIs are supported in both cases, but the ETL events will be /// formatted a bit differently depending on how the channel was constructed. /// </param> private void DemonstrateLogging(LoggingChannel channel) { // Whenever any ETW session changes the way it is listening to this // channel, the LoggingEnable event is fired. For example, this might // be called when a session begins listening, changes the level at // which it is listening, or stops listening. channel.LoggingEnabled += this.OnLoggingEnabled; // Log simple string events channel.LogMessage("Simple message"); // default level is Verbose channel.LogMessage("Simple error", LoggingLevel.Error); // Log simple string + integer events. channel.LogValuePair("Simple message", 123); // default level is Verbose channel.LogValuePair("Simple error", 456, LoggingLevel.Error); // The channel.Name property returns the name that was used when the // channel was constructed. When running in Windows 10 mode, the name // is already set as the provider name, so no LoggingChannelName is // automatically added to the event. channel.LogMessage(channel.Name); // The channel.Id property is new to Windows 10. channel.LogMessage(channel.Id.ToString()); // If you want to avoid the overhead of collecting data when nobody is // listening to your channel, check the Enabled property before logging. if (channel.Enabled) { channel.LogMessage(this.CollectExpensiveData()); } // The IsEnabled() method is exactly the same as the Enabled property, // except that it is a new Windows 10 API. if (channel.IsEnabled()) { channel.LogMessage(this.CollectExpensiveData()); } // If you want to only collect data if somebody is listening at a specific // level, you need to check both Enabled and Level. Note that the value of // the Level is unspecified when Enabled is false. if (channel.Enabled && channel.Level <= LoggingLevel.Warning) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // The IsEnabled(LoggingLevel) method is a bit nicer than checking both // Enabled and Level, but it is only available on Windows 10 or later. if (channel.IsEnabled(LoggingLevel.Warning)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Warning); } // You can also use IsEnabled to check for keywords. if (channel.IsEnabled(LoggingLevel.Information, 0x10)) { channel.LogMessage(this.CollectExpensiveData(), LoggingLevel.Information); } // Use LoggingFields with the LogEvent method to write complex events. var fields = new LoggingFields(); fields.AddDouble("pi", 3.14159); channel.LogEvent( "ComplexEvent", fields, LoggingLevel.Verbose, new LoggingOptions(0x10)); // Keywords = 0x10 // You can add any number of name-value pairs to a fields object, though // you may encounter ETW limitations if you add too many. For example, // ETW is limited to a maximum event size of 64KB, and the current // TraceLogging decoder can handle no more than 128 fields. // Performance optimization: You can reuse a LoggingFields object to // avoid unnecessary allocations. Don't forget to call Clear() // between uses, and don't try to share a LoggingFields object between // threads. fields.Clear(); fields.AddDateTime("Date", DateTimeOffset.Now); channel.LogEvent("Now", fields); fields.Clear(); // You can add a formatting hint to affect the way a value is decoded. // Not all combinations are useful, and the hint may be ignored. // For example, you can encode an MBCS string by writing a byte array // with a String hint. fields.AddUInt8Array( "AnsiString", new byte[] { 65, 66, 67, 49, 50, 51 }, // "ABC123" LoggingFieldFormat.String); // You can add "tag" bits to a field. These are user-defined bits that // can be used to communicate with an event processing tool. For example, // you might define a tag bit to indicate that a field contains private // data that should not be displayed on-screen. fields.AddString("Password", "12345", LoggingFieldFormat.Default, 0x10); // You can add a "structure" to an event. A structure is a name for a // group of fields. Structures can nest. Call BeginStruct to add a level // of nesting, and call EndStruct after the last field of the structure. fields.BeginStruct("Nested"); fields.AddInt16("Nested-1", 1); fields.AddInt16("Nested-2", 2); fields.BeginStruct("Nested-Nested"); fields.AddInt16("Nested-Nested-3", 3); fields.EndStruct(); fields.AddInt16("Nested-4", 4); fields.EndStruct(); // Advanced scenarios: you can use a LoggingOptions object to control // detailed event settings such as keywords, opcodes, and activity Ids. // These have their normal ETW semantics. You can also set event tags, // which are bit values that can be used to communicate with the event // processor. channel.LogEvent( "VeryComplexEvent", fields, LoggingLevel.Information, new LoggingOptions { Keywords = 0x123, Tags = 0x10 }); // Windows 10 introduces the ILoggingTarget interface. LoggingChannel // implements this interface. This interface allows components to accept // a logger as an parameter. this.DoSomething(channel); /* If a LoggingActivity is created using a LoggingActivity constructor, it will use Windows 8.1 semantics: - If an activity is destroyed (garbage-collected) without being closed and the associated LoggingChannel is still open, the activity will write a default Stop event. - The default Stop event (written by the destructor or by the Close() method) is encoded as a "simple" event. The 8.1 semantics are deprecated because the automatic generation of a Stop event at garbage-collection can be misleading. The Stop event is intended to mark the a precise point at which an activity is completed, while the garbage-collection of an abandoned activity is inherently imprecise and unpredictable. If a LoggingActivity is created using a StartActivity method, it will use Windows 10 semantics: - If an activity is destroyed (garbage-collected) without being closed, there will be no Stop event for the activity. - The default Stop event (written by the Close() method) is encoded as a TraceLogging event with name "ActivityClosed". */ // This activity is created with Windows 8.1 semantics. using (var a1 = new LoggingActivity("Activity1", channel)) { // The activity Start event is written by the LoggingActivity constructor. // You would do your activity's work here. // The activity Stop event is written when the activity is closed (disposed). // The Windows 10 LoggingActivity adds new methods for writing events // that are marked as associated with the activity. a1.LogEvent("Activity event"); // LoggingActivity also implements the ILoggingTarget interface, so you can // use either a channel or an activity as a logging target. this.DoSomething(a1); // The Windows 10 LoggingActivity adds new methods for creating nested activities. // Note that nested activities are always created with Windows 10 semantics, // even when nested under an activity that is using Windows 8.1 semantics. using (var a2 = a1.StartActivity("Activity2")) { // Nested task occurs here. // The Windows 10 LoggingActivity allows you to customize the Stop event. a2.StopActivity("Activity 2 stop"); } // Because a1 is using Windows 8.1 semantics, if we did not call Dispose(), // it would attempt to write a Stop event when it is garbage collected. // Writing Stop events during garbage collection is not useful, so be sure // to properly stop, close, or dispose activities. } // The Windows 10 StartActivity method creates a new activity, optionally with // specified fields and characteristics. // This activity is created with Windows 10 semantics. using (var a3 = channel.StartActivity("Activity3")) { // Because a3 is using Windows 10 semantics, if we did not call Dispose(), // there would be no Stop event (not even when the activity is garbage // collected). To get a Stop event, be sure to stop, close, or dispose the // activity. } }