Bagikan melalui


Mengumpulkan metrik

Artikel ini berlaku untuk: ✔️ .NET Core 3.1 dan yang lebih baru ✔️ .NET Framework 4.6.1 dan yang lebih baru

Kode berinstrumen dapat merekam pengukuran numerik, tetapi pengukuran biasanya perlu dikumpulkan, ditransmisikan, dan disimpan untuk membuat metrik yang berguna untuk pemantauan. Proses agregasi, transmisi, dan penyimpanan data disebut pengumpulan. Tutorial ini menunjukkan beberapa contoh pengumpulan metrik:

Untuk informasi selengkapnya tentang instrumentasi dan opsi metrik kustom, lihat Membandingkan API metrik.

Prasyarat

Membuat aplikasi contoh

Sebelum metrik dapat dikumpulkan, pengukuran harus diproduksi. Tutorial ini membuat aplikasi yang memiliki instrumentasi metrik dasar. Runtime .NET juga memiliki berbagai metrik bawaan. Untuk informasi selengkapnya tentang membuat metrik baru menggunakan System.Diagnostics.Metrics.Meter API, lihat tutorial instrumentasi.

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

Ganti isi Program.cs dengan kode berikut:

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

Kode sebelumnya mensimulasikan penjualan topi secara acak dan waktu acak.

Melihat metrik dengan penghitung dotnet

dotnet-counters adalah alat baris perintah yang dapat melihat metrik langsung untuk aplikasi .NET Core sesuai permintaan. Ini tidak memerlukan penyiapan, membuatnya berguna untuk investigasi ad-hoc atau memverifikasi bahwa instrumentasi metrik berfungsi. Ini berfungsi dengan System.Diagnostics.Metrics API berbasis dan EventCounters.

Jika alat penghitung dotnet tidak diinstal, jalankan perintah berikut:

dotnet tool update -g dotnet-counters

Saat aplikasi contoh berjalan, luncurkan penghitung dotnet. Perintah berikut menunjukkan contoh dotnet-counters pemantauan semua metrik dari meteran HatCo.HatStore . Nama meter peka huruf besar/kecil. Aplikasi sampel kami adalah metric-instr.exe, ganti ini dengan nama aplikasi sampel Anda.

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

Output yang mirip dengan berikut ini ditampilkan:

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

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

dotnet-counters dapat dijalankan dengan serangkaian metrik yang berbeda untuk melihat beberapa instrumentasi bawaan dari runtime .NET:

dotnet-counters monitor -n metric-instr

Output yang mirip dengan berikut ini ditampilkan:

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

Untuk informasi selengkapnya, lihat penghitung dotnet. Untuk mempelajari selengkapnya tentang metrik di .NET, lihat metrik bawaan.

Melihat metrik di Grafana dengan OpenTelemetry dan Prometheus

Gambaran Umum

OpenTelemetry:

  • Adalah proyek sumber terbuka vendor netral yang didukung oleh Cloud Native Computing Foundation.
  • Menstandarkan pembuatan dan pengumpulan telemetri untuk perangkat lunak cloud-native.
  • Bekerja dengan .NET menggunakan API metrik .NET.
  • Didukung oleh Azure Monitor dan banyak vendor APM.

Tutorial ini menunjukkan salah satu integrasi yang tersedia untuk metrik OpenTelemetry menggunakan proyek OSS Prometheus dan Grafana . Aliran data metrik:

  1. API metrik .NET merekam pengukuran dari aplikasi contoh.

  2. Pustaka OpenTelemetry yang berjalan di aplikasi menggabungkan pengukuran.

  3. Pustaka pengekspor Prometheus membuat data agregat tersedia melalui titik akhir metrik HTTP. 'Pengekspor' adalah apa yang disebut OpenTelemetry pustaka yang mengirimkan telemetri ke backend khusus vendor.

  4. Server Prometheus:

    • Polling titik akhir metrik
    • Membaca data
    • Menyimpan data dalam database untuk persistensi jangka panjang. Prometheus mengacu pada membaca dan menyimpan data sebagai mengikis titik akhir.
    • Dapat berjalan pada komputer yang berbeda
  5. Server Grafana:

    • Mengkueri data yang disimpan di Prometheus dan menampilkannya di dasbor pemantauan berbasis web.
    • Dapat berjalan pada komputer yang berbeda.

Mengonfigurasi contoh aplikasi untuk menggunakan pengekspor Prometheus OpenTelemetry

Tambahkan referensi ke pengekspor OpenTelemetry Prometheus ke aplikasi contoh:

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

Catatan

Tutorial ini menggunakan build pra-rilis dukungan Prometheus OpenTelemetry yang tersedia pada saat penulisan.

Perbarui Program.cs dengan konfigurasi 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));
        }
    }
}

Dalam kode sebelumnya:

  • AddMeter("HatCo.HatStore") mengonfigurasi OpenTelemetry untuk mengirimkan semua metrik yang dikumpulkan oleh Meter yang ditentukan dalam aplikasi.
  • AddPrometheusHttpListener mengonfigurasi OpenTelemetry untuk:
    • Mengekspos titik akhir metrik Prometheus pada port 9184
    • Gunakan HttpListener.

Lihat dokumentasi OpenTelemetry untuk informasi selengkapnya tentang opsi konfigurasi OpenTelemetry. Dokumentasi OpenTelemetry menunjukkan opsi hosting untuk aplikasi ASP.NET.

Jalankan aplikasi dan biarkan berjalan sehingga pengukuran dapat dikumpulkan:

dotnet run

Menyiapkan dan mengonfigurasi Prometheus

Ikuti langkah-langkah pertama Prometheus untuk menyiapkan server Prometheus dan mengonfirmasi bahwa prometheus berfungsi.

Ubah file konfigurasi prometheus.yml sehingga Prometheus mengikis titik akhir metrik yang diekspos aplikasi contoh. Tambahkan teks yang disorot berikut ini di bagian 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']

Mulai Prometheus

  1. Muat ulang konfigurasi atau mulai ulang server Prometheus.

  2. Konfirmasikan bahwa OpenTelemetryTest berada dalam status UP di halaman Target Status>portal web Prometheus. Prometheus status

  3. Pada halaman Grafik portal web Prometheus, masukkan hats dalam kotak teks ekspresi dan pilih hats_sold_Hatshat Di tab grafik, Prometheus menunjukkan peningkatan nilai Penghitung "topi dijual" yang sedang dipancarkan oleh aplikasi contoh. Prometheus hats sold graph

Pada gambar sebelumnya, waktu grafik diatur ke 5m, yaitu 5 menit.

Jika server Prometheus belum lama mengekstrak aplikasi contoh, Anda mungkin perlu menunggu data terakumulasi.

Menampilkan metrik di dasbor Grafana

  1. Ikuti instruksi standar untuk menginstal Grafana dan menyambungkannya ke sumber data Prometheus.

  2. Buat dasbor Grafana dengan mengklik + ikon di toolbar kiri di portal web Grafana, lalu pilih Dasbor. Di editor dasbor yang muncul, masukkan Topi Terjual/Detik di kotak Input judul dan laju(hats_sold[5m]) di bidang ekspresi PromQL:

    Hats sold Grafana dashboard editor

  3. Klik Terapkan untuk menyimpan dan menampilkan dasbor baru.

    Hats sold Grafana dashboard]

Membuat alat koleksi kustom menggunakan .NET MeterListener API

.NET MeterListener API memungkinkan Anda membuat logika dalam proses kustom untuk mengamati pengukuran yang direkam oleh System.Diagnostics.Metrics.Meter. Untuk panduan tentang membuat logika kustom yang kompatibel dengan instrumentasi EventCounters yang lebih lama, lihat EventCounters.

Ubah kode untuk Program.cs digunakan MeterListener:

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

Output berikut menunjukkan output aplikasi dengan panggilan balik kustom pada setiap pengukuran:

> 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
...

Menjelaskan kode sampel

Cuplikan kode di bagian ini berasal dari sampel sebelumnya.

Dalam kode yang disorot berikut, instans MeterListener dibuat untuk menerima pengukuran. Kata using kunci menyebabkan Dispose dipanggil ketika meterListener keluar dari cakupan.

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

Kode yang disorot berikut mengonfigurasi instrumen mana yang menerima pengukuran dari listener. InstrumentPublished adalah delegasi yang dipanggil saat instrumen baru dibuat dalam aplikasi.

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

Delegasi dapat memeriksa instrumen untuk memutuskan apakah akan berlangganan. Misalnya, delegasi dapat memeriksa nama, Meter, atau properti publik lainnya. EnableMeasurementEvents memungkinkan penerimaan pengukuran dari instrumen yang ditentukan. Kode yang mendapatkan referensi ke instrumen dengan pendekatan lain:

  • Biasanya tidak dilakukan.
  • Dapat memanggil EnableMeasurementEvents() kapan saja dengan referensi.

Delegasi yang dipanggil ketika pengukuran diterima dari instrumen dikonfigurasi dengan memanggil 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}");
}

Parameter generik mengontrol jenis data pengukuran mana yang diterima oleh panggilan balik. Misalnya, menghasilkan Counter<int> int pengukuran, Counter<double> menghasilkan double pengukuran. Instrumen dapat dibuat dengan bytejenis , , short, intlong, float, double, dan decimal . Sebaiknya daftarkan panggilan balik untuk setiap jenis data kecuali Anda memiliki pengetahuan khusus skenario bahwa tidak semua jenis data diperlukan. Melakukan panggilan berulang ke SetMeasurementEventCallback dengan argumen generik yang berbeda mungkin tampak sedikit tidak biasa. API dirancang dengan cara ini untuk memungkinkan MeterListener untuk menerima pengukuran dengan overhead performa rendah, biasanya hanya beberapa nanodetik.

Ketika MeterListener.EnableMeasurementEvents dipanggil, state objek dapat disediakan sebagai salah satu parameter. Objek state ini segan-segan. Jika Anda memberikan objek status dalam panggilan tersebut, maka objek tersebut disimpan dengan instrumen tersebut state dan dikembalikan kepada Anda sebagai parameter dalam panggilan balik. Ini dimaksudkan baik sebagai kenyamanan maupun sebagai pengoptimalan performa. Seringkali pendengar perlu:

  • Buat objek untuk setiap instrumen yang menyimpan pengukuran dalam memori.
  • Memiliki kode untuk melakukan perhitungan pada pengukuran tersebut.

Atau, buat Dictionary yang memetakan dari instrumen ke objek penyimpanan dan mencarinya di setiap pengukuran. Dictionary Menggunakan jauh lebih lambat daripada mengaksesnya dari state.

meterListener.Start();

Kode sebelumnya memulai MeterListener yang memungkinkan panggilan balik. Delegasi InstrumentPublished dipanggil untuk setiap Instrumen yang sudah ada sebelumnya dalam proses. Objek Instrumen yang baru dibuat juga memicu InstrumentPublished untuk dipanggil.

using MeterListener meterListener = new MeterListener();

Ketika aplikasi selesai mendengarkan, membuang pendengar menghentikan alur panggilan balik dan merilis referensi internal apa pun ke objek pendengar. Kata kunci yang using digunakan saat mendeklarasikan meterListener penyebab Dispose dipanggil ketika variabel keluar dari cakupan. Perhatikan bahwa Dispose hanya menjanjikan bahwa ia tidak akan memulai panggilan balik baru. Karena panggilan balik terjadi pada utas yang berbeda, mungkin masih ada panggilan balik yang sedang berlangsung setelah panggilan untuk Dispose kembali.

Untuk menjamin bahwa wilayah kode tertentu dalam panggilan balik saat ini tidak dijalankan dan tidak akan dijalankan di masa mendatang, sinkronisasi utas harus ditambahkan. Dispose tidak menyertakan sinkronisasi secara default karena:

  • Sinkronisasi menambahkan overhead performa di setiap panggilan balik pengukuran.
  • MeterListener dirancang sebagai API sadar performa tinggi.