From f94b69efb8c62b4687df55a0378dd27b0943b65f Mon Sep 17 00:00:00 2001 From: John Salem Date: Thu, 2 Sep 2021 14:08:20 -0700 Subject: [PATCH 1/9] Update and improve EventSource documentation * reduces focus on ETW * Explains difference between self describing and manifest event formats * adds more complex examples * explains conventions for a well designed EventSource * remove references to 4.5 and 4.6 to reduce reading complexity * remove note about IDisposable --- .../VS_Snippets_CLR/etwtrace/cs/program.cs | 6 +- .../etwtracelarge/cs/program.cs | 169 ++++++++++++++++++ .../etwtracesmall/cs/program.cs | 2 +- .../EventSource.xml | 68 +++++-- 4 files changed, 223 insertions(+), 22 deletions(-) create mode 100644 samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/program.cs b/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/program.cs index eb172a9324a..1033aecc23d 100644 --- a/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/program.cs +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/program.cs @@ -7,11 +7,11 @@ namespace Demo2 enum MyColor { Red, Yellow, Blue }; [EventSource(Name = "MyCompany")] - class MyCompanyEventSource : EventSource + sealed class MyCompanyEventSource : EventSource { // // - public class Keywords + public static class Keywords { public const EventKeywords Page = (EventKeywords)1; public const EventKeywords DataBase = (EventKeywords)2; @@ -21,7 +21,7 @@ public class Keywords // // - public class Tasks + public static class Tasks { public const EventTask Page = (EventTask)1; public const EventTask DBQuery = (EventTask)2; diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs new file mode 100644 index 00000000000..cf506b0dd63 --- /dev/null +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs @@ -0,0 +1,169 @@ +// +using System; +using System.Diagnostics.Tracing; + +namespace Demo +{ + // + public interface IMyLogging + { + void Error(int errorCode, string message); + void Warning(string message); + } + + public sealed class MySource : EventSource, IMyLogging + { + public static MySource Log = new(); + + [Event(1)] + public void Error(int errorCode, string message) => WriteEvent(1, errorCode, message); + + [Event(2)] + public void Warning(string message) => WriteEvent(2, message); + } + // + + // + public abstract class UtilBaseEventSource : EventSource + { + protected UtilBaseEventSource() + : base() + { } + + protected UtilBaseEventSource(bool throwOnEventWriteErrors) + : base(throwOnEventWriteErrors) + { } + + // helper overload of WriteEvent for optimizing writing an event containing + // payload properties that don't align with a provided overload. This prevents + // EventSource from using the object[] overload which is expensive. + protected unsafe void WriteEvent(int eventId, int arg1, short arg2, long arg3) + { + if (IsEnabled()) + { + EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; + descrs[0].DataPointer = (IntPtr)(&arg1); + descrs[0].Size = 4 + descrs[1].DataPointer = (IntPtr)(&arg2); + descrs[2].Size = 2 + descrs[1].DataPointer = (IntPtr)(&arg3); + descrs[2].Size = 8 + WriteEventCore(eventId, 3, descrs); + } + } + } + + public sealed class OptimizedEventSource : UtilBaseEventSource + { + public static OptimizedEventSource Log = new(); + + public static class Keywords + { + public const EventKeywords Kwd1 = (EventKeywords)1; + } + + [Event(1, Keywords = Keywords.kwd1, Level = EventLevel.Informational, Message = "LogElements called {0}/{1}/{2}.")] + public void LogElements(int n, short sh, long l) => WriteEvent(1, n, sh, l); // uses the overload we added! + } + // + + // + public class ComplexComponent : IDisposable + { + internal static Dictionary _internalState = new(); + + private string _name; + + public ComplexComponent(string name) + { + _name = name ?? throw new ArgumentNullException(nameof(name)); + ComplexSource.Log.NewComponent(_name); + } + + public void SetState(string key, string value) + { + lock (_internalState) + { + _internalState[key] = value; + ComplexSource.Log.SetState(_name, key, value); + } + } + + // ... + + public void DoWork() + { + ComplexSource.Log.ExpensiveWorkStart(_name); + + ExpensiveWork1(); + ExpensiveWork2(); + ExpensiveWork3(); + ExpensiveWork4(); + + ComplexSource.Log.ExpensiveWorkStop(_name); + } + + public void Dispose() + { + ComplexSource.Log.ComponentDisposed(_name); + } + } + + internal sealed class ComplexSource : EventSource + { + public static ComplexSource Log = new(); + + public static class Keywords + { + public const EventKeywords ComponentLifespan = (EventKeywords)1; + public const EventKeywords StateChanges = (EventKeywords)(1 << 1); + public const EventKeywords Performance = (EventKeywords)(1 << 2); + public const EventKeywords DumpState = (EventKeywords)(1 << 3); + // a utility keyword for a common combination of keywords users might enable + public const EventKeywords StateTracking = ComponentLifespan & StateChanges & DumpState; + } + + protected override void OnEventCommand(EventCommandEventArgs args) + { + base.OnEventCommand(args); + + if (args.Command == EventCommand.Enable) + { + DumpComponentState(); + } + } + + [Event(1, Keywords = Keywords.ComponentLifespan, Message = "New component with name '{0}'.")] + public void NewComponent(string name) => WriteEvent(1, name); + + [Event(2, Keywords = Keywords.ComponentLifespan, Message = "Component with name '{0}' disposed.")] + public void ComponentDisposed(string name) => WriteEvent(2, name); + + [Event(3, Keywords = Keywords.StateChanges)] + public void SetState(string name, string key, string value) => WriteEvent(3, name, key, value); + + [Event(4, Keywords = Keywords.Performance)] + public void ExpensiveWorkStart(string name) => WriteEvent(4, name); + + [Event(5, Keywords = Keywords.Performance)] + public void ExpensiveWorkStop(string name) => WriteEvent(5, name); + + [Event(6, Keywords = Keywords.DumpState)] + public void ComponentState(string key, string value) => WriteEvent(6, key, value); + + [NonEvent] + public void DumpComponentState() + { + if (IsEnabled(EventLevel.Informational, Keywords.DumpState)) + { + lock (ComplexComponent._internalState) + { + foreach (var (key, value) in ComplexComponent._internalState) + ComponentState(key, value); + } + } + } + } + // +} +// \ No newline at end of file diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/program.cs b/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/program.cs index 2fb4aadda6f..e6aa975b6d4 100644 --- a/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/program.cs +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/program.cs @@ -5,7 +5,7 @@ namespace Demo1 { // - class MyCompanyEventSource : EventSource + sealed class MyCompanyEventSource : EventSource { public static MyCompanyEventSource Log = new MyCompanyEventSource(); diff --git a/xml/System.Diagnostics.Tracing/EventSource.xml b/xml/System.Diagnostics.Tracing/EventSource.xml index 136fa24778b..48dfe31b0e3 100644 --- a/xml/System.Diagnostics.Tracing/EventSource.xml +++ b/xml/System.Diagnostics.Tracing/EventSource.xml @@ -49,28 +49,38 @@ - Provides the ability to create events for event tracing for Windows (ETW). + Provides the ability to create events for event tracing across platforms. methods are called to log the events. - -> [!IMPORTANT] -> This type implements the interface. When you have finished using the type, you should dispose of it either directly or indirectly. To dispose of the type directly, call its method in a `try`/`catch` block. To dispose of it indirectly, use a language construct such as `using` (in C#) or `Using` (in Visual Basic). For more information, see the "Using an Object that Implements IDisposable" section in the interface topic. - - The basic functionality of is sufficient for most applications. If you want more control over the ETW manifest that is created, you can apply the attribute to the methods. For advanced event source applications, it is possible to intercept the commands being sent to the derived event source and change the filtering, or to cause actions (such as dumping a data structure) to be performed by the inheritor. An event source can be activated with Windows ETW controllers, such as the Logman tool, immediately. It is also possible to programmatically control and intercept the data dispatcher. The class provides additional functionality. - - Starting with .NET Framework 4.6, provides channel support and some of the event source validation rules have been relaxed. This means: - -- types may now implement interfaces. This enables the use of event source types in advanced logging systems that use interfaces to define a common logging target. - -- The concept of a utility event source type has been introduced. This feature enables sharing code across multiple event source types in a project to enable scenarios such as optimized overloads. - - For a version of the class that provides features such as channel support you are targeting .NET Framework 4.5.1 or earlier, see [Microsoft EventSource Library 1.0.16](https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.EventSource). - - + This class is intended to be inherited by a user class that provides specific events to be used for event tracing. The methods are called to log the events. + The basic functionality of is sufficient for most applications. If you want more control over the event metadata that is created, you can apply the attribute to the methods. For advanced event source applications, it is possible to intercept the commands being sent to the derived event source and change the filtering, or to cause actions (such as dumping a data structure) to be performed by the inheritor. An event source can be activated in-process using and out-of-process using tools EVentPipe based tools such as `dotnet-trace` or Event Tracing for Windows (ETW) based tools like `PerfView` or `Logman`. It is also possible to programmatically control and intercept the data dispatcher. The class provides additional functionality. + +## Conventions + derived classes should follow the following conventions: + + - User defined classes should implement a singleton pattern. The singleton instance is traditionally named `Log`. By extension, users should not call `IDisposable.Dispose` manually and allow the runtime to clean up the singleton instance at the end of managed code execution. + - User defined, derived class should be marked as `sealed` unless implementing the advanced "Utility EventSource" configuration discussed below. + - Users should call before performing any resource intensive work related to firing an event. + - Users can implicitly create s by declaring two event methods with subsequent event IDs that have the naming pattern `Start` and `Stop`. Note that these events _must_ declared next to each other in the class definition and the `Start` method _must_ come first. + - User should attempt to keep their s backwards compatible and appropriately version them. The default version for an event is `0`. The version can be can be changed by setting . Users should change the version of an event whenever they change properites of the payload. New payload properties should always be added to the end of the event declaration. If this is not possible, a new event with a new ID should be created to replace the old one. + - Users should specify fixed size payload properties before variably sized properties when declaring event methods. + - are used as a bitmask for specifying specific events when subscribing to a provider. Users can specify keywords by defining a `public static class Keywords` member class that has `public const EventKeywords` members. + - Expensive events should be associated with a using . This allows users of your to opt out of these expensive operations. + +## Self Describing (Tracelogging) vs Manifest Event Formats + can be configured into to two different modes based on the constructor used or what flags are set on . + + Historically, these two formats are derived from two formats Event Tracing for Windows (ETW) used. These two modes generate the metadata for events differently. + + The default event format is . Manifest-based s will generate an xml document representing the events defined on the class upon intialization. This requires the to reflect over itself to generate the provider and event metadata. + + To use Self Describing (Tracelogging) event format, construct your using the constructor, constructor, or setting the flag. Self Describing sources generate minimnal provider metadata on initialization and only generate event metadata when is called. + + In practice, these event format settings only affect usage with Event Tracing for Windows (ETW) based readers. They can, however, have a small effect on initialization time and per-event write times due to the time requried for reflection and generating the metadata. + ## Examples The following example shows a simple implementation of the class. @@ -82,6 +92,28 @@ :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/program.cs" id="Snippet1"::: :::code language="vb" source="~/samples/snippets/visualbasic/VS_Snippets_CLR/etwtrace/vb/program.vb" id="Snippet1"::: + +## Advanced Usage + Traditionally, user defined s expect to inherit directly from . For advanced scenarios, however, users can create `abstract` s, called a Utility Source, and implement interfaces. Using one or both of these techniques allows users to share code between different derived sources. + +> [!IMPORTANT] +> Abstract s cannot define keywords, tasks, opcodes, channels, or events. + +> [!IMPORTANT] +> When using interfaces with , do not explicitly implement interface methods to avoid name collisions at runtime when generating event metadata. + +The following example shows an implementation of that uses an interface. + + :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs" id="InterfaceSource"::: + +The following example shows an implementation of that uses the Utility EventSource pattern. + + :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs" id="UtilitySource"::: + +The following example shows an implementation of for tracing information about a component in a library. + + :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs" id="ComplexSource"::: + ]]> This type is thread safe. @@ -731,7 +763,7 @@ 4.0.0.0 - netstandard + netstandard 2.0.0.0 2.1.0.0 From 7e97cfea039c3b074d05240931c392dc02f2e389 Mon Sep 17 00:00:00 2001 From: John Salem Date: Thu, 2 Sep 2021 14:29:53 -0700 Subject: [PATCH 2/9] complete "..." in sample so it can compile --- .../etwtracelarge/cs/program.cs | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs index cf506b0dd63..687f439074d 100644 --- a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs @@ -89,7 +89,10 @@ public void SetState(string key, string value) } } - // ... + private void ExpensiveWork1() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250)); + private void ExpensiveWork2() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250)); + private void ExpensiveWork3() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250)); + private void ExpensiveWork4() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250)); public void DoWork() { @@ -165,5 +168,34 @@ public void DumpComponentState() } } // + + //
+ class Program + { + static void Main(string[] args) + { + Console.WriteLine($"PID: {System.Diagnostics.Process.GetCurrentProcess().Id}"); + + long i = 0; + while (true) + { + using ComplexComponent c1 = new($"COMPONENT_{i++}"); + using ComplexComponent c2 = new($"COMPONENT_{i++}"); + using ComplexComponent c3 = new($"COMPONENT_{i++}"); + using ComplexComponent c4 = new($"COMPONENT_{i++}"); + + c1.SetState("key1", "value1"); + c2.SetState("key2", "value2"); + c3.SetState("key3", "value3"); + c4.SetState("key4", "value4"); + + c1.DoWork(); + c2.DoWork(); + c3.DoWork(); + c4.DoWork(); + } + } + } + //
} // \ No newline at end of file From cbf84de5f9a40aa3bac9c40e09398ef689c2f6ff Mon Sep 17 00:00:00 2001 From: John Salem Date: Thu, 2 Sep 2021 17:03:02 -0700 Subject: [PATCH 3/9] fix a couple xrefs --- xml/System.Diagnostics.Tracing/EventSource.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xml/System.Diagnostics.Tracing/EventSource.xml b/xml/System.Diagnostics.Tracing/EventSource.xml index 48dfe31b0e3..4f89cb6a356 100644 --- a/xml/System.Diagnostics.Tracing/EventSource.xml +++ b/xml/System.Diagnostics.Tracing/EventSource.xml @@ -73,11 +73,11 @@ ## Self Describing (Tracelogging) vs Manifest Event Formats can be configured into to two different modes based on the constructor used or what flags are set on . - Historically, these two formats are derived from two formats Event Tracing for Windows (ETW) used. These two modes generate the metadata for events differently. + Historically, these two formats are derived from two formats Event Tracing for Windows (ETW) used. While these two modes don't affect your ability to use Event Tracing for Windows (ETW) or EventPipe based listeners, they do generate the metadata for events differently. - The default event format is . Manifest-based s will generate an xml document representing the events defined on the class upon intialization. This requires the to reflect over itself to generate the provider and event metadata. + The default event format is `EtwManifestEventFormat` which is set if not specified on . Manifest-based s will generate an xml document representing the events defined on the class upon intialization. This requires the to reflect over itself to generate the provider and event metadata. - To use Self Describing (Tracelogging) event format, construct your using the constructor, constructor, or setting the flag. Self Describing sources generate minimnal provider metadata on initialization and only generate event metadata when is called. + To use Self Describing (Tracelogging) event format, construct your using the constructor, constructor, or setting the `EtwSelfDescribingEventFormat` flag on . Self Describing sources generate minimnal provider metadata on initialization and only generate event metadata when is called. In practice, these event format settings only affect usage with Event Tracing for Windows (ETW) based readers. They can, however, have a small effect on initialization time and per-event write times due to the time requried for reflection and generating the metadata. From 528c95c49a9e6d1989379c4a98f609e4a5ca565f Mon Sep 17 00:00:00 2001 From: John Salem Date: Fri, 3 Sep 2021 14:04:40 -0700 Subject: [PATCH 4/9] Apply suggestions from code review Co-authored-by: Genevieve Warren <24882762+gewarren@users.noreply.github.com> --- .../EventSource.xml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/xml/System.Diagnostics.Tracing/EventSource.xml b/xml/System.Diagnostics.Tracing/EventSource.xml index 4f89cb6a356..5b77254bd8d 100644 --- a/xml/System.Diagnostics.Tracing/EventSource.xml +++ b/xml/System.Diagnostics.Tracing/EventSource.xml @@ -56,30 +56,30 @@ ## Remarks This class is intended to be inherited by a user class that provides specific events to be used for event tracing. The methods are called to log the events. - The basic functionality of is sufficient for most applications. If you want more control over the event metadata that is created, you can apply the attribute to the methods. For advanced event source applications, it is possible to intercept the commands being sent to the derived event source and change the filtering, or to cause actions (such as dumping a data structure) to be performed by the inheritor. An event source can be activated in-process using and out-of-process using tools EVentPipe based tools such as `dotnet-trace` or Event Tracing for Windows (ETW) based tools like `PerfView` or `Logman`. It is also possible to programmatically control and intercept the data dispatcher. The class provides additional functionality. + The basic functionality of is sufficient for most applications. If you want more control over the event metadata that's created, you can apply the attribute to the methods. For advanced event source applications, it is possible to intercept the commands being sent to the derived event source and change the filtering, or to cause actions (such as dumping a data structure) to be performed by the inheritor. An event source can be activated in-process using and out-of-process using EventPipe-based tools such as `dotnet-trace` or Event Tracing for Windows (ETW) based tools like `PerfView` or `Logman`. It is also possible to programmatically control and intercept the data dispatcher. The class provides additional functionality. ## Conventions - derived classes should follow the following conventions: + -derived classes should follow the following conventions: - - User defined classes should implement a singleton pattern. The singleton instance is traditionally named `Log`. By extension, users should not call `IDisposable.Dispose` manually and allow the runtime to clean up the singleton instance at the end of managed code execution. - - User defined, derived class should be marked as `sealed` unless implementing the advanced "Utility EventSource" configuration discussed below. - - Users should call before performing any resource intensive work related to firing an event. - - Users can implicitly create s by declaring two event methods with subsequent event IDs that have the naming pattern `Start` and `Stop`. Note that these events _must_ declared next to each other in the class definition and the `Start` method _must_ come first. - - User should attempt to keep their s backwards compatible and appropriately version them. The default version for an event is `0`. The version can be can be changed by setting . Users should change the version of an event whenever they change properites of the payload. New payload properties should always be added to the end of the event declaration. If this is not possible, a new event with a new ID should be created to replace the old one. - - Users should specify fixed size payload properties before variably sized properties when declaring event methods. - - are used as a bitmask for specifying specific events when subscribing to a provider. Users can specify keywords by defining a `public static class Keywords` member class that has `public const EventKeywords` members. - - Expensive events should be associated with a using . This allows users of your to opt out of these expensive operations. + - User-defined classes should implement a singleton pattern. The singleton instance is traditionally named `Log`. By extension, users should not call `IDisposable.Dispose` manually and allow the runtime to clean up the singleton instance at the end of managed code execution. + - A user-defined, derived class should be marked as `sealed` unless it implements the advanced "Utility EventSource" configuration discussed below. + - Call before performing any resource intensive work related to firing an event. + - You can implicitly create objects by declaring two event methods with subsequent event IDs that have the naming pattern `Start` and `Stop`. These events _must_ be declared next to each other in the class definition and the `Start` method _must_ come first. + - Attempt to keep objects backwards compatible and version them appropriately. The default version for an event is `0`. The version can be can be changed by setting . Change the version of an event whenever you change properties of the payload. Always add new payload properties to the end of the event declaration. If this is not possible, create a new event with a new ID to replace the old one. + - When declaring event methods, specify fixed-size payload properties before variably sized properties. + - are used as a bit mask for specifying specific events when subscribing to a provider. You can specify keywords by defining a `public static class Keywords` member class that has `public const EventKeywords` members. + - Associate expensive events with an using . This pattern allows users of your to opt out of these expensive operations. -## Self Describing (Tracelogging) vs Manifest Event Formats +## Self-describing (tracelogging) vs. manifest event formats can be configured into to two different modes based on the constructor used or what flags are set on . - Historically, these two formats are derived from two formats Event Tracing for Windows (ETW) used. While these two modes don't affect your ability to use Event Tracing for Windows (ETW) or EventPipe based listeners, they do generate the metadata for events differently. + Historically, these two formats are derived from two formats that Event Tracing for Windows (ETW) used. While these two modes don't affect your ability to use Event Tracing for Windows (ETW) or EventPipe-based listeners, they do generate the metadata for events differently. - The default event format is `EtwManifestEventFormat` which is set if not specified on . Manifest-based s will generate an xml document representing the events defined on the class upon intialization. This requires the to reflect over itself to generate the provider and event metadata. + The default event format is , which is set if not specified on . Manifest-based objects generate an XML document representing the events defined on the class upon initialization. This requires the to reflect over itself to generate the provider and event metadata. - To use Self Describing (Tracelogging) event format, construct your using the constructor, constructor, or setting the `EtwSelfDescribingEventFormat` flag on . Self Describing sources generate minimnal provider metadata on initialization and only generate event metadata when is called. + To use self-describing (tracelogging) event format, construct your using the constructor, the constructor, or by setting the `EtwSelfDescribingEventFormat` flag on . Self-describing sources generate minimal provider metadata on initialization and only generate event metadata when is called. - In practice, these event format settings only affect usage with Event Tracing for Windows (ETW) based readers. They can, however, have a small effect on initialization time and per-event write times due to the time requried for reflection and generating the metadata. + In practice, these event format settings only affect usage with readers based on Event Tracing for Windows (ETW). They can, however, have a small effect on initialization time and per-event write times due to the time required for reflection and generating the metadata. ## Examples The following example shows a simple implementation of the class. @@ -94,13 +94,13 @@ ## Advanced Usage - Traditionally, user defined s expect to inherit directly from . For advanced scenarios, however, users can create `abstract` s, called a Utility Source, and implement interfaces. Using one or both of these techniques allows users to share code between different derived sources. + Traditionally, user-defined objects expect to inherit directly from . For advanced scenarios, however, you can create `abstract` objects, called *Utility Sources*, and implement interfaces. Using one or both of these techniques allows you to share code between different derived sources. > [!IMPORTANT] -> Abstract s cannot define keywords, tasks, opcodes, channels, or events. +> Abstract objects cannot define keywords, tasks, opcodes, channels, or events. > [!IMPORTANT] -> When using interfaces with , do not explicitly implement interface methods to avoid name collisions at runtime when generating event metadata. +> To avoid name collisions at run time when generating event metadata, don't explicitly implement interface methods when using interfaces with . The following example shows an implementation of that uses an interface. From 4e2a07407235ce5251c45c1d6e294e20299d8359 Mon Sep 17 00:00:00 2001 From: John Salem Date: Fri, 3 Sep 2021 14:06:57 -0700 Subject: [PATCH 5/9] Add proj files for samples --- .../csharp/VS_Snippets_CLR/etwtrace/cs/etwtrace.csproj | 8 ++++++++ .../VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj | 8 ++++++++ .../VS_Snippets_CLR/etwtracesmall/cs/etwtracesmall.csproj | 8 ++++++++ 3 files changed, 24 insertions(+) create mode 100644 samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/etwtrace.csproj create mode 100644 samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj create mode 100644 samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/etwtracesmall.csproj diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/etwtrace.csproj b/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/etwtrace.csproj new file mode 100644 index 00000000000..0bcc5100df7 --- /dev/null +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtrace/cs/etwtrace.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp5.0 + + + \ No newline at end of file diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj new file mode 100644 index 00000000000..0bcc5100df7 --- /dev/null +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp5.0 + + + \ No newline at end of file diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/etwtracesmall.csproj b/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/etwtracesmall.csproj new file mode 100644 index 00000000000..0bcc5100df7 --- /dev/null +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/etwtracesmall.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp5.0 + + + \ No newline at end of file From f81b06c50fa0bd538b85d475c1a5868fd66927c7 Mon Sep 17 00:00:00 2001 From: John Salem Date: Fri, 3 Sep 2021 14:45:19 -0700 Subject: [PATCH 6/9] Fix typo in sample --- .../csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs index 687f439074d..cc6e8251977 100644 --- a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs @@ -43,11 +43,11 @@ protected unsafe void WriteEvent(int eventId, int arg1, short arg2, long arg3) { EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 4 + descrs[0].Size = 4; descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[2].Size = 2 + descrs[2].Size = 2; descrs[1].DataPointer = (IntPtr)(&arg3); - descrs[2].Size = 8 + descrs[2].Size = 8; WriteEventCore(eventId, 3, descrs); } } From 0ade23ddf3165303abc8c5b1323d5da0f75f321a Mon Sep 17 00:00:00 2001 From: John Salem Date: Fri, 3 Sep 2021 14:53:56 -0700 Subject: [PATCH 7/9] Fix compiler errors for snippets --- .../csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs | 3 ++- .../csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs index cc6e8251977..494699cb04c 100644 --- a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs @@ -1,5 +1,6 @@ // using System; +using System.Collections.Generic; using System.Diagnostics.Tracing; namespace Demo @@ -62,7 +63,7 @@ public static class Keywords public const EventKeywords Kwd1 = (EventKeywords)1; } - [Event(1, Keywords = Keywords.kwd1, Level = EventLevel.Informational, Message = "LogElements called {0}/{1}/{2}.")] + [Event(1, Keywords = Keywords.Kwd1, Level = EventLevel.Informational, Message = "LogElements called {0}/{1}/{2}.")] public void LogElements(int n, short sh, long l) => WriteEvent(1, n, sh, l); // uses the overload we added! } // diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj index 0bcc5100df7..21e49aa943d 100644 --- a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/etwtracelarge.csproj @@ -3,6 +3,7 @@ Exe netcoreapp5.0 + true \ No newline at end of file From 77b00cec5100dd99fd40531bc545292605338503 Mon Sep 17 00:00:00 2001 From: John Salem Date: Tue, 7 Sep 2021 09:48:22 -0700 Subject: [PATCH 8/9] Update sample to use new on stackalloc'd structs to ensure unset values are 0'd --- .../csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs | 9 +++------ xml/System.Diagnostics.Tracing/EventSource.xml | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs index 494699cb04c..5c05d4fd43b 100644 --- a/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs +++ b/samples/snippets/csharp/VS_Snippets_CLR/etwtracelarge/cs/program.cs @@ -43,12 +43,9 @@ protected unsafe void WriteEvent(int eventId, int arg1, short arg2, long arg3) if (IsEnabled()) { EventSource.EventData* descrs = stackalloc EventSource.EventData[3]; - descrs[0].DataPointer = (IntPtr)(&arg1); - descrs[0].Size = 4; - descrs[1].DataPointer = (IntPtr)(&arg2); - descrs[2].Size = 2; - descrs[1].DataPointer = (IntPtr)(&arg3); - descrs[2].Size = 8; + descrs[0] = new EventData { DataPointer = (IntPtr)(&arg1), Size = 4 }; + descrs[1] = new EventData { DataPointer = (IntPtr)(&arg2), Size = 2 }; + descrs[2] = new EventData { DataPointer = (IntPtr)(&arg3), Size = 8 }; WriteEventCore(eventId, 3, descrs); } } diff --git a/xml/System.Diagnostics.Tracing/EventSource.xml b/xml/System.Diagnostics.Tracing/EventSource.xml index 5b77254bd8d..5ece82b63f1 100644 --- a/xml/System.Diagnostics.Tracing/EventSource.xml +++ b/xml/System.Diagnostics.Tracing/EventSource.xml @@ -62,7 +62,7 @@ -derived classes should follow the following conventions: - User-defined classes should implement a singleton pattern. The singleton instance is traditionally named `Log`. By extension, users should not call `IDisposable.Dispose` manually and allow the runtime to clean up the singleton instance at the end of managed code execution. - - A user-defined, derived class should be marked as `sealed` unless it implements the advanced "Utility EventSource" configuration discussed below. + - A user-defined, derived class should be marked as `sealed` unless it implements the advanced "Utility EventSource" configuration discussed in the Advanced Usage section. - Call before performing any resource intensive work related to firing an event. - You can implicitly create objects by declaring two event methods with subsequent event IDs that have the naming pattern `Start` and `Stop`. These events _must_ be declared next to each other in the class definition and the `Start` method _must_ come first. - Attempt to keep objects backwards compatible and version them appropriately. The default version for an event is `0`. The version can be can be changed by setting . Change the version of an event whenever you change properties of the payload. Always add new payload properties to the end of the event declaration. If this is not possible, create a new event with a new ID to replace the old one. From 304e7e9cfcd1ef99f6fdb3a5c7343da288bae2a6 Mon Sep 17 00:00:00 2001 From: John Salem Date: Tue, 7 Sep 2021 16:47:26 -0700 Subject: [PATCH 9/9] Use H3 instead of H2 --- xml/System.Diagnostics.Tracing/EventSource.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xml/System.Diagnostics.Tracing/EventSource.xml b/xml/System.Diagnostics.Tracing/EventSource.xml index 5ece82b63f1..49962525ce1 100644 --- a/xml/System.Diagnostics.Tracing/EventSource.xml +++ b/xml/System.Diagnostics.Tracing/EventSource.xml @@ -58,7 +58,7 @@ The basic functionality of is sufficient for most applications. If you want more control over the event metadata that's created, you can apply the attribute to the methods. For advanced event source applications, it is possible to intercept the commands being sent to the derived event source and change the filtering, or to cause actions (such as dumping a data structure) to be performed by the inheritor. An event source can be activated in-process using and out-of-process using EventPipe-based tools such as `dotnet-trace` or Event Tracing for Windows (ETW) based tools like `PerfView` or `Logman`. It is also possible to programmatically control and intercept the data dispatcher. The class provides additional functionality. -## Conventions +### Conventions -derived classes should follow the following conventions: - User-defined classes should implement a singleton pattern. The singleton instance is traditionally named `Log`. By extension, users should not call `IDisposable.Dispose` manually and allow the runtime to clean up the singleton instance at the end of managed code execution. @@ -70,7 +70,7 @@ - are used as a bit mask for specifying specific events when subscribing to a provider. You can specify keywords by defining a `public static class Keywords` member class that has `public const EventKeywords` members. - Associate expensive events with an using . This pattern allows users of your to opt out of these expensive operations. -## Self-describing (tracelogging) vs. manifest event formats +### Self-describing (tracelogging) vs. manifest event formats can be configured into to two different modes based on the constructor used or what flags are set on . Historically, these two formats are derived from two formats that Event Tracing for Windows (ETW) used. While these two modes don't affect your ability to use Event Tracing for Windows (ETW) or EventPipe-based listeners, they do generate the metadata for events differently. @@ -81,7 +81,7 @@ In practice, these event format settings only affect usage with readers based on Event Tracing for Windows (ETW). They can, however, have a small effect on initialization time and per-event write times due to the time required for reflection and generating the metadata. -## Examples +### Examples The following example shows a simple implementation of the class. :::code language="csharp" source="~/samples/snippets/csharp/VS_Snippets_CLR/etwtracesmall/cs/program.cs" id="Snippet1"::: @@ -93,7 +93,7 @@ :::code language="vb" source="~/samples/snippets/visualbasic/VS_Snippets_CLR/etwtrace/vb/program.vb" id="Snippet1"::: -## Advanced Usage +### Advanced Usage Traditionally, user-defined objects expect to inherit directly from . For advanced scenarios, however, you can create `abstract` objects, called *Utility Sources*, and implement interfaces. Using one or both of these techniques allows you to share code between different derived sources. > [!IMPORTANT]