Output Diagnostic Source to Event Source

7 minute read

At the beginning

Previously described DiagnosticSource, but DiagnosticSource only provides a mechanism for event occurrence and capture of that event, so how do you finally output it? Whether or not to do so is largely at the discretion of the user.

The standard method is to subscribe to DiagnosticListener.AllListeners and output it, but here I will describe how to output it through EventSource.

Please note that this article is based on .NET 5.0-rc1, so the specifications may change in future releases.

What is EventSource?

As mentioned in DiagnosticSource article, it is like a senior of DiagnosticSource and has existed since the .NET Framework era.
Originally it was a mechanism that got on ETW, but in order to make it cross-platform, a mechanism called EventPipe has been used from .NET Core 3.0, and it can be used without restrictions regardless of OS.

The advantage over DiagnosticSource is that it has a mechanism for collecting information from the outside, so it is possible to collect information later. (Example: dotnet-trace, Application Insights (Azure) etc.)

There is no document that the mechanism of EventPipe etc. is like this, but for individual IPC protocols etc. documentation of dotnet / diagnostics repository, So if you want to know more details, click here (it seems that you can connect via a named pipe on win and a unix domain socket on linux).
If you do your best, you can also make your own output mechanism.
This area is documents of client library of dotnet / diagnostics and Documentation about parser provided by perfview may be helpful. ..

DiagnosticSource cannot be collected directly, but DiagnosticSource has a function to output events with EventSource, so event information can be acquired through it.

Preparation

First, suppose you have the following Diagnostic Source

// using System.Diagnostics;
class C1
{
    public DiagnosticSource _D = new DiagnosticListener("Diag1");
    public void A()
    {
        if(_D.IsEnabled("ev1"))
        {
            _D.Write("ev1", new { X = "str" });
        }
    }
}

In this way, the event “ev1” will be fired when C1.A () is executed.

Then, create a process that executes this C1.A () on the ** app side of ** netcore app3.0 or later.

class Program
{
    static void Main(string[] args)
    {
        while(true)
        {
            //Once a second"ev1"Event occurs
            new C1().A();
            Task.Delay(1000).Wait();
        }
    }
}

This is all the preparation. No additional implementation is required on the app side.

Collection of events

All DiagnosticSource events are output from a provider called Microsoft-Diagnostics-DiagnosticSource (internally ʻEventSource.Name`).
Therefore, the collection tool side specifies this name as the EventPipe Provider and collects.

Parameters that can be specified at the start of monitoring include

  • Event level to monitor
  • Event keywords to monitor
  • Additional parameters

There is.
The event level is fixed at ʻEventLevel.Informational`.
Event keywords will be described later.
As an additional parameter, there is “FilterAndPayloadSpec” that filters events, etc., but this will be described later because the specifications are complicated.

Event keyword flag

A bit flag set for the event to be collected, with a hexadecimal number (up to 8 digits)
The following keyword flags exist in Microsoft-Diagnostics-DiagnosticSource

name Numerical value Overview
Messages 0x1 Other debug messages
Events 0x2 Event occurrence from Diagnostic Source or Activity Source
IgnoreShortCutKeywords 0x800 When true, ignore the following two keywords
AspNetCoreHosting 0x1000 ASP to collect.Add .NET Core related events
EntityFrameworkCoreCommands 0x2000 Add EFCore related events to the collection target

There is no problem if you include Events in the amount you normally use.

Events added by AspNetCoreHosting

  • Microsoft.AspNetCore/Microsoft.AspNetCore.Hosting.BeginRequest@Activity1Start
  • Microsoft.AspNetCore/Microsoft.AspNetCore.Hosting.EndRequest@Activity1Stop

See “About FilterAndPayloadSpec” below for this format.

Events added by EntityFrameworkCoreCommands

  • Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.BeforeExecuteCommand@Activity1Start
  • Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.AfterExecuteCommand@Activity1Stop

See “About FilterAndPayloadSpec” below for this format.

Additional parameters

So far only FilterAndPayloadSpec, but this is quite complicated. Unless otherwise specified, it means “capture all DiagnosticSource events, not ActivitySource”.

About FilterAndPayloadSpec

Set which event to monitor and how to interpret the data generated by the event.
Unless otherwise specified, it means “capture all DiagnosticSource events, not ActivitySource”.
This is a very basic one, so if you want to know more, you can use [Actual DiagnosticSourceEventSource Source](https://github.com/dotnet/runtime/blob/v5.0.0-rc.1.20451. It’s a good idea to check (14 / src / libraries / System.Diagnostics.DiagnosticSource / src / System / Diagnostics / DiagnosticSourceEventSource.cs).

Basic format

Specifies what events to capture and how to interpret the parameters of the captured events
In the case of DiagnosticSource, the format is [DiagnosticSource name] / [event name] @ [EventSource name]: [conversion destination parameter name] = [conversion source parameter name]; ] to specify.
It’s long to write everything, but all you need is the name of the Diagnostic Source.

Specifying Diagnostic Source

The one specified by [DiagnositicSource name] is included in the capture target. Use the name DiagnosticSource.Name here.

event name

Enter the name specified by DiagnosticSource.Write here.

The [EventSource name] part is a little difficult to understand, but by writing ʻActivity1Start etc., it means that the event is captured as ʻActivity1Start. If there is no description around here, all events are processed as event ID = 2, event name = " [Activity name]. [Start or Stop] ".

Describe this with ** CRLF delimiter ** for each Diagnostic Source.
So if you want to detect both Diag1 and Diag2,

Diag1<CRLF>
Diag2

Set the parameters as follows. If it is programmatic, it can be set with StringBuilder, but be careful if you want to specify it on the command line.

Also, the ActivitySource event cannot be captured by default, but if you want to add an ActivitySource event, add [AS] to the beginning of the same parameter.
At this time, if you specify [AS] * as a special notation, it is possible to capture all Activity Source events.
As an example, if you want to add an ActivitySource called ʻAct1`,

[AS]Act1

Describe as.

Event level

The event level of Microsoft-Diagnostics-Diagnostic Source introduced this time is fixed to Informational,
The following is a list of other things.

  • Critial = 1
  • Error = 2
  • Warning = 3
  • Informational = 4
  • Verbose = 5
  • LogAlways = 0

See the official documentation (https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventlevel) for the exact reference.

Events that occur in Microsoft-Diagnostics-DiagnosticSource

Event ID event name Event keywords Overview
1 Message Messages Other messages
2 Event Events DiagnosticSource.WriteEvents that occur in
3 EventJson Events Same as Event, but occurs only on net45 and event parameters are jsonized
4 Activity1Start Events DiagnosticSource.StartActivityEvent that occurs when
5 Activity1Stop Events DiagnosticSource.StopActivityEvent that occurs when
6 Activity2Start Events DiagnosticSource.StartActivityEvent that occurs when
7 Activity2Stop Events DiagnosticSource.StopActivityEvent that occurs when
8 RecursiveActivity1Start Events Events that occur when a nested Activity is started
9 RecursiveActivity1Stop Events Events that occur when a nested Activity ends
10 NewDiagnosticListener Events Events that occur when a DiagnosticListener is added
11 ActivityStart Events Event that occurs when Activity starts in ActivitySource
12 ActivityStop Events Event that occurs when Activity ends in ActivitySource

Collection by dotnet-trace

There are various ways to capture the output of EventSource, but here we will introduce the method by dotnet-trace.

Introduction of tools

It is provided as a dotnet tool, so you can install dotnet-sdk and install it with dotnet tool install -g dotnet-trace (you need to pass the path to $ HOME / .dotnet / tools)

Collection of events

Collect information as follows:

  1. Launch the app
  2. Get PID
    • In addition to OS commands (tasklist and ps), you can also get PID with dotnet-trace ps.
  3. dotnet-trace collect -p [PID] [Other options] --providers" Microsoft-Diagnostics-DiagnosticSource [Additional options] " to start collecting
    • By default, all event information of DiagnosticSource is output.
  4. Stop collecting with Ctrl-C or Enter

The above will generate a trace file that records event information (default is trace.net trace).
Note that ** Since there is no function to start monitoring as soon as the application is started, it may not be possible to acquire event logs at startup **

The format of the additional option is [event keyword]: [event level]: [additional parameter specified in K = V format]

How to pass to providers option

Since FilterAndPayloadSpecs includes line breaks, it may be difficult to pass a value depending on the shell.
Since dotnet-trace uses the framework of System.CommandLine for the application execution platform, it can be read using the response file. Yes](https://github.com/dotnet/command-line-api/blob/main/docs/Features-overview.md#Response-files).
First, prepare the following text file

--providers
Microsoft-Diagnostics-DiagnosticSource:[Event keywords]:[Event level]:FilterAndPayloadSpecs=[What you want to pass to FilterAndPayloadSpecs]

Line breaks can be represented by \ r \ n.
Once the file is ready, execute it as follows.

dotnet trace collect -p [PID] "@[Path to file]"

Viewing event information

Event information can be viewed at Perfview. Perfview is for Windows only, but it can also be converted to chromium trace file format (Chromium) or SpeedScope format, so you can view logs on Linux. However, note that the primary format is nettrace, so the other two formats may lack information.

At the end

In fact, I think that Diagnostic Source and Activity Source will be used indirectly through more user-friendly libraries such as opentelemetry rather than directly. But I think it’s good to have an ad hoc way to get information when something goes wrong.
Later, I personally think that ActivitySource should have a separate EventSource.

Reference article