Сбор метрик

Эта статья относится к : ✔️ .NET Core 3.1 и более поздних версий платформа .NET Framework 4.6.1 и более поздних✔️ версий.

Инструментированный код может записывать числовые измерения, однако чтобы создать полезные метрики для мониторинга эти измерения обычно необходимо агрегировать, передавать и хранить. Процесс агрегирования, передачи и хранения данных называется коллекцией. В этом руководстве показано несколько примеров сбора метрик:

Дополнительные сведения о пользовательском инструментировании метрик и параметрах см. в разделе "Сравнение API метрик".

Необходимые компоненты

  • Пакет SDK для .NET Core 3.1 или более поздней версии

Создание примера приложения

Прежде чем собирать метрики, необходимо производить измерения. В этом руководстве создается приложение с базовым инструментированием метрик. Среда выполнения .NET также содержит различные встроенные метрики. Дополнительные сведения о создании новых метрик с помощью System.Diagnostics.Metrics.Meter API см . в руководстве по инструментированию.

dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource

Замените все содержимое Program.cs следующим кодом:

using System.Diagnostics.Metrics;

class Program
{
    static Meter s_meter = new("HatCo.HatStore", "1.0.0");
    static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hats-sold");

    static void Main(string[] args)
    {
        var rand = Random.Shared;
        Console.WriteLine("Press any key to exit");
        while (!Console.KeyAvailable)
        {
            //// Simulate hat selling transactions.
            Thread.Sleep(rand.Next(100, 2500));
            s_hatsSold.Add(rand.Next(0, 1000));
        }
    }
}

Предыдущий код имитирует продажу шляп с случайными интервалами и случайным временем.

Просмотр метрик с помощью dotnet-counters

dotnet-counters — это средство командной строки, которое может просматривать динамические метрики для приложений .NET Core по запросу. Для нее не требуется настройка, что полезно для нерегламентированных расследований или проверки работы инструментирования метрик. Она работает с API-интерфейсами на основе System.Diagnostics.Metrics и счетчиками событий.

Если средство dotnet-counters не установлено, выполните следующую команду:

dotnet tool update -g dotnet-counters

Пока выполняется пример приложения, запустите счетчики dotnet-counters. В следующей команде показан пример dotnet-counters мониторинга всех метрик из счетчика HatCo.HatStore . Имя счетчика указывается с учетом регистра. Пример приложения — metric-instr.exe, замените его именем примера приложения.

dotnet-counters monitor -n metric-instr HatCo.HatStore

Выходные данные должны выглядеть примерно так:

Press p to pause, r to resume, q to quit.
    Status: Running

[HatCo.HatStore]
    hats-sold (Count / 1 sec)                          4

dotnet-counters можно запустить с другим набором метрик, чтобы увидеть некоторые встроенные инструментирование из среды выполнения .NET:

dotnet-counters monitor -n metric-instr

Выходные данные должны выглядеть примерно так:

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    % Time in GC since last GC (%)                                 0
    Allocation Rate (B / 1 sec)                                8,168
    CPU Usage (%)                                                  0
    Exception Count (Count / 1 sec)                                0
    GC Heap Size (MB)                                              2
    Gen 0 GC Count (Count / 1 sec)                                 0
    Gen 0 Size (B)                                         2,216,256
    Gen 1 GC Count (Count / 1 sec)                                 0
    Gen 1 Size (B)                                           423,392
    Gen 2 GC Count (Count / 1 sec)                                 0
    Gen 2 Size (B)                                           203,248
    LOH Size (B)                                             933,216
    Monitor Lock Contention Count (Count / 1 sec)                  0
    Number of Active Timers                                        1
    Number of Assemblies Loaded                                   39
    ThreadPool Completed Work Item Count (Count / 1 sec)           0
    ThreadPool Queue Length                                        0
    ThreadPool Thread Count                                        3
    Working Set (MB)                                              30

Дополнительные сведения см. в разделе dotnet-counters. Дополнительные сведения о метриках в .NET см . в встроенных метриках.

Просмотр метрик в Grafana с помощью OpenTelemetry и Prometheus.

Обзор

OpenTelemetry:

  • Является проектом с открытым исходным кодом, нейтральным поставщиком, поддерживаемым Cloud Native Computing Foundation.
  • Стандартизирует создание и сбор данных телеметрии для облачного программного обеспечения.
  • Работает с .NET с помощью API метрик .NET.
  • Поддерживается Azure Monitor и многими поставщиками APM.

В этом руководстве показана одна из интеграции, доступная для метрик OpenTelemetry с помощью проектов OSS Prometheus и Grafana . Поток данных метрик:

  1. Api метрик .NET записывают измерения из примера приложения.

  2. Библиотека OpenTelemetry, запущенная в приложении, объединяет измерения.

  3. Библиотека программы экспорта Prometheus предоставляет доступ к агрегированным данным через конечную точку метрик HTTP. Программа экспорта — это библиотеки OpenTelemetry, передающие телеметрию в серверные части конкретных поставщиков.

  4. Сервер Prometheus:

    • Опрос конечной точки метрик
    • Считывает данные
    • Сохраняет данные в базе данных для долгосрочного сохранения. Prometheus относится к чтению и хранению данных в качестве очистки конечной точки.
    • Может работать на другом компьютере
  5. Сервер Grafana:

    • Запрашивает данные, хранящиеся в Prometheus, и отображает его на веб-панели мониторинга.
    • Может работать на другом компьютере.

Настройка примера приложения для использования экспортера Prometheus в OpenTelemetry

Добавьте ссылку на экспортер Prometheus OpenTelemetry в пример приложения:

dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease

Примечание.

В этом руководстве используется предварительная сборка поддержки Prometheus OpenTelemetry, доступная во время написания статьи.

Обновление Program.cs с помощью конфигурации OpenTelemetry:

using OpenTelemetry;
using OpenTelemetry.Metrics;
using System.Diagnostics.Metrics;

class Program
{
    static Meter s_meter = new("HatCo.HatStore", "1.0.0");
    static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
        name: "hats-sold",
        unit: "Hats",
        description: "The number of hats sold in our store");

    static void Main(string[] args)
    {
        using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder()
                .AddMeter("HatCo.HatStore")
                .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { "http://localhost:9184/" })
                .Build();

        var rand = Random.Shared;
        Console.WriteLine("Press any key to exit");
        while (!Console.KeyAvailable)
        {
            //// Simulate hat selling transactions.
            Thread.Sleep(rand.Next(100, 2500));
            s_hatsSold.Add(rand.Next(0,1000));
        }
    }
}

В предыдущем коде:

  • AddMeter("HatCo.HatStore") настраивает OpenTelemetry для передачи всех метрик, собранных счетчиком, определенным в приложении.
  • AddPrometheusHttpListener настраивает OpenTelemetry для:
    • Предоставление конечной точки метрик Prometheus на порту 9184
    • Используйте HttpListener.

Дополнительные сведения о параметрах конфигурации OpenTelemetry см. в документации по OpenTelemetry. В документации по OpenTelemetry показаны параметры размещения для ASP.NET приложений.

Запустите приложение и оставьте его запущенным, чтобы можно было собирать измерения:

dotnet run

Установка и настройка Prometheus

Выполните первые действия Prometheus, чтобы настроить сервер Prometheus и подтвердить его работу.

Измените файл конфигурации prometheus.yml , чтобы Prometheus сломать конечную точку метрик, которую представляет пример приложения. Добавьте следующий выделенный текст в scrape_configs разделе:

# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]

  - job_name: 'OpenTelemetryTest'
    scrape_interval: 1s # poll very quickly for a more responsive demo
    static_configs:
      - targets: ['localhost:9184']

Запуск Prometheus

  1. Перезагрузите конфигурацию или перезапустите сервер Prometheus.

  2. Убедитесь, что OpenTelemetryTest находится в состоянии UP на странице "Целевые показатели состояния>" веб-портала Prometheus. Prometheus status

  3. На странице Graph веб-портала Prometheus введите hats текстовое поле выражения и выберите hats_sold_Hatshat На вкладке графа Prometheus отображает увеличение значения счетчика "hats-sold", который создается примером приложения. Prometheus hats sold graph

На предыдущем изображении для графа задано значение 5 м, что составляет 5 минут.

Если сервер Prometheus не сломывает пример приложения в течение длительного времени, может потребоваться ждать, пока данные будут накапливаться.

Отображение метрик на панели мониторинга Grafana

  1. Следуйте стандартным инструкциям по установке решения Grafana и его подключению к источнику данных Prometheus.

  2. Создайте панель мониторинга Grafana, щелкнув значок + на панели инструментов слева на веб-портале Grafana, а затем выберите Панель мониторинга. В появившемся редакторе панели мониторинга введите Hats Sold/Sec в поле ввода заголовка и rate(hats_sold[5m]) в поле выражения PromQL:

    Hats sold Grafana dashboard editor

  3. Нажмите кнопку " Применить" , чтобы сохранить и просмотреть новую панель мониторинга.

    Hats sold Grafana dashboard]

Создание пользовательского средства сбора с помощью API .NET MeterListener

API .NET MeterListener позволяет создавать пользовательскую логику внутрипроцессного процесса, чтобы наблюдать за измерениями, записанными System.Diagnostics.Metrics.Meter. Рекомендации по созданию пользовательской логики, совместимой с более старой инструментацией EventCounters, см. в разделе EventCounters.

Измените код, используемый Program.csMeterListener:

using System.Diagnostics.Metrics;

class Program
{
    static Meter s_meter = new("HatCo.HatStore", "1.0.0");
    static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
        name: "hats-sold",
        unit: "Hats",
        description: "The number of hats sold in our store");

    static void Main(string[] args)
    {
        using MeterListener meterListener = new();
        meterListener.InstrumentPublished = (instrument, listener) =>
        {
            if (instrument.Meter.Name is "HatCo.HatStore")
            {
                listener.EnableMeasurementEvents(instrument);
            }
        };

        meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
        // Start the meterListener, enabling InstrumentPublished callbacks.
        meterListener.Start();

        var rand = Random.Shared;
        Console.WriteLine("Press any key to exit");
        while (!Console.KeyAvailable)
        {
            //// Simulate hat selling transactions.
            Thread.Sleep(rand.Next(100, 2500));
            s_hatsSold.Add(rand.Next(0, 1000));
        }
    }

    static void OnMeasurementRecorded<T>(
        Instrument instrument,
        T measurement,
        ReadOnlySpan<KeyValuePair<string, object?>> tags,
        object? state)
    {
        Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
    }
}

В следующих выходных данных показаны выходные данные приложения с пользовательским обратным вызовом для каждого измерения:

> dotnet run
Press any key to exit
hats-sold recorded measurement 978
hats-sold recorded measurement 775
hats-sold recorded measurement 666
hats-sold recorded measurement 66
hats-sold recorded measurement 914
hats-sold recorded measurement 912
...

Объяснение примера кода

Фрагменты кода в этом разделе приведены из предыдущего примера.

В следующем выделенном коде создается экземпляр MeterListener объекта для получения измерений. Ключевое слово using вызывается Dispose при meterListener выходе из область.

using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
    if (instrument.Meter.Name is "HatCo.HatStore")
    {
        listener.EnableMeasurementEvents(instrument);
    }
};

Следующий выделенный код настраивает инструмент, из которого прослушиватель получает измерения. InstrumentPublished — это делегат, который вызывается при создании нового инструмента в приложении.

using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
    if (instrument.Meter.Name is "HatCo.HatStore")
    {
        listener.EnableMeasurementEvents(instrument);
    }
};

Делегат может проверить инструмент, чтобы решить, следует ли подписаться. Например, делегат может проверка имя, счетчик или любое другое общедоступное свойство. EnableMeasurementEvents позволяет получать измерения из указанного инструмента. Код, который получает ссылку на инструмент с помощью другого подхода:

  • Обычно не выполняется.
  • Может вызываться EnableMeasurementEvents() в любое время со ссылкой.

Делегат, вызываемый при получении измерений от инструмента, настраивается путем вызова SetMeasurementEventCallback:

    meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
    // Start the meterListener, enabling InstrumentPublished callbacks.
    meterListener.Start();

    var rand = Random.Shared;
    Console.WriteLine("Press any key to exit");
    while (!Console.KeyAvailable)
    {
        //// Simulate hat selling transactions.
        Thread.Sleep(rand.Next(100, 2500));
        s_hatsSold.Add(rand.Next(0, 1000));
    }
}

static void OnMeasurementRecorded<T>(
    Instrument instrument,
    T measurement,
    ReadOnlySpan<KeyValuePair<string, object?>> tags,
    object? state)
{
    Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
}

Универсальный параметр управляет типом данных измерения, получаемым обратным вызовом. Например, создается Counter<int>int измерение. Counter<double>double Инструменты можно создавать с помощью типов byte, short, int, long, float, doubleи decimal. Мы рекомендуем зарегистрировать обратный вызов для каждого типа данных, если у вас нет определенных сценариев знаний о том, что не все типы данных необходимы. Выполнение повторяющихся вызовов SetMeasurementEventCallback с различными универсальными аргументами может показаться немного необычным. API был разработан таким образом, чтобы позволить MeterListener получать измерения с низкими затратами на производительность, как правило, всего несколько наносекунд.

При MeterListener.EnableMeasurementEvents вызове state объект можно указать как один из параметров. Объект state является произвольным. Если вы предоставляете объект состояния в этом вызове, он хранится с этим инструментом и возвращается в качестве state параметра в обратном вызове. Это сделано как для удобства, так и для оптимизации производительности. Часто прослушиватели должны:

  • Создайте объект для каждого инструмента, в котором хранятся измерения в памяти.
  • Код должен выполнять вычисления по этим измерениям.

Кроме того, создайте объект Dictionary хранилища, который сопоставляется с инструментом и ищет его на каждом измерении. Использование гораздо Dictionary медленнее, чем доступ к нему.state

meterListener.Start();

Предыдущий код запускает MeterListener обратный вызов. Делегат InstrumentPublished вызывается для каждого существующего инструмента в процессе. Недавно созданные объекты инструментирования также активируются InstrumentPublished для вызова.

using MeterListener meterListener = new MeterListener();

Когда приложение будет выполнено прослушивание, удаление прослушивателя останавливает поток обратных вызовов и освобождает все внутренние ссылки на объект прослушивателя. Ключевое слово, используемый при объявлении meterListener причин Dispose вызова, когда переменная using выходит из область. Обратите внимание, что это только обещает, Dispose что он не будет инициировать новые обратные вызовы. Поскольку обратные вызовы происходят в разных потоках, после возврата вызова Dispose все еще могут присутствовать выполняющиеся обратные вызовы.

Чтобы гарантировать, что определенный регион кода в обратном вызове не выполняется в настоящее время и не будет выполняться в будущем, необходимо добавить синхронизацию потоков. Dispose Не включает синхронизацию по умолчанию, так как:

  • Синхронизация добавляет затраты на производительность при каждом обратном вызове измерения.
  • MeterListener разработан как высокопроизводительный сознательный API.