(ETW) EventSource in a common component

2019-09-16 14:44发布

I would like to know what are yours experiences regarding using EventSource for components that are common and that can be utilized few times within the same process.

One simple example. My shared component is TestQueue and I would like to utilize it few times with my process and again in PerfView to see what event belongs to which queue.

public class TestQueue<T>
{
    private readonly IEtwQueue _etwQueue;
    private readonly Queue<T> _instance = new Queue<T>();

    public TestQueue(IEtwQueue etwQueue)
    {
        _etwQueue = etwQueue;
    }

    public void Enqueue(T item)
    {
        _instance.Enqueue(item);
        _etwQueue.CommandEnqueued(_instance.Count);
    }

    public T Dequeue()
    {
        _etwQueue.CommandDequed(_instance.Count);
        return _instance.Dequeue();
    }
}

public interface IEtwQueue
{
    void CommandEnqueued(int items);
    void CommandDequed(int items);
}

[EventSource(Name = "Test-ETW-Queue")]
public class EtwQueue : EventSource, IEtwQueue
{
    public static EtwQueue Log = new EtwQueue();

    private EtwQueue() { }

    [Event(1)]
    public void CommandEnqueued(int items) { if (IsEnabled()) WriteEvent(1, items); }

    [Event(2)]
    public void CommandDequed(int items) { if (IsEnabled()) WriteEvent(2, items); }
}

And I would like to use it like this:

TestQueue<string> testStringQueue = new TestQueue<string>(EtwQueue.Log);
TestQueue<int> testIntQueue = new TestQueue<int>(EtwQueue.Log);
testIntQueue.Enqueue(15);
testStringQueue.Enqueue("X");

Here is what I have in PerfView:

enter image description here

There is no difference between these two events. I would like to know how could I identify them so that some name (string) or ID figures out as part of event name? I know that I could use Tasks for logical grouping of events, but it is not what I would expect here, especially since they must be predefined in the event source. Activity ID is also the same in the use case.

Cheers!


One better way then implementing two EventSources is passing event source name through constructor of custom implementation. Maybe this is the way to do it :)

So there is no EventSource attribute on custom event source class and constructor has event source name parameter:

public class EtwQueueEventSource : EventSource, IEtwQueue
{
    public EtwQueueEventSource(string sourceName) : base(sourceName) { }

    [Event(1)]
    public void CommandEnqueued(int items) { if (IsEnabled()) WriteEvent(1, items); }

    [Event(2)]
    public void CommandDequed(int items) { if (IsEnabled()) WriteEvent(2, items); }
}

So previous usage example becomes something like:

TestQueue<string> testStringQueue = new TestQueue<string>(new EtwQueueEventSource("Test-ETW-Queue-String"));
TestQueue<int> testIntQueue = new TestQueue<int>(new EtwQueueEventSource("Test-ETW-Queue-Integer"));
testIntQueue.Enqueue(15);
testStringQueue.Enqueue("X");

PerfView

1条回答
三岁会撩人
2楼-- · 2019-09-16 15:11

Instead of using two sources I would add a parameters where you can pass the name of the queue in and the ToString representation of the object.

public class TestQueue<T>
{
    private readonly IEtwQueue _etwQueue;
    private readonly string _queueName;
    private readonly Queue<T> _instance = new Queue<T>();

    public TestQueue(IEtwQueue etwQueue, string queueName)
    {
        _etwQueue = etwQueue;
        _queueName = queueName;
    }

    public void Enqueue(T item)
    {
        _instance.Enqueue(item);

        if(_etwQueue.IsEnabled()) //So we only call item.ToString() if the queue is enabled.
        {
            _etwQueue.CommandEnqueued(_instance.Count, item.ToString(), queueName);
        }
    }

    public T Dequeue()
    {
        T item = _instance.Dequeue();
        if(_etwQueue.IsEnabled()) //So we only call item.ToString() if the queue is enabled.
        {
            _etwQueue.CommandDequed(_instance.Count, item.ToString(), queueName);
        }
        return 
    }
}

public interface IEtwQueue
{
    void CommandEnqueued(int items, string itemDescription, string queueName);
    void CommandDequed(int items, string itemDescription, string queueName);
}

[EventSource(Name = "Test-ETW-Queue")]
public class EtwQueue : EventSource, IEtwQueue
{
    public static EtwQueue Log = new EtwQueue();

    private EtwQueue() { }

    [Event(1)]
    public void CommandEnqueued(int items, string itemDescription, string queueName) 
    { 
        if (IsEnabled()) WriteEvent(1, items, itemDescription, queueName); 
    }

    [Event(2)]
    public void CommandDequed(int items, string itemDescription, string queueName)
    { 
        if (IsEnabled()) WriteEvent(2, items, itemDescription, queueName); 
    }

However, if you expect > 1000 / sec events to be raised you may want to increase performance by the unsafe code to call WriteEventCore to make a overload of WriteEvent that takes a int, int, string, string as the 4 arguments instead of using the slower int, params object[]` overload.

[EventSource(Name = "Test-ETW-Queue")]
public class EtwQueue : EventSource
{
    public static EtwQueue Log = new EtwQueue();

    private EtwQueue() { }

    [Event(1)]
    public void CommandEnqueued(int items, string itemDescription, string queueName)
    {
        if (IsEnabled()) WriteEvent(1, items, itemDescription, queueName);
    }

    [Event(2)]
    public void CommandDequed(int items, string itemDescription, string queueName)
    {
        if (IsEnabled()) WriteEvent(2, items, itemDescription, queueName);
    }

    [NonEvent]
    public unsafe void WriteEvent(int eventId, int arg1, string arg2, string arg3)
    {
        if (arg2 == null) arg2 = "";
        if (arg3 == null) arg3 = "";

        fixed (char* string2Bytes = arg2)
        fixed (char* string3Bytes = arg3)
        {
            EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
            descrs[0].DataPointer = (IntPtr)(&arg1);
            descrs[0].Size = 4;
            descrs[1].DataPointer = (IntPtr)string2Bytes;
            descrs[1].Size = ((arg2.Length + 1) * 2);
            descrs[2].DataPointer = (IntPtr)string3Bytes;
            descrs[2].Size = ((arg3.Length + 1) * 2);
            WriteEventCore(eventId, 3, descrs);
        }
    }
}
查看更多
登录 后发表回答