using Diagnostics.Tracing;
using Diagnostics.Tracing.Parsers;
using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Threading;
using System.Threading.Tasks;
// This program is a simple demo, it has both a EventSource and a EventGenerator which generate events for 10 seconds
// as well as a 'listener; that listens for these events (potentially in a differnet process, but we don't do that here)
// and does some rudimentary processing (calculates the difference between two particular events.
namespace SimpleMonitor
{
using Microsoft.Cloud.InstrumentationFramework;
// This code belongs in the process generating the events. They are samples
[EventSource(Name = "Microsoft-Demos-SimpleMonitor")] // This is the name of my eventSource outside my program.
class MyEventSource : EventSource
{
// Notice that the bodies of the events follow a pattern: WriteEvent(ID, <args>) where ID is a unique ID
// Starting at 1 and giving each different event a unque one, and passing all the payload arguments on to be sent out.
public void MyFirstEvent(string MyName, int MyId) { WriteEvent(1, MyName, MyId); }
public void MySecondEvent(int MyId) { WriteEvent(2, MyId); }
public void Stop() { WriteEvent(3); }
// Typically you only create one EventSource and use it throughout your program. Thus a static field makes sense.
public static MyEventSource Log = new MyEventSource();
}
// This code belongs in the process generating the events. It is in this process for simplicity.
class EventGenerator
{
public static void CreateEvents()
{
// This just spanws a thread that generates events every second for 10 seconds than issues a Stop event.
Task.Factory.StartNew(delegate
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("** Generateing the MyFirst and MySecond events.");
MyEventSource.Log.MyFirstEvent("Some string", i);
Thread.Sleep(10);
MyEventSource.Log.MySecondEvent(i);
Thread.Sleep(1000);
}
//Console.WriteLine("** Generating the Stop Event.");
//MyEventSource.Log.Stop();
string monitoringAccountName = "TestAccount";
string monitoringNamespace = "TestNamespace";
string metricName = "TestMetric";
string[] dimensionNames = new[] {"Name"};
string[] dimensionValues = new[] {"Vivek"};
//ErrorContext errorContext = new ErrorContext();
//MeasureMetric measureMetric = MeasureMetric.Create(
// monitoringAccountName,
// monitoringNamespace,
// metricName,
// true,
// dimensionNames);
//measureMetric.LogValue(5, ref errorContext, dimensionValues);
});
}
}
/// <summary>
/// The main program is the 'listener' that listens and processes the events that come from EventGenerator
/// </summary>
class Program
{
/// <summary>
/// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to
/// the MyEventSource above. Normally this event source would be in a differnet process, but
/// it also works if this process generate the evnets and I do that here for simplicity.
/// </summary>
static int Main(string[] args)
{
try
{
// This is the name of the event source.
var providerName = "Microsoft-Demos-SimpleMonitor";
Debug.Assert(providerName == MyEventSource.Log.Name);
// Given just the name of the eventSource you can get the GUID for the evenSource by calling this API.
// From a ETW perspective, the GUID is the 'true name' of the EventSource.
var providerGuid = TraceEventSession.GetEventSourceGuidFromName(providerName);
Debug.Assert(providerGuid == MyEventSource.Log.Guid);
var provider2 = "Test-Event";
var providerGuid2 = TraceEventSession.GetEventSourceGuidFromName(provider2);
var provider3 = "IfxMetricsEvent";
var providerGuid3 = TraceEventSession.GetEventSourceGuidFromName(provider3);
var provider4 = "Microsoft-Cloud-InstrumentationFramework-Metrics";
var providerGuid4 = TraceEventSession.GetEventSourceGuidFromName(provider4);
var provider5 = "MetricEtwEvent";
var providerGuid5 = TraceEventSession.GetEventSourceGuidFromName(provider5);
// Today you have to be Admin to turn on ETW events (anyone can write ETW events).
if (!(TraceEventSession.IsElevated() ?? false))
{
Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
return -1;
}
// As mentioned below, sessions can outlive the process that created them. Thus you need a way of
// naming the session so that you can 'reconnect' to it from another process. This is what the name
// is for. It can be anything, but it should be descriptive and unique. If you expect mulitple versions
// of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix)
Console.WriteLine("Creating a 'My Session' session");
var sessionName = "My Session";
using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session'
// using (var session = new EtwRealtimeConsumer("EtwRealtimeConsumer"))
{
// Note that sessions create a OS object (a session) that lives beyond the lifetime of the process
// that created it (like Filles), thus you have to be more careful about always cleaning them up.
// An importanty way you can do this is to set the 'StopOnDispose' property which will cause the session to
// stop (and thus the OS object will die) when the TraceEventSession dies. Because we used a 'using'
// statement, this means that any exception in the code below will clean up the OS object.
session.StopOnDispose = true;
// By default, if you hit Ctrl-C your .NET objects may not be disposed, so force it to. It is OK if dispose is called twice.
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) { session.Dispose(); };
// prepare to read from the session, connect the ETWTraceEventSource to the session
using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session))
{
// To demonstrate non-trivial event manipuation, we calculate the time delta between 'MyFirstEvent and 'MySecondEvent'
// These variables are used in this calculation
int lastMyEventID = int.MinValue; // an illegal value to start with.
double lastMyEventMSec = 0;
// Hook up the parser that knows about EventSources
var parser = new DynamicTraceEventParser(source);
parser.All += delegate (TraceEvent data)
{
Console.WriteLine("GOT EVENT: " + data.ToString());
if (true /*data.ProviderGuid == providerGuid */) // We don't actually need this since we only turned one one provider.
{
// Note that this is the inefficient way of parsing events (there are string comparisions on the
// event Name and every payload value), however it is fine for events that occur less than 100 times
// a second. For more volumous events, you should consider making a parser for you eventSource
// (covered in another demo). This makes your code fare less 'reflection-like' where you have lots
// of strings (e.g. "MyFirstEvent", "MyId" ...) which is better even ignoring the performance benefit.
if (data.EventName == "MyFirstEvent")
{
// On First Events, simply remember the ID and time of the event
lastMyEventID = (int)data.PayloadByName("MyId");
lastMyEventMSec = data.TimeStampRelativeMSec;
}
else if (data.EventName == "MySecondEvent")
{
// On Second Events, if the ID matches, compute the delta and display it.
if (lastMyEventID == (int)data.PayloadByName("MyId"))
Console.WriteLine(" > Time Delta from first Event = {0:f3} MSec", data.TimeStampRelativeMSec - lastMyEventMSec);
}
else if (data.EventName == "Stop")
{
// Stop processing after we we see the 'Stop' event
source.Close();
}
}
};
// Enable my provider, you can call many of these on the same session to get other events.
session.EnableProvider(providerGuid);
session.EnableProvider(providerGuid2);
session.EnableProvider(providerGuid3);
session.EnableProvider(providerGuid4);
session.EnableProvider(providerGuid5);
session.EnableProvider(new Guid("95595d3e-4dd8-49b1-9f90-85870e95b10b"));
//session.EnableProvider(new Guid("edc24920-e004-40f6-a8e1-0e6e48f39d84"));
session.EnableProvider(Guid.Parse("{edc24920-e004-40f6-a8e1-0e6e48f39d84}"));
// Start another thread that Causes MyEventSource to create some events
// Normally this code as well as the EventSource itself would be in a different process.
EventGenerator.CreateEvents();
Console.WriteLine("Staring Listing for events");
// go into a loop processing events can calling the callbacks. Because this is live data (not from a file)
// processing never completes by itself, but only because someone called 'source.Close()'.
source.Process();
Console.WriteLine();
Console.WriteLine("Stopping the collection of events.");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
//while (true)
//{
// Thread.Sleep(100);
//}
return 0;
}
}
}
using Diagnostics.Tracing.Parsers;
using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Threading;
using System.Threading.Tasks;
// This program is a simple demo, it has both a EventSource and a EventGenerator which generate events for 10 seconds
// as well as a 'listener; that listens for these events (potentially in a differnet process, but we don't do that here)
// and does some rudimentary processing (calculates the difference between two particular events.
namespace SimpleMonitor
{
using Microsoft.Cloud.InstrumentationFramework;
// This code belongs in the process generating the events. They are samples
[EventSource(Name = "Microsoft-Demos-SimpleMonitor")] // This is the name of my eventSource outside my program.
class MyEventSource : EventSource
{
// Notice that the bodies of the events follow a pattern: WriteEvent(ID, <args>) where ID is a unique ID
// Starting at 1 and giving each different event a unque one, and passing all the payload arguments on to be sent out.
public void MyFirstEvent(string MyName, int MyId) { WriteEvent(1, MyName, MyId); }
public void MySecondEvent(int MyId) { WriteEvent(2, MyId); }
public void Stop() { WriteEvent(3); }
// Typically you only create one EventSource and use it throughout your program. Thus a static field makes sense.
public static MyEventSource Log = new MyEventSource();
}
// This code belongs in the process generating the events. It is in this process for simplicity.
class EventGenerator
{
public static void CreateEvents()
{
// This just spanws a thread that generates events every second for 10 seconds than issues a Stop event.
Task.Factory.StartNew(delegate
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("** Generateing the MyFirst and MySecond events.");
MyEventSource.Log.MyFirstEvent("Some string", i);
Thread.Sleep(10);
MyEventSource.Log.MySecondEvent(i);
Thread.Sleep(1000);
}
//Console.WriteLine("** Generating the Stop Event.");
//MyEventSource.Log.Stop();
string monitoringAccountName = "TestAccount";
string monitoringNamespace = "TestNamespace";
string metricName = "TestMetric";
string[] dimensionNames = new[] {"Name"};
string[] dimensionValues = new[] {"Vivek"};
//ErrorContext errorContext = new ErrorContext();
//MeasureMetric measureMetric = MeasureMetric.Create(
// monitoringAccountName,
// monitoringNamespace,
// metricName,
// true,
// dimensionNames);
//measureMetric.LogValue(5, ref errorContext, dimensionValues);
});
}
}
/// <summary>
/// The main program is the 'listener' that listens and processes the events that come from EventGenerator
/// </summary>
class Program
{
/// <summary>
/// This is a demo of using TraceEvent to activate a 'real time' provider that is listening to
/// the MyEventSource above. Normally this event source would be in a differnet process, but
/// it also works if this process generate the evnets and I do that here for simplicity.
/// </summary>
static int Main(string[] args)
{
try
{
// This is the name of the event source.
var providerName = "Microsoft-Demos-SimpleMonitor";
Debug.Assert(providerName == MyEventSource.Log.Name);
// Given just the name of the eventSource you can get the GUID for the evenSource by calling this API.
// From a ETW perspective, the GUID is the 'true name' of the EventSource.
var providerGuid = TraceEventSession.GetEventSourceGuidFromName(providerName);
Debug.Assert(providerGuid == MyEventSource.Log.Guid);
var provider2 = "Test-Event";
var providerGuid2 = TraceEventSession.GetEventSourceGuidFromName(provider2);
var provider3 = "IfxMetricsEvent";
var providerGuid3 = TraceEventSession.GetEventSourceGuidFromName(provider3);
var provider4 = "Microsoft-Cloud-InstrumentationFramework-Metrics";
var providerGuid4 = TraceEventSession.GetEventSourceGuidFromName(provider4);
var provider5 = "MetricEtwEvent";
var providerGuid5 = TraceEventSession.GetEventSourceGuidFromName(provider5);
// Today you have to be Admin to turn on ETW events (anyone can write ETW events).
if (!(TraceEventSession.IsElevated() ?? false))
{
Console.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
return -1;
}
// As mentioned below, sessions can outlive the process that created them. Thus you need a way of
// naming the session so that you can 'reconnect' to it from another process. This is what the name
// is for. It can be anything, but it should be descriptive and unique. If you expect mulitple versions
// of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix)
Console.WriteLine("Creating a 'My Session' session");
var sessionName = "My Session";
using (var session = new TraceEventSession(sessionName, null)) // the null second parameter means 'real time session'
// using (var session = new EtwRealtimeConsumer("EtwRealtimeConsumer"))
{
// Note that sessions create a OS object (a session) that lives beyond the lifetime of the process
// that created it (like Filles), thus you have to be more careful about always cleaning them up.
// An importanty way you can do this is to set the 'StopOnDispose' property which will cause the session to
// stop (and thus the OS object will die) when the TraceEventSession dies. Because we used a 'using'
// statement, this means that any exception in the code below will clean up the OS object.
session.StopOnDispose = true;
// By default, if you hit Ctrl-C your .NET objects may not be disposed, so force it to. It is OK if dispose is called twice.
Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) { session.Dispose(); };
// prepare to read from the session, connect the ETWTraceEventSource to the session
using (var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session))
{
// To demonstrate non-trivial event manipuation, we calculate the time delta between 'MyFirstEvent and 'MySecondEvent'
// These variables are used in this calculation
int lastMyEventID = int.MinValue; // an illegal value to start with.
double lastMyEventMSec = 0;
// Hook up the parser that knows about EventSources
var parser = new DynamicTraceEventParser(source);
parser.All += delegate (TraceEvent data)
{
Console.WriteLine("GOT EVENT: " + data.ToString());
if (true /*data.ProviderGuid == providerGuid */) // We don't actually need this since we only turned one one provider.
{
// Note that this is the inefficient way of parsing events (there are string comparisions on the
// event Name and every payload value), however it is fine for events that occur less than 100 times
// a second. For more volumous events, you should consider making a parser for you eventSource
// (covered in another demo). This makes your code fare less 'reflection-like' where you have lots
// of strings (e.g. "MyFirstEvent", "MyId" ...) which is better even ignoring the performance benefit.
if (data.EventName == "MyFirstEvent")
{
// On First Events, simply remember the ID and time of the event
lastMyEventID = (int)data.PayloadByName("MyId");
lastMyEventMSec = data.TimeStampRelativeMSec;
}
else if (data.EventName == "MySecondEvent")
{
// On Second Events, if the ID matches, compute the delta and display it.
if (lastMyEventID == (int)data.PayloadByName("MyId"))
Console.WriteLine(" > Time Delta from first Event = {0:f3} MSec", data.TimeStampRelativeMSec - lastMyEventMSec);
}
else if (data.EventName == "Stop")
{
// Stop processing after we we see the 'Stop' event
source.Close();
}
}
};
// Enable my provider, you can call many of these on the same session to get other events.
session.EnableProvider(providerGuid);
session.EnableProvider(providerGuid2);
session.EnableProvider(providerGuid3);
session.EnableProvider(providerGuid4);
session.EnableProvider(providerGuid5);
session.EnableProvider(new Guid("95595d3e-4dd8-49b1-9f90-85870e95b10b"));
//session.EnableProvider(new Guid("edc24920-e004-40f6-a8e1-0e6e48f39d84"));
session.EnableProvider(Guid.Parse("{edc24920-e004-40f6-a8e1-0e6e48f39d84}"));
// Start another thread that Causes MyEventSource to create some events
// Normally this code as well as the EventSource itself would be in a different process.
EventGenerator.CreateEvents();
Console.WriteLine("Staring Listing for events");
// go into a loop processing events can calling the callbacks. Because this is live data (not from a file)
// processing never completes by itself, but only because someone called 'source.Close()'.
source.Process();
Console.WriteLine();
Console.WriteLine("Stopping the collection of events.");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
//while (true)
//{
// Thread.Sleep(100);
//}
return 0;
}
}
}