// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Text;
using System.Threading;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;
namespace System.Diagnostics.Tests
{
//Complex types are not supported on EventSource for .NET 4.5
public class DiagnosticSourceEventSourceBridgeTests
{
// To avoid interactions between tests when they are run in parallel, we run all these tests in their
// own sub-process using RemoteExecutor.Invoke() However this makes it very inconvenient to debug the test.
// By seting this #if to true you stub out RemoteInvoke and the code will run in-proc which is useful
// in debugging.
#if false
class NullDispose : IDisposable
{
public void Dispose()
{
}
}
static IDisposable RemoteExecutor.Invoke(Action a)
{
a();
return new NullDispose();
}
#endif
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestEnableAllActivitySourcesAllEvents()
{
RemoteExecutor.Invoke(() =>
{
using TestDiagnosticSourceEventListener eventSourceListener = new TestDiagnosticSourceEventListener();
ActivitySource [] sources = new ActivitySource[10];
for (int i = 0; i < 10; i++)
{
sources[i] = new ActivitySource($"Source{i}");
}
int eventsCount = 0;
Assert.Equal(eventsCount, eventSourceListener.EventCount);
eventSourceListener.Enable(" [AS]* \r\n"); // All Sources + All Events
Activity [] activities = new Activity[10];
for (int i = 0; i < 10; i++)
{
activities[i] = sources[i].StartActivity($"activity{i}");
Assert.NotNull(activities[i]);
Assert.Equal(++eventsCount, eventSourceListener.EventCount);
ValidateActivityEvents(eventSourceListener, "ActivityStart", sources[i].Name, activities[i].OperationName);
Assert.True(activities[i].IsAllDataRequested);
Assert.Equal(ActivityTraceFlags.Recorded, activities[i].ActivityTraceFlags);
}
for (int i = 0; i < 10; i++)
{
activities[i].Dispose();
Assert.Equal(++eventsCount, eventSourceListener.EventCount);
ValidateActivityEvents(eventSourceListener, "ActivityStop", sources[i].Name, activities[i].OperationName);
sources[i].Dispose();
}
}).Dispose();
}
[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[InlineData("Start")]
[InlineData("Stop")]
[InlineData("start")]
[InlineData("stop")]
public void TestEnableAllActivitySourcesWithOneEvent(string eventName)
{
RemoteExecutor.Invoke((eventname) =>
{
using TestDiagnosticSourceEventListener eventSourceListener = new TestDiagnosticSourceEventListener();
ActivitySource [] sources = new ActivitySource[10];
for (int i = 0; i < 10; i++)
{
sources[i] = new ActivitySource($"Source{i}");
}
int eventsCount = 0;
Assert.Equal(eventsCount, eventSourceListener.EventCount);
eventSourceListener.Enable($" [AS]* / {eventname} \r\n"); // All Sources + one Event
Activity [] activities = new Activity[10];
for (int i = 0; i < 10; i++)
{
activities[i] = sources[i].StartActivity($"activity{i}");
Assert.NotNull(activities[i]);
if (eventname.Equals("Start", StringComparison.OrdinalIgnoreCase))
{
eventsCount++;
ValidateActivityEvents(eventSourceListener, "ActivityStart", sources[i].Name, activities[i].OperationName);
}
Assert.Equal(eventsCount, eventSourceListener.EventCount);
Assert.True(activities[i].IsAllDataRequested);
Assert.Equal(ActivityTraceFlags.Recorded, activities[i].ActivityTraceFlags);
}
for (int i = 0; i < 10; i++)
{
activities[i].Dispose();
if (eventname.Equals("Stop", StringComparison.OrdinalIgnoreCase))
{
eventsCount++;
ValidateActivityEvents(eventSourceListener, "ActivityStop", sources[i].Name, activities[i].OperationName);
}
Assert.Equal(eventsCount, eventSourceListener.EventCount);
sources[i].Dispose();
}
}, eventName).Dispose();
}
[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[InlineData("Propagate", false, ActivityTraceFlags.None)]
[InlineData("PROPAGATE", false, ActivityTraceFlags.None)]
[InlineData("Record", true, ActivityTraceFlags.None)]
[InlineData("recorD", true, ActivityTraceFlags.None)]
[InlineData("", true, ActivityTraceFlags.Recorded)]
public void TestEnableAllActivitySourcesWithSpeciifcSamplingResult(string samplingResult, bool alldataRequested, ActivityTraceFlags activityTraceFlags)
{
RemoteExecutor.Invoke((result, dataRequested, traceFlags) =>
{
using TestDiagnosticSourceEventListener eventSourceListener = new TestDiagnosticSourceEventListener();
using ActivitySource source = new ActivitySource("SamplingSource");
Assert.Equal(0, eventSourceListener.EventCount);
eventSourceListener.Enable($" [AS]* /- {result} \r\n"); // All Sources + one Event
Activity activity = source.StartActivity($"samplingActivity");
Assert.NotNull(activity);
Assert.Equal(1, eventSourceListener.EventCount);
ValidateActivityEvents(eventSourceListener, "ActivityStart", source.Name, activity.OperationName);
Assert.Equal(bool.Parse(dataRequested), activity.IsAllDataRequested);
Assert.Equal(traceFlags, activity.ActivityTraceFlags.ToString());
activity.Dispose();
Assert.Equal(2, eventSourceListener.EventCount);
ValidateActivityEvents(eventSourceListener, "ActivityStop", source.Name, activity.OperationName);
}, samplingResult, alldataRequested.ToString(), activityTraceFlags.ToString()).Dispose();
}
[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[InlineData("", "", true)]
[InlineData("Start", "", true)]
[InlineData("stop", "", true)]
[InlineData("", "Propagate", false)]
[InlineData("", "Record", true)]
[InlineData("Start", "Propagate", false)]
[InlineData("Stop", "Record", true)]
public void TestDefaultActivitySource(string eventName, string samplingResult, bool alldataRequested)
{
RemoteExecutor.Invoke((eventname, result, dataRequested) =>
{
using TestDiagnosticSourceEventListener eventSourceListener = new TestDiagnosticSourceEventListener();
Activity a = new Activity(""); // we need this to ensure DiagnosticSourceEventSource.Logger creation.
Assert.Equal(0, eventSourceListener.EventCount);
eventSourceListener.Enable($" [AS]/{eventname}-{result}\r\n");
Assert.Equal("", a.Source.Name);
a = a.Source.StartActivity("newOne");
Assert.Equal(bool.Parse(dataRequested), a.IsAllDataRequested);
// All Activities created with "new Activity(...)" will have ActivityTraceFlags is `None`;
Assert.Equal(result.Length == 0 ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None, a.ActivityTraceFlags);
a.Dispose();
int eCount = eventname.Length == 0 ? 2 : 1;
Assert.Equal(eCount, eventSourceListener.EventCount);
// None Default Source
ActivitySource source = new ActivitySource("NoneDefault"); // not the default ActivitySource
Activity activity = source.StartActivity($"ActivityFromNoneDefault"); // Shouldn't fire any event
Assert.Equal(eCount, eventSourceListener.EventCount);
Assert.Null(activity);
}, eventName, samplingResult, alldataRequested.ToString()).Dispose();
}
[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[InlineData("[AS]*\r\n [AS]Specific/-Propagate\r\n [AS]*/-Propagate\r\n", false, true)]
[InlineData("[AS]AnotherSource/-Propagate\r\n [AS]*/-Propagate\r\n [AS]Specific/-Record\r\n [AS]*\r\n", true, true)]
[InlineData("[AS]*/-Propagate\r\n [AS]*/-Propagate\r\n [AS]Specific/-Propagate\r\n[AS]Specific/-Record\r\n", true, false)]
[InlineData("[AS]*/-Propagate\r\n", false, false)]
[InlineData("[AS]*/-Record\r\n", true, true)]
[InlineData("[AS]Specific/-Propagate\r\n [AS]NoneSpecific/-Record\r\n", false, true)]
[InlineData("[AS]Specific/-Record\r\n [AS]NoneSpecific/-Propagate\r\n", true, false)]
[InlineData("[AS]Specific\r\n [AS]NoneSpecific\r\n", true, true)]
public void TestMultipleSpecs(string spec, bool isAlldataRequestedFromSpecif, bool alldataRequestedFromNoneSpecific)
{
RemoteExecutor.Invoke((specString, specificAllData, noneSpecificAllData) =>
{
using TestDiagnosticSourceEventListener eventSourceListener = new TestDiagnosticSourceEventListener();
using ActivitySource aSource1 = new ActivitySource("Specific");
eventSourceListener.Enable(specString);
Assert.Equal(0, eventSourceListener.EventCount);
Activity a1 = aSource1.StartActivity("a1");
Assert.NotNull(a1);
Assert.Equal(1, eventSourceListener.EventCount);
Assert.Equal(bool.Parse(specificAllData), a1.IsAllDataRequested);
a1.Dispose();
Assert.Equal(2, eventSourceListener.EventCount);
using ActivitySource aSource2 = new ActivitySource("NoneSpecific");
Activity a2 = aSource2.StartActivity("a2");
Assert.NotNull(a2);
Assert.Equal(3, eventSourceListener.EventCount);
Assert.Equal(bool.Parse(noneSpecificAllData), a2.IsAllDataRequested);
a2.Dispose();
Assert.Equal(4, eventSourceListener.EventCount);
}, spec, isAlldataRequestedFromSpecif.ToString(), alldataRequestedFromNoneSpecific.ToString()).Dispose();
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestTransformSpecs()
{
RemoteExecutor.Invoke(() =>
{
using (TestDiagnosticSourceEventListener eventSourceListener = new TestDiagnosticSourceEventListener())
{
using ActivitySource aSource1 = new ActivitySource("TransSpecsSource");
eventSourceListener.Enable("[AS]*");
Activity a = aSource1.StartActivity("TransSpecs");
ValidateActivityEvents(eventSourceListener, "ActivityStart", "TransSpecsSource", "TransSpecs");
var list1 = eventSourceListener.LastEvent.Arguments;
Assert.Equal(a.OperationName, list1["OperationName"]);
a.Dispose();
ValidateActivityEvents(eventSourceListener, "ActivityStop", "TransSpecsSource", "TransSpecs");
var list2 = eventSourceListener.LastEvent.Arguments;
Assert.Equal(a.OperationName, list2["OperationName"]);
Assert.Equal(list1.Count, list2.Count);
foreach (string key in list1.Keys)
{
Assert.NotNull(list2[key]);
}
// Suppress all
eventSourceListener.Enable("[AS]*:-");
a = aSource1.StartActivity("TransSpecs");
Assert.Equal(0, eventSourceListener.LastEvent.Arguments.Count);
a.Stop();
Assert.Equal(0, eventSourceListener.LastEvent.Arguments.Count);
// Suppress all except ActivitySource name
eventSourceListener.Enable("[AS]*:-ActivitySourceName=Source.Name");
a = aSource1.StartActivity("TransSpecs");
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(aSource1.Name, eventSourceListener.LastEvent.Arguments["ActivitySourceName"]);
a.Stop();
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(aSource1.Name, eventSourceListener.LastEvent.Arguments["ActivitySourceName"]);
// Collect TraceId, SpanId, and ParentSpanId only
eventSourceListener.Enable("[AS]*:-TraceId;SpanId;ParentSpanId");
a = aSource1.StartActivity("ActivityData");
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(a.SpanId.ToString(), eventSourceListener.LastEvent.Arguments["SpanId"]);
Assert.Equal(a.TraceId.ToString(), eventSourceListener.LastEvent.Arguments["TraceId"]);
Assert.Equal(a.ParentSpanId.ToString(), eventSourceListener.LastEvent.Arguments["ParentSpanId"]);
a.Stop();
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(a.SpanId.ToString(), eventSourceListener.LastEvent.Arguments["SpanId"]);
Assert.Equal(a.TraceId.ToString(), eventSourceListener.LastEvent.Arguments["TraceId"]);
Assert.Equal(a.ParentSpanId.ToString(), eventSourceListener.LastEvent.Arguments["ParentSpanId"]);
}
}).Dispose();
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestFilteringWithActivityName()
{
RemoteExecutor.Invoke(() =>
{
using (TestDiagnosticSourceEventListener eventSourceListener = new TestDiagnosticSourceEventListener())
{
using ActivitySource aSource1 = new ActivitySource("Source1");
using ActivitySource aSource2 = new ActivitySource("Source2");
eventSourceListener.Enable("[AS]*+MyActivity");
Assert.Equal(0, eventSourceListener.EventCount);
Activity a = aSource1.StartActivity("NotMyActivity"); // Shouldn't get created because nt matching MyActivity
Assert.Null(a);
Assert.Equal(0, eventSourceListener.EventCount);
a = aSource1.StartActivity("MyActivity");
Assert.NotNull(a);
Assert.Equal(1, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
a.Stop();
Assert.Equal(2, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
a = aSource2.StartActivity("MyActivity");
Assert.NotNull(a);
Assert.Equal(3, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
a.Stop();
Assert.Equal(4, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
//
// Now test with specific ActivitySource and ActivityName
//
eventSourceListener.Enable("[AS]MySource+MyActivity");
a = aSource1.StartActivity("MyActivity"); // Not from MySource
Assert.Null(a);
Assert.Equal(4, eventSourceListener.EventCount);
using ActivitySource aSource3 = new ActivitySource("MySource");
a = aSource3.StartActivity("NotMyActivity"); // from MySource but NoMyActivity
Assert.Null(a);
Assert.Equal(4, eventSourceListener.EventCount);
a = aSource3.StartActivity("MyActivity"); // from MySource and MyActivity
Assert.NotNull(a);
Assert.Equal(5, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
a.Stop();
Assert.Equal(6, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
//
// test with full query
//
eventSourceListener.Enable("[AS]MySource+MyActivity/Start-Propagate");
a = aSource3.StartActivity("MyActivity"); // from MySource and MyActivity
Assert.NotNull(a);
Assert.Equal(7, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
Assert.False(a.IsAllDataRequested);
a.Stop(); // shouldn't fire
Assert.Equal(7, eventSourceListener.EventCount);
//
// test with default Source
//
eventSourceListener.Enable("[AS]+MyActivity");
a = new Activity("MyActivity");
a.Start();
Assert.Equal(8, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
Assert.True(a.IsAllDataRequested);
a.Stop();
Assert.Equal(9, eventSourceListener.EventCount);
Assert.Equal(a.OperationName, eventSourceListener.LastEvent.Arguments["OperationName"]);
a = new Activity("NotMyActivity");
a.Start(); // nothing fire
Assert.Equal(9, eventSourceListener.EventCount);
//
// Test with Empty ActivityName
//
eventSourceListener.Enable("[AS]+");
a = new Activity("");
a.Start();
Assert.Equal(10, eventSourceListener.EventCount);
a.Stop();
Assert.Equal(11, eventSourceListener.EventCount);
a = aSource3.StartActivity("");
Assert.Null(a);
}
}).Dispose();
}
internal void ValidateActivityEvents(TestDiagnosticSourceEventListener eventSourceListener, string eventName, string sourceName, string activityName)
{
Assert.Equal(eventName, eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal(sourceName, eventSourceListener.LastEvent.SourceName);
Assert.Equal(activityName, eventSourceListener.LastEvent.EventName);
}
///
/// Tests the basic functionality of turning on specific EventSources and specifying
/// the events you want.
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestSpecificEvents()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestSpecificEventsSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
// Turn on events with both implicit and explicit types You can have whitespace
// before and after each spec.
eventSourceListener.Enable(
" TestSpecificEventsSource/TestEvent1:cls_Point_X=cls.Point.X;cls_Point_Y=cls.Point.Y\r\n" +
" TestSpecificEventsSource/TestEvent2:cls_Url=cls.Url\r\n"
);
/***************************************************************************************/
// Emit an event that matches the first pattern.
MyClass val = new MyClass() { Url = "MyUrl", Point = new MyPoint() { X = 3, Y = 5 } };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr = "hi", propInt = 4, cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestSpecificEventsSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(5, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("hi", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("4", eventSourceListener.LastEvent.Arguments["propInt"]);
Assert.Equal("3", eventSourceListener.LastEvent.Arguments["cls_Point_X"]);
Assert.Equal("5", eventSourceListener.LastEvent.Arguments["cls_Point_Y"]);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an event that matches the second pattern.
if (diagnosticSourceListener.IsEnabled("TestEvent2"))
diagnosticSourceListener.Write("TestEvent2", new { prop2Str = "hello", prop2Int = 8, cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestSpecificEventsSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent2", eventSourceListener.LastEvent.EventName);
Assert.Equal(4, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("hello", eventSourceListener.LastEvent.Arguments["prop2Str"]);
Assert.Equal("8", eventSourceListener.LastEvent.Arguments["prop2Int"]);
Assert.Equal("MyUrl", eventSourceListener.LastEvent.Arguments["cls_Url"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Emit an event that does not match either pattern. (thus will be filtered out)
if (diagnosticSourceListener.IsEnabled("TestEvent3"))
diagnosticSourceListener.Write("TestEvent3", new { propStr = "prop3", });
Assert.Equal(0, eventSourceListener.EventCount); // No Event should be fired.
/***************************************************************************************/
// Emit an event from another diagnostic source with the same event name.
// It will be filtered out.
using (var diagnosticSourceListener2 = new DiagnosticListener("TestSpecificEventsSource2"))
{
if (diagnosticSourceListener2.IsEnabled("TestEvent1"))
diagnosticSourceListener2.Write("TestEvent1", new { propStr = "hi", propInt = 4, cls = val });
}
Assert.Equal(0, eventSourceListener.EventCount); // No Event should be fired.
// Disable all the listener and insure that no more events come through.
eventSourceListener.Disable();
diagnosticSourceListener.Write("TestEvent1", null);
diagnosticSourceListener.Write("TestEvent2", null);
Assert.Equal(0, eventSourceListener.EventCount); // No Event should be received.
}
// Make sure that there are no Diagnostic Listeners left over.
DiagnosticListener.AllListeners.Subscribe(DiagnosticSourceTest.MakeObserver(delegate (DiagnosticListener listen)
{
Assert.True(!listen.Name.StartsWith("BuildTestSource"));
}));
}).Dispose();
}
///
/// Test that things work properly for Linux newline conventions.
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void LinuxNewLineConventions()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("LinuxNewLineConventionsSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
// Turn on events with both implicit and explicit types You can have whitespace
// before and after each spec. Use \n rather than \r\n
eventSourceListener.Enable(
" LinuxNewLineConventionsSource/TestEvent1:-cls_Point_X=cls.Point.X\n" +
" LinuxNewLineConventionsSource/TestEvent2:-cls_Url=cls.Url\n"
);
/***************************************************************************************/
// Emit an event that matches the first pattern.
MyClass val = new MyClass() { Url = "MyUrl", Point = new MyPoint() { X = 3, Y = 5 } };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr = "hi", propInt = 4, cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("LinuxNewLineConventionsSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("3", eventSourceListener.LastEvent.Arguments["cls_Point_X"]);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an event that matches the second pattern.
if (diagnosticSourceListener.IsEnabled("TestEvent2"))
diagnosticSourceListener.Write("TestEvent2", new { prop2Str = "hello", prop2Int = 8, cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("LinuxNewLineConventionsSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent2", eventSourceListener.LastEvent.EventName);
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("MyUrl", eventSourceListener.LastEvent.Arguments["cls_Url"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Emit an event that does not match either pattern. (thus will be filtered out)
if (diagnosticSourceListener.IsEnabled("TestEvent3"))
diagnosticSourceListener.Write("TestEvent3", new { propStr = "prop3", });
Assert.Equal(0, eventSourceListener.EventCount); // No Event should be fired.
}
// Make sure that there are no Diagnostic Listeners left over.
DiagnosticListener.AllListeners.Subscribe(DiagnosticSourceTest.MakeObserver(delegate (DiagnosticListener listen)
{
Assert.True(!listen.Name.StartsWith("BuildTestSource"));
}));
}).Dispose();
}
///
/// Tests what happens when you wildcard the source name (empty string)
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestWildCardSourceName()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener1 = new DiagnosticListener("TestWildCardSourceName1"))
using (var diagnosticSourceListener2 = new DiagnosticListener("TestWildCardSourceName2"))
{
eventSourceListener.Filter = (DiagnosticSourceEvent evnt) => evnt.SourceName.StartsWith("TestWildCardSourceName");
// Turn On Everything. Note that because of concurrent testing, we may get other sources as well.
// but we filter them out because we set eventSourceListener.Filter.
eventSourceListener.Enable("");
Assert.True(diagnosticSourceListener1.IsEnabled("TestEvent1"));
Assert.True(diagnosticSourceListener1.IsEnabled("TestEvent2"));
Assert.True(diagnosticSourceListener2.IsEnabled("TestEvent1"));
Assert.True(diagnosticSourceListener2.IsEnabled("TestEvent2"));
Assert.Equal(0, eventSourceListener.EventCount);
diagnosticSourceListener1.Write("TestEvent1", new { prop111 = "prop111Val", prop112 = 112 });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardSourceName1", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(2, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("prop111Val", eventSourceListener.LastEvent.Arguments["prop111"]);
Assert.Equal("112", eventSourceListener.LastEvent.Arguments["prop112"]);
eventSourceListener.ResetEventCountAndLastEvent();
diagnosticSourceListener1.Write("TestEvent2", new { prop121 = "prop121Val", prop122 = 122 });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardSourceName1", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent2", eventSourceListener.LastEvent.EventName);
Assert.Equal(2, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("prop121Val", eventSourceListener.LastEvent.Arguments["prop121"]);
Assert.Equal("122", eventSourceListener.LastEvent.Arguments["prop122"]);
eventSourceListener.ResetEventCountAndLastEvent();
diagnosticSourceListener2.Write("TestEvent1", new { prop211 = "prop211Val", prop212 = 212 });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardSourceName2", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(2, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("prop211Val", eventSourceListener.LastEvent.Arguments["prop211"]);
Assert.Equal("212", eventSourceListener.LastEvent.Arguments["prop212"]);
eventSourceListener.ResetEventCountAndLastEvent();
diagnosticSourceListener2.Write("TestEvent2", new { prop221 = "prop221Val", prop222 = 122 });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardSourceName2", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent2", eventSourceListener.LastEvent.EventName);
Assert.Equal(2, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("prop221Val", eventSourceListener.LastEvent.Arguments["prop221"]);
Assert.Equal("122", eventSourceListener.LastEvent.Arguments["prop222"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}).Dispose();
}
///
/// Tests what happens when you wildcard event name (but not the source name)
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestWildCardEventName()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestWildCardEventNameSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
// Turn on events with both implicit and explicit types
eventSourceListener.Enable("TestWildCardEventNameSource");
/***************************************************************************************/
// Emit an event, check that all implicit properties are generated
MyClass val = new MyClass() { Url = "MyUrl", Point = new MyPoint() { X = 3, Y = 5 } };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr = "hi", propInt = 4, cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardEventNameSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("hi", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("4", eventSourceListener.LastEvent.Arguments["propInt"]);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit the same event, with a different set of implicit properties
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr2 = "hi2", cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardEventNameSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(2, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("hi2", eventSourceListener.LastEvent.Arguments["propStr2"]);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an event with the same schema as the first event. (uses first-event cache)
val = new MyClass() { };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr = "hiThere", propInt = 5, cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardEventNameSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("hiThere", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("5", eventSourceListener.LastEvent.Arguments["propInt"]);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an event with the same schema as the second event. (uses dictionary cache)
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr2 = "hi3", cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestWildCardEventNameSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(2, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("hi3", eventSourceListener.LastEvent.Arguments["propStr2"]);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an event from another diagnostic source with the same event name.
// It will be filtered out.
using (var diagnosticSourceListener2 = new DiagnosticListener("TestWildCardEventNameSource2"))
{
if (diagnosticSourceListener2.IsEnabled("TestEvent1"))
diagnosticSourceListener2.Write("TestEvent1", new { propStr = "hi", propInt = 4, cls = val });
}
Assert.Equal(0, eventSourceListener.EventCount); // No Event should be fired.
}
}).Dispose();
}
public class PropertyThrow
{
public string property1 => "P1";
public string property2 => throw new Exception("Always throw!");
public string property3 => "P3";
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestWithPropertyThrowing()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestThrows"))
{
eventSourceListener.Enable("TestThrows/TestEvent1");
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new PropertyThrow());
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestThrows", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("P1", eventSourceListener.LastEvent.Arguments["property1"]);
Assert.Equal("P3", eventSourceListener.LastEvent.Arguments["property3"]);
Assert.Equal("", eventSourceListener.LastEvent.Arguments["property2"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}).Dispose();
}
///
/// Test what happens when there are nulls passed in the event payloads
/// Basically strings get turned into empty strings and other nulls are typically
/// ignored.
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestNulls()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestNullsTestSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
// Turn on events with both implicit and explicit types
eventSourceListener.Enable("TestNullsTestSource/TestEvent1:cls.Url;cls_Point_X=cls.Point.X");
/***************************************************************************************/
// Emit a null arguments object.
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", null);
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestNullsTestSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(0, eventSourceListener.LastEvent.Arguments.Count);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an arguments object with nulls in it.
MyClass val = null;
string strVal = null;
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { cls = val, propStr = "propVal1", propStrNull = strVal });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestNullsTestSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("", eventSourceListener.LastEvent.Arguments["cls"]); // Tostring() on a null end up as an empty string.
Assert.Equal("propVal1", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("", eventSourceListener.LastEvent.Arguments["propStrNull"]); // null strings get turned into empty strings
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an arguments object that points at null things
MyClass val1 = new MyClass() { Url = "myUrlVal", Point = null };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { cls = val1, propStr = "propVal1" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestNullsTestSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val1.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("propVal1", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("myUrlVal", eventSourceListener.LastEvent.Arguments["Url"]);
eventSourceListener.ResetEventCountAndLastEvent();
/***************************************************************************************/
// Emit an arguments object that points at null things (variation 2)
MyClass val2 = new MyClass() { Url = null, Point = new MyPoint() { X = 8, Y = 9 } };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { cls = val2, propStr = "propVal1" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestNullsTestSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val2.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("propVal1", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("8", eventSourceListener.LastEvent.Arguments["cls_Point_X"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}).Dispose();
}
///
/// Tests the feature that suppresses the implicit inclusion of serialable properties
/// of the payload object.
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestNoImplicitTransforms()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestNoImplicitTransformsSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
// use the - prefix to suppress the implicit properties. Thus you should only get propStr and Url.
eventSourceListener.Enable("TestNoImplicitTransformsSource/TestEvent1:-propStr;cls.Url");
/***************************************************************************************/
// Emit an event that matches the first pattern.
MyClass val = new MyClass() { Url = "MyUrl", Point = new MyPoint() { X = 3, Y = 5 } };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr = "hi", propInt = 4, cls = val, propStr2 = "there" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestNoImplicitTransformsSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(2, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("hi", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("MyUrl", eventSourceListener.LastEvent.Arguments["Url"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}).Dispose();
}
///
/// Tests what happens when wacky characters are used in property specs.
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestBadProperties()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestBadPropertiesSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
// This has a syntax error in the Url case, so it should be ignored.
eventSourceListener.Enable("TestBadPropertiesSource/TestEvent1:cls.Ur-+l");
/***************************************************************************************/
// Emit an event that matches the first pattern.
MyClass val = new MyClass() { Url = "MyUrl", Point = new MyPoint() { X = 3, Y = 5 } };
if (diagnosticSourceListener.IsEnabled("TestEvent1"))
diagnosticSourceListener.Write("TestEvent1", new { propStr = "hi", propInt = 4, cls = val });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("TestBadPropertiesSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName);
Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal(val.GetType().FullName, eventSourceListener.LastEvent.Arguments["cls"]); // ToString on cls is the class name
Assert.Equal("hi", eventSourceListener.LastEvent.Arguments["propStr"]);
Assert.Equal("4", eventSourceListener.LastEvent.Arguments["propInt"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}).Dispose();
}
// Tests that messages about DiagnosticSourceEventSource make it out.
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestMessages()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestMessagesSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
// This is just to make debugging easier.
var messages = new List();
eventSourceListener.OtherEventWritten += delegate (EventWrittenEventArgs evnt)
{
if (evnt.EventName == "Message")
{
var message = (string)evnt.Payload[0];
messages.Add(message);
}
};
// This has a syntax error in the Url case, so it should be ignored.
eventSourceListener.Enable("TestMessagesSource/TestEvent1:-cls.Url");
Assert.Equal(0, eventSourceListener.EventCount);
Assert.True(3 <= messages.Count);
}
}).Dispose();
}
///
/// Tests the feature to send the messages as EventSource Activities.
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestActivities()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
using (var diagnosticSourceListener = new DiagnosticListener("TestActivitiesSource"))
{
Assert.Equal(0, eventSourceListener.EventCount);
eventSourceListener.Enable(
"TestActivitiesSource/TestActivity1Start@Activity1Start\r\n" +
"TestActivitiesSource/TestActivity1Stop@Activity1Stop\r\n" +
"TestActivitiesSource/TestActivity2Start@Activity2Start\r\n" +
"TestActivitiesSource/TestActivity2Stop@Activity2Stop\r\n" +
"TestActivitiesSource/TestEvent\r\n"
);
// Start activity 1
diagnosticSourceListener.Write("TestActivity1Start", new { propStr = "start" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity1Start", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("TestActivitiesSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestActivity1Start", eventSourceListener.LastEvent.EventName);
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("start", eventSourceListener.LastEvent.Arguments["propStr"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Start nested activity 2
diagnosticSourceListener.Write("TestActivity2Start", new { propStr = "start" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity2Start", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("TestActivitiesSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestActivity2Start", eventSourceListener.LastEvent.EventName);
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("start", eventSourceListener.LastEvent.Arguments["propStr"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Send a normal event
diagnosticSourceListener.Write("TestEvent", new { propStr = "event" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Event", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("TestActivitiesSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestEvent", eventSourceListener.LastEvent.EventName);
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("event", eventSourceListener.LastEvent.Arguments["propStr"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Stop nested activity 2
diagnosticSourceListener.Write("TestActivity2Stop", new { propStr = "stop" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity2Stop", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("TestActivitiesSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestActivity2Stop", eventSourceListener.LastEvent.EventName);
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("stop", eventSourceListener.LastEvent.Arguments["propStr"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Stop activity 1
diagnosticSourceListener.Write("TestActivity1Stop", new { propStr = "stop" });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity1Stop", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("TestActivitiesSource", eventSourceListener.LastEvent.SourceName);
Assert.Equal("TestActivity1Stop", eventSourceListener.LastEvent.EventName);
Assert.Equal(1, eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("stop", eventSourceListener.LastEvent.Arguments["propStr"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}).Dispose();
}
///
/// Tests that keywords that define shortcuts work.
///
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void TestShortcutKeywords()
{
RemoteExecutor.Invoke(() =>
{
using (var eventSourceListener = new TestDiagnosticSourceEventListener())
// These are look-alikes for the real ones.
using (var aspNetCoreSource = new DiagnosticListener("Microsoft.AspNetCore"))
using (var entityFrameworkCoreSource = new DiagnosticListener("Microsoft.EntityFrameworkCore"))
{
// Sadly we have a problem where if something else has turned on Microsoft-Diagnostics-DiagnosticSource then
// its keywords are ORed with our and because the shortcuts require that IgnoreShortCutKeywords is OFF
// Something outside this test (the debugger seems to do this), will cause the test to fail.
// Currently we simply give up in that case (but it really is a deeper problem.
var IgnoreShortCutKeywords = (EventKeywords)0x0800;
foreach (var eventSource in EventSource.GetSources())
{
if (eventSource.Name == "Microsoft-Diagnostics-DiagnosticSource")
{
if (eventSource.IsEnabled(EventLevel.Informational, IgnoreShortCutKeywords))
return; // Don't do the testing.
}
}
// These are from DiagnosticSourceEventListener.
var Messages = (EventKeywords)0x1;
var Events = (EventKeywords)0x2;
var AspNetCoreHosting = (EventKeywords)0x1000;
var EntityFrameworkCoreCommands = (EventKeywords)0x2000;
// Turn on listener using just the keywords
eventSourceListener.Enable(null, Messages | Events | AspNetCoreHosting | EntityFrameworkCoreCommands);
Assert.Equal(0, eventSourceListener.EventCount);
// Start a ASP.NET Request
aspNetCoreSource.Write("Microsoft.AspNetCore.Hosting.BeginRequest",
new
{
httpContext = new
{
Request = new
{
Method = "Get",
Host = "MyHost",
Path = "MyPath",
QueryString = "MyQuery"
}
}
});
// Check that the morphs work as expected.
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity1Start", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("Microsoft.AspNetCore", eventSourceListener.LastEvent.SourceName);
Assert.Equal("Microsoft.AspNetCore.Hosting.BeginRequest", eventSourceListener.LastEvent.EventName);
Assert.True(4 <= eventSourceListener.LastEvent.Arguments.Count);
Debug.WriteLine("Arg Keys = " + string.Join(" ", eventSourceListener.LastEvent.Arguments.Keys));
Debug.WriteLine("Arg Values = " + string.Join(" ", eventSourceListener.LastEvent.Arguments.Values));
Assert.Equal("Get", eventSourceListener.LastEvent.Arguments["Method"]);
Assert.Equal("MyHost", eventSourceListener.LastEvent.Arguments["Host"]);
Assert.Equal("MyPath", eventSourceListener.LastEvent.Arguments["Path"]);
Assert.Equal("MyQuery", eventSourceListener.LastEvent.Arguments["QueryString"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Start a SQL command
entityFrameworkCoreSource.Write("Microsoft.EntityFrameworkCore.BeforeExecuteCommand",
new
{
Command = new
{
Connection = new
{
DataSource = "MyDataSource",
Database = "MyDatabase",
},
CommandText = "MyCommand"
}
});
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity2Start", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("Microsoft.EntityFrameworkCore", eventSourceListener.LastEvent.SourceName);
Assert.Equal("Microsoft.EntityFrameworkCore.BeforeExecuteCommand", eventSourceListener.LastEvent.EventName);
Assert.True(3 <= eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("MyDataSource", eventSourceListener.LastEvent.Arguments["DataSource"]);
Assert.Equal("MyDatabase", eventSourceListener.LastEvent.Arguments["Database"]);
Assert.Equal("MyCommand", eventSourceListener.LastEvent.Arguments["CommandText"]);
eventSourceListener.ResetEventCountAndLastEvent();
// Stop the SQL command
entityFrameworkCoreSource.Write("Microsoft.EntityFrameworkCore.AfterExecuteCommand", null);
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity2Stop", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("Microsoft.EntityFrameworkCore", eventSourceListener.LastEvent.SourceName);
Assert.Equal("Microsoft.EntityFrameworkCore.AfterExecuteCommand", eventSourceListener.LastEvent.EventName);
eventSourceListener.ResetEventCountAndLastEvent();
// Stop the ASP.NET request.
aspNetCoreSource.Write("Microsoft.AspNetCore.Hosting.EndRequest",
new
{
httpContext = new
{
Response = new
{
StatusCode = "200"
},
TraceIdentifier = "MyTraceId"
}
});
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity1Stop", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("Microsoft.AspNetCore", eventSourceListener.LastEvent.SourceName);
Assert.Equal("Microsoft.AspNetCore.Hosting.EndRequest", eventSourceListener.LastEvent.EventName);
Assert.True(2 <= eventSourceListener.LastEvent.Arguments.Count);
Assert.Equal("MyTraceId", eventSourceListener.LastEvent.Arguments["TraceIdentifier"]);
Assert.Equal("200", eventSourceListener.LastEvent.Arguments["StatusCode"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}).Dispose();
}
[OuterLoop("Runs for several seconds")]
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void Stress_WriteConcurrently_DoesntCrash()
{
const int StressTimeSeconds = 4;
RemoteExecutor.Invoke(() =>
{
using (new TurnOnAllEventListener())
using (var source = new DiagnosticListener("testlistener"))
{
var ce = new CountdownEvent(Environment.ProcessorCount * 2);
for (int i = 0; i < ce.InitialCount; i++)
{
new Thread(() =>
{
DateTime end = DateTime.UtcNow.Add(TimeSpan.FromSeconds(StressTimeSeconds));
while (DateTime.UtcNow < end)
{
source.Write("event1", Tuple.Create(1));
source.Write("event2", Tuple.Create(1, 2));
source.Write("event3", Tuple.Create(1, 2, 3));
source.Write("event4", Tuple.Create(1, 2, 3, 4));
source.Write("event5", Tuple.Create(1, 2, 3, 4, 5));
}
ce.Signal();
})
{ IsBackground = true }.Start();
}
ce.Wait();
}
}).Dispose();
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void IndexGetters_DontThrow()
{
RemoteExecutor.Invoke(() =>
{
using (var eventListener = new TestDiagnosticSourceEventListener())
using (var diagnosticListener = new DiagnosticListener("MySource"))
{
eventListener.Enable(
"MySource/MyEvent"
);
// The type MyEvent only declares 3 Properties, but actually
// has 4 due to the implicit Item property from having the index
// operator implemented. The Getter for this Item property
// is unusual for Property getters because it takes
// an int32 as an input. This test ensures that this
// implicit Property isn't implicitly serialized by
// DiagnosticSourceEventSource.
diagnosticListener.Write(
"MyEvent",
new MyEvent
{
Number = 1,
OtherNumber = 2
}
);
Assert.Equal(1, eventListener.EventCount);
Assert.Equal("MySource", eventListener.LastEvent.SourceName);
Assert.Equal("MyEvent", eventListener.LastEvent.EventName);
Assert.True(eventListener.LastEvent.Arguments.Count <= 3);
Assert.Equal("1", eventListener.LastEvent.Arguments["Number"]);
Assert.Equal("2", eventListener.LastEvent.Arguments["OtherNumber"]);
Assert.Equal("2", eventListener.LastEvent.Arguments["Count"]);
}
}).Dispose();
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void ActivityObjectsAreInspectable()
{
RemoteExecutor.Invoke(() =>
{
using (var eventListener = new TestDiagnosticSourceEventListener())
using (var diagnosticListener = new DiagnosticListener("MySource"))
{
string activityProps =
"-DummyProp" +
";ActivityId=*Activity.Id" +
";ActivityStartTime=*Activity.StartTimeUtc.Ticks" +
";ActivityDuration=*Activity.Duration.Ticks" +
";ActivityOperationName=*Activity.OperationName" +
";ActivityIdFormat=*Activity.IdFormat" +
";ActivityParentId=*Activity.ParentId" +
";ActivityTags=*Activity.Tags.*Enumerate" +
";ActivityTraceId=*Activity.TraceId" +
";ActivitySpanId=*Activity.SpanId" +
";ActivityTraceStateString=*Activity.TraceStateString" +
";ActivityParentSpanId=*Activity.ParentSpanId";
eventListener.Enable(
"MySource/TestActivity1.Start@Activity1Start:" + activityProps + "\r\n" +
"MySource/TestActivity1.Stop@Activity1Stop:" + activityProps + "\r\n" +
"MySource/TestActivity2.Start@Activity2Start:" + activityProps + "\r\n" +
"MySource/TestActivity2.Stop@Activity2Stop:" + activityProps + "\r\n"
);
Activity activity1 = new Activity("TestActivity1");
activity1.SetIdFormat(ActivityIdFormat.W3C);
activity1.TraceStateString = "hi_there";
activity1.AddTag("one", "1");
activity1.AddTag("two", "2");
diagnosticListener.StartActivity(activity1, new { DummyProp = "val" });
Assert.Equal(1, eventListener.EventCount);
AssertActivityMatchesEvent(activity1, eventListener.LastEvent, isStart: true);
Activity activity2 = new Activity("TestActivity2");
diagnosticListener.StartActivity(activity2, new { DummyProp = "val" });
Assert.Equal(2, eventListener.EventCount);
AssertActivityMatchesEvent(activity2, eventListener.LastEvent, isStart: true);
diagnosticListener.StopActivity(activity2, new { DummyProp = "val" });
Assert.Equal(3, eventListener.EventCount);
AssertActivityMatchesEvent(activity2, eventListener.LastEvent, isStart: false);
diagnosticListener.StopActivity(activity1, new { DummyProp = "val" });
Assert.Equal(4, eventListener.EventCount);
AssertActivityMatchesEvent(activity1, eventListener.LastEvent, isStart: false);
}
}).Dispose();
}
private void AssertActivityMatchesEvent(Activity a, DiagnosticSourceEvent e, bool isStart)
{
Assert.Equal("MySource", e.SourceName);
Assert.Equal(a.OperationName + (isStart ? ".Start" : ".Stop"), e.EventName);
Assert.Equal("val", e.Arguments["DummyProp"]);
Assert.Equal(a.Id, e.Arguments["ActivityId"]);
Assert.Equal(a.StartTimeUtc.Ticks.ToString(), e.Arguments["ActivityStartTime"]);
if (!isStart)
{
Assert.Equal(a.Duration.Ticks.ToString(), e.Arguments["ActivityDuration"]);
}
Assert.Equal(a.OperationName, e.Arguments["ActivityOperationName"]);
if (a.ParentId == null)
{
Assert.True(!e.Arguments.ContainsKey("ActivityParentId"));
}
else
{
Assert.Equal(a.ParentId, e.Arguments["ActivityParentId"]);
}
Assert.Equal(a.IdFormat.ToString(), e.Arguments["ActivityIdFormat"]);
if (a.IdFormat == ActivityIdFormat.W3C)
{
Assert.Equal(a.TraceId.ToString(), e.Arguments["ActivityTraceId"]);
Assert.Equal(a.SpanId.ToString(), e.Arguments["ActivitySpanId"]);
Assert.Equal(a.TraceStateString, e.Arguments["ActivityTraceStateString"]);
if(a.ParentSpanId != default)
{
Assert.Equal(a.ParentSpanId.ToString(), e.Arguments["ActivityParentSpanId"]);
}
}
Assert.Equal(string.Join(',', a.Tags), e.Arguments["ActivityTags"]);
}
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/50924", TestPlatforms.Android)]
public void NoExceptionThrownWhenProcessingStaticActivityProperties()
{
// Ensures that no exception is thrown when static properties on the Activity type are passed to EventListener.
using (var eventListener = new TestDiagnosticSourceEventListener())
using (var diagnosticListener = new DiagnosticListener("MySource"))
{
string activityProps =
"-DummyProp" +
";ActivityEvents=*Activity.DefaultIdFormat" +
";ActivityBaggage=*Activity.Current" +
";ActivityContext=*Activity.ForceDefaultIdFormat";
eventListener.Enable(
"MySource/TestActivity1.Start@Activity1Start:" + activityProps + "\r\n" +
"MySource/TestActivity1.Stop@Activity1Stop:" + activityProps + "\r\n" +
"MySource/TestActivity2.Start@Activity2Start:" + activityProps + "\r\n" +
"MySource/TestActivity2.Stop@Activity2Stop:" + activityProps + "\r\n"
);
Activity activity1 = new Activity("TestActivity1");
activity1.SetIdFormat(ActivityIdFormat.W3C);
activity1.TraceStateString = "hi_there";
activity1.AddTag("one", "1");
activity1.AddTag("two", "2");
var obj = new { DummyProp = "val" };
diagnosticListener.StartActivity(activity1, obj);
Assert.Equal(1, eventListener.EventCount);
}
}
}
/****************************************************************************/
// classes to make up data for the tests.
///
/// classes for test data.
///
internal class MyClass
{
public string Url { get; set; }
public MyPoint Point { get; set; }
}
///
/// classes for test data.
///
internal class MyPoint
{
public int X { get; set; }
public int Y { get; set; }
}
///
/// classes for test data
///
internal class MyEvent
{
public int Number { get; set; }
public int OtherNumber { get; set; }
public int Count => 2;
public KeyValuePair this[int index] => index switch
{
0 => new KeyValuePair(nameof(Number), Number),
1 => new KeyValuePair(nameof(OtherNumber), OtherNumber),
_ => throw new IndexOutOfRangeException()
};
}
/****************************************************************************/
// Harness infrastructure
///
/// TestDiagnosticSourceEventListener installs a EventWritten callback that updates EventCount and LastEvent.
///
internal sealed class TestDiagnosticSourceEventListener : DiagnosticSourceEventListener
{
public TestDiagnosticSourceEventListener()
{
EventWritten += UpdateLastEvent;
}
public int EventCount;
public DiagnosticSourceEvent LastEvent;
// Here just for debugging. Lets you see the last 3 events that were sent.
public DiagnosticSourceEvent SecondLast;
public DiagnosticSourceEvent ThirdLast;
///
/// Sets the EventCount to 0 and LastEvents to null
///
public void ResetEventCountAndLastEvent()
{
EventCount = 0;
LastEvent = null;
SecondLast = null;
ThirdLast = null;
}
///
/// If present, will ignore events that don't cause this filter predicate to return true.
///
public Predicate Filter;
#region private
private void UpdateLastEvent(DiagnosticSourceEvent anEvent)
{
if (Filter != null && !Filter(anEvent))
return;
ThirdLast = SecondLast;
SecondLast = LastEvent;
EventCount++;
LastEvent = anEvent;
}
#endregion
}
///
/// Represents a single DiagnosticSource event.
///
internal sealed class DiagnosticSourceEvent
{
public string SourceName;
public string EventName;
public Dictionary Arguments;
// Not typically important.
public string EventSourceEventName; // This is the name of the EventSourceEvent that carried the data. Only important for activities.
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("{");
sb.Append(" SourceName: \"").Append(SourceName ?? "").Append("\",").AppendLine();
sb.Append(" EventName: \"").Append(EventName ?? "").Append("\",").AppendLine();
sb.Append(" Arguments: ").Append("[").AppendLine();
bool first = true;
foreach (var keyValue in Arguments)
{
if (!first)
sb.Append(",").AppendLine();
first = false;
sb.Append(" ").Append(keyValue.Key).Append(": \"").Append(keyValue.Value).Append("\"");
}
sb.AppendLine().AppendLine(" ]");
sb.AppendLine("}");
return sb.ToString();
}
}
///
/// A helper class that listens to Diagnostic sources and send events to the 'EventWritten'
/// callback. Standard use is to create the class set up the events of interested and
/// use 'Enable'. .
///
internal class DiagnosticSourceEventListener : EventListener
{
public DiagnosticSourceEventListener() { }
///
/// Will be called when a DiagnosticSource event is fired.
///
public new event Action EventWritten;
///
/// It is possible that there are other events besides those that are being forwarded from
/// the DiagnosticSources. These come here.
///
public event Action OtherEventWritten;
public void Enable(string filterAndPayloadSpecs, EventKeywords keywords = EventKeywords.All)
{
var args = new Dictionary();
if (filterAndPayloadSpecs != null)
args.Add("FilterAndPayloadSpecs", filterAndPayloadSpecs);
EnableEvents(_diagnosticSourceEventSource, EventLevel.Verbose, keywords, args);
}
public void Disable()
{
DisableEvents(_diagnosticSourceEventSource);
}
///
/// Cleans this class up. Among other things disables the DiagnosticSources being listened to.
///
public override void Dispose()
{
if (_diagnosticSourceEventSource != null)
{
Disable();
_diagnosticSourceEventSource = null;
}
}
#region private
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
bool wroteEvent = false;
var eventWritten = EventWritten;
if (eventWritten != null)
{
if (eventData.Payload.Count == 3 && (eventData.EventName == "Event" || eventData.EventName.Contains("Activity")))
{
Debug.Assert(eventData.PayloadNames[0] == "SourceName");
Debug.Assert(eventData.PayloadNames[1] == "EventName" || eventData.PayloadNames[1] == "ActivityName");
Debug.Assert(eventData.PayloadNames[2] == "Arguments");
var anEvent = new DiagnosticSourceEvent();
anEvent.EventSourceEventName = eventData.EventName;
anEvent.SourceName = eventData.Payload[0].ToString();
anEvent.EventName = eventData.Payload[1].ToString();
anEvent.Arguments = new Dictionary();
var asKeyValueList = eventData.Payload[2] as IEnumerable