Showing posts with label Event. Show all posts
Showing posts with label Event. Show all posts

Thursday, July 14, 2016

ETW event listener

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;
        }
    }
}

Wednesday, December 2, 2015

Event in C#

Events:
It is basically used for subscriber/publisher model.
subscriber of any delegate function can be declared as an event. In such case, Any operation can be done on the event inside the class, however only += and -+ can be done outside the class. eg:
public delegate void PriceChangedHandler();
class{
public event PriceChangedHandler PriceChanged;
}
Here inside the class we can do any operation on the PriceChanged but outside the class we can just do += and -=;

Standard event pattern:
 Class  extends System.EventArgs. It just contains some empty static property
 Next create delegate: return type should be void. It must accept 2 parameters. First is object  type. Second is subclass of EventArgs. Its name must end in "EventHandler"
 eg public delegate void EventHandler<TEventArgs>
  (object source, TEventArgs e) where TEventArgs : EventArgs;
 Next define event: public event EventHandler<PriceChangedEventArgs> PriceChanged;
 Finally, the pattern requires that you write a protected virtual method that fires the event. The name must match the name of the event, prefixed with the word"On", and then accept a single EventArgs argument: eg : protected virtual void OnPriceChanged (PriceChangedEventArgs e)
  {
    if (PriceChanged != null) PriceChanged (this, e);
  }

  Sample program:
  using System;

public class PriceChangedEventArgs : EventArgs
{
  public readonly decimal LastPrice;
  public readonly decimal NewPrice;

  public PriceChangedEventArgs (decimal lastPrice, decimal newPrice)
  {
    LastPrice = lastPrice; NewPrice = newPrice;
  }
}

public class Stock
{
  string symbol;
  decimal price;

  public Stock (string symbol) {this.symbol = symbol;}

  public event EventHandler<PriceChangedEventArgs> PriceChanged;
  protected virtual void OnPriceChanged (PriceChangedEventArgs e)
  {
    if (PriceChanged != null) PriceChanged (this, e);
  }

  public decimal Price
  {
    get { return price; }
    set
    {
      if (price == value) return;
      OnPriceChanged (new PriceChangedEventArgs (price, value));
      price = value;
    }
  }
}

class Test
{
  static void Main(  )
  {
    Stock stock = new Stock ("THPW");
    stock.Price = 27.10M;   //1
    // register with the PriceChanged event
    stock.PriceChanged += stock_PriceChanged; //2
    stock.Price = 31.59M; //3
stock.Price = 31.59M; //4
    stock.Price = 35.69M; //5
    stock.PriceChanged -= stock_PriceChanged; //6
    stock.Price = 37.89M;//7
  }

  static void stock_PriceChanged (object sender, PriceChangedEventArgs e)
  {
    if ((e.NewPrice - e.LastPrice) / e.LastPrice > 0.1M)
      Console.WriteLine ("Alert, 10% stock price increase!");
  }
}

In this program, executino starts with main. value of Price is set at 1. Since there is price change, it will call OnPriceChanged. Inside this funciton, since PriceChanged is null, it is not doing anything. At 2, It is adding method stock_PriceChanged to PriceChanged. Notice stock_PriceChanged is static with void return type. At 3, it is again setting value of Price and which is calling OnPriceChanged. Now PriceChanged is not null. So PriceChanged is called which actually is calling stock_PriceChanged with arguments of type object and PriceChangedEventArgs(subclass of EventArgs). At 4, there is no price change, so it returns. 5 is same as 3. At 6, function stock_PriceChanged is removed from PriceChanged event. So in 7, stock_PriceChanged is not called.

If we don't need any other information other than just that event is fired, we can use EventArgs.Empty
eg: Lets modify Stock class to incorporate this:
public class Stock
{
  string symbol;
  decimal price;

  public Stock (string symbol) {this.symbol = symbol;}

  public eventEventHandler PriceChanged;

protected virtual void OnPriceChanged (EventArgs e)
  {
    if (PriceChanged != null) PriceChanged (this, e);
  }

  public decimal Price
  {
    get { return price; }
    set
    {
      if (price == value) return;
      price = value;
      OnPriceChanged (EventArgs.Empty);
    }
  }
}