(ETW) EventSource в общем компоненте

Я хотел бы знать, каков ваш опыт использования EventSource для компонентов, которые являются общими и могут использоваться несколько раз в одном и том же процессе.

Один простой пример. Мой общий компонент — TestQueue, и я хотел бы использовать его несколько раз в моем процессе и снова в PerfView, чтобы увидеть, какое событие принадлежит какой очереди.

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

И я хотел бы использовать его так:

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

Вот что у меня есть в PerfView:

введите здесь описание изображения

Между этими двумя событиями нет никакой разницы. Я хотел бы знать, как я могу идентифицировать их, чтобы какое-то имя (строка) или идентификатор фигурировали как часть имени события? Я знаю, что мог бы использовать Задачи для логической группировки событий, но это не то, что я ожидал бы здесь, тем более, что они должны быть предопределены в источнике событий. Идентификатор действия также одинаков в случае использования.

Ваше здоровье!


Один из лучших способов реализации двух источников событий — это передача имени источника события через конструктор пользовательской реализации. Может быть, это способ сделать это :)

Таким образом, в пользовательском классе источника события нет атрибута EventSource, а конструктор имеет параметр имени источника события:

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

Таким образом, предыдущий пример использования становится примерно таким:

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


person Zeljko    schedule 05.04.2017    source источник
comment
Я забыл упомянуть... Как видите, в моей реализации я оставил место для создания нового экземпляра IEtwQueue и передачи его при каждом новом использовании, но это не кажется хорошим решением, поскольку каждое использование требует реализации нового EventSource .   -  person Zeljko    schedule 05.04.2017
comment
Похоже, что использование дополнительных параметров EventAttribute сделает то, что вы хотите, особенно. Сообщение. См. aroundtuitblog.wordpress.com/2014/ 24/10/a-look-at-etw-part-2 для получения дополнительной информации.   -  person Kim Johnson    schedule 05.04.2017
comment
Я мог бы отформатировать содержимое с параметром сообщения. Я мог бы передать имя очереди или идентификатор как часть сообщения, но так ли MS себе это представляет? Фильтрация на основе содержимого сообщения — это то, что мне нужно сделать, чтобы увидеть события, связанные только с экземпляром очереди.   -  person Zeljko    schedule 05.04.2017
comment
Я не знаю, как это себе представлял MS, и согласен, что это сложно использовать. Он может быть либо тесно связан с кодом, генерирующим события, либо слишком универсален для действительно эффективной фильтрации.   -  person Kim Johnson    schedule 06.04.2017
comment
Привет, Ким, не могли бы вы взглянуть на обновление моего вопроса. Может быть, что-то в этом роде?   -  person Zeljko    schedule 12.04.2017


Ответы (1)


Вместо использования двух источников я бы добавил параметры, в которых вы можете передать имя очереди и представление объекта ToString.

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

Однако, если вы ожидаете возникновения событий > 1000/sec, вы можете повысить производительность с помощью небезопасного кода для вызова WriteEventCore для создания перегрузки WriteEvent, которая принимает int, int, string, string в качестве 4 аргументов вместо используя более медленную перегрузку int, params object[]`.

[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);
        }
    }
}
person Scott Chamberlain    schedule 12.04.2017
comment
Я думал об этом, и, кажется, все в порядке. Как правило, у нас есть структурированное ведение журнала, и мы можем фильтровать по любому параметру события. Также это улучшение производительности является значительным - более чем в два раза быстрее. - person Zeljko; 27.04.2017
comment
Хотя определение EventSource во время выполнения иногда может быть полезным, особенно когда нам нужны события только от одного экземпляра компонента в системе. Например. если у нас есть 50 очередей с огромным давлением, включение прослушивателя событий для общего источника событий замедлит всю систему, а включение его только для определенного экземпляра очереди повлияет на использование только этой одной очереди. - person Zeljko; 27.04.2017
comment
Я отмечаю этот ответ как правильный, но я хотел бы посмотреть, как другие используют ETW в реальных случаях использования. - person Zeljko; 27.04.2017