Вряд ли кто-нибудь утверждает важность инструментария вашего кода и есть некоторые встроенные параметры журнала в .NET Framework, не говоря уже о бесчисленных больших 3-х сторонних библиотек, таких как nlog, serilog и log4net. Но когда дело доходит до метрики отчетности (подумайте: запросы в минуту, использование, скорость сообщений и тому подобное) встроенная поддержка очень ограничена. Конечно, у нас есть счетчики производительности, но их нельзя использовать кроссплатформу, так как они являются только Windows. К счастью, существует относительный простой способ сообщить метрики, используя класс Eventsource, который является кроссплатформой и частью структуры, поэтому нет необходимости в внешних зависимостях.
ETW (отслеживание событий для Windows) и События ...| класс уже давно был частью .NET Рамки. Регуляторы в значительной степени используются самими каркасом, а также многие (3-го вечеринка) библиотек. Это позволяет использовать структурированную регистрацию в своем приложении с минимальным накладным расходом производительности. Данные могут быть собраны в процессе или внепроцессу, используя такие инструменты, как
Perfview Отказ
В этом сообщении мы посмотрим поближе к не так знакомую Счетчик событий
Класс, который позволяет нам определять и сообщать метрики, используя События ...| Отказ Я специально хочу показать, как вы можете прочитать эти счетчики в процессе с использованием
EventListener. Как это не очень хорошо
Документировано Отказ
Как стартер, давайте посмотрим, что команда CoreFX должна сказать о Events Corters (взято из их Учебное пособие ):
Хотя Eventsource быстро, лесозаготавливайте слишком много событий для очень частых событий, по-прежнему является ударом производительности. В этом руководстве мы введем EventCounter, механизм измерения производительности для очень частых событий.
Для события, которое происходит очень часто (например, если это случится каждые несколько миллисекунд), в целом вы захотите накладные расходы на производительность на мероприятие (например, меньше, чем миллисекунда), в противном случае он будет стоить значительную производительность накладные расходы. Регистрация события, в конце дня, нужно что-то написать на диск. Если диск недостаточно быстро, вы потеряете события. Нам нужно решение, отличное от регистрации самого события.
При работе с большим количеством событий, зная мера на мероприятие, также не очень полезна. В большинстве случаев все, что нам нужно, это просто некоторые статистические данные. Таким образом, мы могли бы заставить статистику в самой процессе, а затем писать событие время от времени, чтобы сообщить о статистике, это то, что EventCounter будет для нас.
Звучит отлично, не так ли? Это вопрос определения и отчетности метрик, а рамки будут выполнять агрегацию и расчет в памяти. Все, что нам нужно сделать, это указать интервал, при котором данные выводятся.
Во-первых, нам нужно определить События ...| это будет содержать
EventCounter экземпляры. Вы можете динамически создавать
EventCounter экземпляры во время выполнения, но из-за ошибки я документировал
здесь У вас будет создать хотя бы один EventCounter. экземпляр в конструкторе.
[EventSource(Name = "My-CustomMetricsEventSource-Minimal")] public sealed class CustomMetricsEventSource : EventSource { private EventCounter methodDurationCounter; private DictionarydynamicCounters = new Dictionary (); public static CustomMetricsEventSource Log = new CustomMetricsEventSource(); public CustomMetricsEventSource() { methodDurationCounter = new EventCounter(nameof(methodDurationCounter), this); } public void ReportMethodDurationInMs(long milliseconds) { methodDurationCounter.WriteMetric(milliseconds); } public void ReportMetric(string name, float value) { if (!dynamicCounters.TryGetValue(name, out EventCounter counterInstance)) { counterInstance = new EventCounter(name, this); dynamicCounters.Add(name, counterInstance); } counterInstance.WriteMetric(value); } }
Мы будем использовать Custommetricseventsource
Для регистрации наших метрик в консоли. Но нам также нужен слушатель, который поднимает зарегистрированные метрики и отправляет их в пункт назначения. В этом примере мы напишем данные в консоли, используя пользовательскую реализацию EventListener
:
internal class CustomMetricsEventListener : EventListener { protected override void OnEventWritten(EventWrittenEventArgs eventData) { var counterData = eventData.ToEventCounterData(); // Only write to console if actual data has been reported if (counterData?.Count == 0) return; Console.WriteLine( $"Counter {counterData.Name} reported values " + $"Min: {counterData.Min}, " + $"Max: {counterData.Max}, " + $"Count {counterData.Count}, " + $"Mean {counterData.Mean}, " + $"StandardDeviation: {counterData.StandardDeviation}, " + $"IntervalSec: {counterData.IntervalSec}"); } }
Код, слушал выше, использует ТовентКустердата
метод. Это на самом деле метод расширения. Возвращает EventCounterData
Экземпляр, который облегчает работу с зарегистрированными метрическими данными:
public static class EventWrittenEventArgsExtensions { public static bool IsEventCounter(this EventWrittenEventArgs eventData) { return eventData.EventName == "EventCounters"; } public static EventCounterData ToEventCounterData(this EventWrittenEventArgs eventData) { if (!eventData.IsEventCounter()) return null; return new EventCounterData(eventData); } } public class EventCounterData { public EventCounterData(EventWrittenEventArgs eventData) { var payload = (IDictionary) eventData.Payload[0]; Name = payload["Name"].ToString(); Mean = (float)payload["Mean"]; StandardDeviation = (float)payload["StandardDeviation"]; Count = (int)payload["Count"]; IntervalSec = (float)payload["IntervalSec"]; Min = (float)payload["Min"]; Max = (float)payload["Max"]; } public string Name { get; } public float Mean { get; } public float StandardDeviation { get; } public int Count { get; } public float IntervalSec { get; } public float Min { get; } public float Max { get; } }
Наконец, мы можем создать приложение консоли, которое позвонит Custommetricseventsource
Для журнала метрических значений. CustomMetricseVentListener
Построено таким образом, что метрики выводятся один раз в секунду.
Важно понимать, что это конфигурация CustomMetricseVentListener
Это определяет метрический интервал отчетности. Это делается путем передачи числового значения, указав интервал в секундах к EventCounterIntervalsec
аргумент
class Program { static void Main(string[] args) { var reader = new CustomMetricsEventListener(); var arguments = new Dictionary{ {"EventCounterIntervalSec", "1"} }; reader.EnableEvents(CustomMetricsEventSource.Log, EventLevel.LogAlways, EventKeywords.All, arguments); var random = new Random(); for (int i = 0; i <= 10; i++) { SleepingBeauty(random.Next(10, 200)); } Console.ReadKey(); } static void SleepingBeauty(int sleepTimeInMs) { var stopwatch = Stopwatch.StartNew(); Thread.Sleep(sleepTimeInMs); stopwatch.Stop(); CustomMetricsEventSource.Log.ReportMethodDurationInMs(stopwatch.ElapsedMilliseconds); CustomMetricsEventSource.Log.ReportMetric("someCounter", DateTime.Now.Millisecond); } }
Когда мы запустим программу, мы увидим, что метрические значения сообщаются на консоли:
Счетчик методика по методике.
Счетчик Somecounter Сообщенные значения Мин: 87, Макс.: 982, Count 9, Среднее 6031111, Стандартное представление: 269 2832, Интервалил: 1 026394
Делайте в голову, что метрики всегда будут сообщены по указанному интервалу (используя EventCounterterIntervalsec
) Даже если нет звонка WriteMetric
Метод EventCounter
экземпляр сделан. Все числовые значения, кроме Промежуток будет иметь значение по умолчанию 0.
Вы можете найти весь рабочий пример в моем репозитории GitHub:
Expecho/Blog.
Защитник для кода я вспомогал, видеть
Заполнитель для кода я вспомогал
Папки в этом репозитории
- Метрики: отчетность метрики с использованием .NET (Основной)
Конечно, отчетность этих значений к консоли не добавляет большого значения, решать вам отправлять данные на вашу собственную Backenc. Вы могли бы, например, отправить их как пользовательские метрики для приложений Insights, используя EventCounterCollectionModule , войдите в свой стек ELK или любую систему журнала, которую вы используете.
Я действительно желаю, чтобы сообщить, что метрики получают немного больше внимания, поскольку имение данных на метрах очень важно для того, чтобы получить подробное понимание того, как ваше приложение ведет себя. Например, вы можете использовать метрики, чтобы масштабировать или выйти или получить идею в использовании определенных частей вашего приложения. Большинство структур лесозаготов, которые я знаю, не обслуживаете метрики.
Было много обсуждений о том, стоит ли метрика поддержки, используя новую Ilogger
Интерфейс .NET. Ядро, но в конце концов нет поддержки для этого правильно знать. Подробнее о том, как и то почему можно прочитать в вопросе, созданной для этого обсуждения:
Обзор
Мы хотим включить приложения для излучения произвольных названных метрик, и у них есть их схватывание систем телеметрии/лесозаготовок (INPONT INSIGHT, ELASTICSearch/ELK и т. Д.), а также другие системы метрик (STATSD, INTOUXDB и т. Д.). Желается абстракция, чтобы позволить приложениям излучать метрики, не связанные с пунктом назначения метрик, особенно с такими функциями, как App Insights Auto-Lightup, в котором приложение не зависит от понимания приложений.
Предложение
Мы добавим поддержку отчетности метрик на Ilogger
Отказ Это будет сделано через новый интерфейс компаньона Imetricslogger
, какие регистраторы могут При желании воплощать в жизнь. Приложения могут выделять метрики через этот интерфейс (см. API ниже), а провайдеры регистратора, которые поддерживают метрики, могут получать и обрабатывать эти метрики. Метрики — это (имя, значение)
кортежи, где ценность
всегда с плавающей запятой номер. Ожидается, что метрики будут рассматриваться в совокупности, следовательно, не имеет смысла использовать универсальный тип данных, как объект
Для метрики, поэтому метрические значения всегда Двойной
с. Поддержка произвольных типов метрических значений является нецелевой. Метрики отчетности должны быть максимально эффективными, особенно в том случае, если для них не зарегистрировано поставщик регистратора регистратора, или они отфильтрованы.
Зачем Добавить в ilogger?
Первоначально я считал разработкой новой абстракции для метрик. Однако я столкнулся с несколькими проблемами:
- Нам нужно будет разработать структуру для именования/организации метрик (скорее всего, используя имена типа/пространства имен в качестве основы)
- Нам понадобится разработать структуру для фильтрации метрик (по категориям, имена, зернистам и т. Д.)
- Многие телеметрические системы обеспечивают поддержку как в журнале и метрической коллекции, и придется предоставить новую систему
- Потребители должны тянуть в две разные репортеры диагностики из ди (
ilogger
,ImetricReporter
и т. Д.) - Это создало бы еще одну диагностику абстракции (на вершине событий, EventSounters, Diagnosticsource, Perf Cashers, Yada Yada Yada (см. Что я там делал? ;П))
Тот факт, что 1 и 2 выше, уже существуют в лесозаготовительном трубопроводе, и многие метрические раковины также регистрируют раковины, привели к идее интеграции метрик в ведение журнала, а не создание совершенно новой абстракции.
Основная причина, по которой мы бы …| не Хотите добавить это в Ilogger был бы риском
API.
Imetriclogger
Дополнительный интерфейс компаньона для Ilogger
Реализации, чтобы указать их поддержку метрик. Ilogger
Экземпляры, возвращенные IloggerProvider
Ожидается, что будет реализовать этот интерфейс Если и только если Они хотят получать метрики.
namespace Microsoft.Extensions.Logging { public interface IMetricLogger { ////// Define a new metric with the provided name and return an /// The name of the metric to define ///that can be used to report values for that metric. /// An IMetric DefineMetric(string name); } }that can be used to report values for the metric
Иминый
Интерфейс, который позволяет сообщать метрические данные. Метрические значения
namespace Microsoft.Extensions.Logging { public interface IMetric { ////// Record a new value for this metric. /// /// The value to record for this metric void RecordValue(double value); } }
Loggermetricsextensions.
Предоставляет метод расширения на Ilogger
Чтобы потребители позволили потребителям излучать метрики, даже если основной поставщик регистратора регистратора не поддерживает (, конечно, метрики будут игнорироваться в этом случае).
namespace Microsoft.Extensions.Logging { public static class LoggerMetricsExtensions { ////// Define a new metric with the provided name and return an ///that can be used to report values for that metric. /// /// If none of the registered logger providers support metrics, values recorded by this metric will be lost. /// /// The name of the metric to define ///An public static IMetric DefineMetric(this ILogger logger, string name) { if(logger is IMetricLogger metricLogger) { return metricLogger.DefineMetric(name); } return NullMetric.Instance; } } }that can be used to report values for the metric
MetricValuextensions.
Методы расширения для Инометрический
Чтобы поддержать другие общие типы метрики (как упомянуто выше, основной тип все еще Двойной
Таким образом, значение должно быть представлено как Двойной
).
Предложение: мы могли бы иметь .Время () API, который возвращает ан. Idisposable который использует секундомер, чтобы сделать время для вас. Это было бы легко добавить позже.
using System; namespace Microsoft.Extensions.Logging { public static class MetricValueExtensions { ////// Record a new value for this metric. /// ////// This is a convenience method that will convert the /// The metric to record the value on. /// The value to record for this metric. public static void RecordValue(this IMetric metric, TimeSpan value) => metric.RecordValue(value.TotalMilliseconds); } }to a via /// the property. ///
Обновления регистратора
Совокупность Логин
Класс, который мы возвращаем из Loggerfactory. Createlogger
будет обновлен для поддержки Imetricslogger
даже если ни один из Ilogger
S, агрегаты поддерживают его. Он отправит бы метрики только к журналам, которые, как известно, поддерживают интерфейс.
Определить/запись
Для того, чтобы сохранить стоимость производительности как можно ниже для записи фактического метрического значения, API использует Определить
/ Запись
шаблон. Потребители должны ожидать, чтобы позвонить Определенный ("Foo")
Однажды Для определенного значения «Foo» и хранить результат как можно по всему миру (в типов Singleton и т. Д.). Это Определенный
Это делает большую часть работы, чтобы создать метрическую запись. После звонка Определенный
, RecordValue
Позвоните на Инометрический
Интерфейс может выбрать, как хранить значение, основанное на потребностях поставщиков. Например, в провайдере, который планирует отправить предварительно агрегированные метрики, RecordValue
может просто обновить прокатные совокупные значения, а затем отбросить фактическую запись (таким образом, имеющие постоянные требования к хранению для каждой метрики). Если поставщик должен сообщить отдельных метрик, он может использовать динамически выделенные буферы или кольцевые буферы, в зависимости от конфигурации и потребностей провайдера. В качестве примера Инометрический
Внедрение на основе предварительно агрегирующих данных, см. https://gist.github.com/anurse/03C6746204317BD3616C372B4DF9DBBA
Фильтрация метрик
Метрики должны быть фильтрующими, и они должны участвовать в существующем фильтрующем конвейере регистратора. Мое текущее предложение заключается в том, что метрики обрабатываются как LogLevel. Критический Сообщения, в этом, если категория вообще включена, метрики написаны. Мы могли бы рассмотреть возможность захвата Логика в .Definemetric и используя это для обеспечения разных «уровней» метрик. Поскольку существующие фильтры позволяют вам фильтровать поставщику, категорию и уровню, очень сложно отфильтровать конкретные метрики по имени (как это потребуется новая структура фильтра). Это может сделать Логика Поддержка метрик более важных.
@pakrym, и я разговаривал и придумал конкретное предложение
Метрики могут быть отфильтрованы у поставщика и названия категории, но нет концепции Loglevel
для метрик. Тем не менее, фильтры могут отключить все Метрики для определенного комбинации поставщика/категории. Для этого мы добавим Autommetrics
логический до Loggerfilterrule
какие по умолчанию на правда
Отказ При написании метрики мы проверим название провайдера и категории против параметров фильтра, а также проверяя этот логический.
Сумки и сумки имущества
Обратная связь запрашивается
Моя первоначальная мысль заключается в том, что метрики существуют за пределами принципов, поскольку они, как правило, агрегируются, и обзоры подразумевают данные для каждого события. Поставщики могли, в теории, дать своим Инометрический
Экземпляры доступ к читанию активных данных охвата данных и прикрепите их к метрикам, если они выбирают (например, системы, которые сообщают о негагентных метриках, могут захотеть). По аналогичным причинам, сумки имущества (I.E. Значения журнала/структурированные ведения журнала и т. Д.) Не имеют смысла для метрик в целом, поскольку они обычно агрегируются. Сказав это, многие провайдеры поддерживают произвольные свойства (IncoluxDB, Incoms Insights), поэтому какой-то уровень поддержки может быть полезен. Мы могли бы легко добавить дополнительный параметр в .RecordValue
Чтобы обеспечить добавление произвольных свойств каждому метрике, и провайдеры могли бы нерешить его, если это не имеет смысла для этого провайдера.
Влияние на существующие поставщики
Существуют минимальное влияние на существующие поставщики. Поскольку интерфейс предоставляет модель opt-in для провайдера для приема метрик, существующие поставщики не затронуты. Я полагаю, что провайдеры, такие как Serilog, не участвуют в этой системе, поскольку у них нет инфраструктуры для метрик (/CC @nblumhardt, я могу ошибаться!). Подобные провайдеры, такие как Incom Insights, могут выбрать либо добавлять поддержку метрик для своих существующих поставщиков, либо добавлять новые поставщики, которые обрабатывают только метрики (и игнорировать сообщения журнала), которые являются совместно зарегистрированы.
Оригинал: «https://dev.to/expecho/reporting-metrics-using-net-core-eventsource-and-eventcounter-23dn»