Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Artikel ini berlaku untuk: ✔️ .NET 6.0 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:
- Mengisi metrik pada Grafana dengan OpenTelemetry dan Prometheus.
- Menampilkan metrik secara real time dengan
dotnet-counters - Membuat alat pengumpulan kustom menggunakan API .NET MeterListener dasar.
Untuk informasi selengkapnya tentang instrumentasi dan opsi metrik kustom, lihat Membandingkan API metrik.
Prasyarat
- .NET 6.0 SDK atau yang lebih baru
Membuat aplikasi contoh
Sebelum metrik dapat dikumpulkan, pengukuran harus dihasilkan. 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 dotnet-counters
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 API berbasis System.Diagnostics.Metrics dan EventCounters.
Jika alat dotnet-counters belum diinstal, jalankan perintah berikut:
dotnet tool update -g dotnet-counters
Jika aplikasi Anda menjalankan versi .NET yang lebih lama dari .NET 9, antarmuka pengguna output penghitung dotnet akan terlihat sedikit berbeda dari di bawah ini; lihat penghitung dotnet untuk detailnya.
Saat aplikasi contoh berjalan, luncurkan dotnet-counters. Perintah berikut menunjukkan contoh pemantauan dotnet-counters semua metrik dari meteran HatCo.HatStore. Nama meter sensitif terhadap huruf besar-kecil. Aplikasi sampel kami 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:
System.Runtime
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[System.Runtime]
dotnet.assembly.count ({assembly}) 11
dotnet.gc.collections ({collection})
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
dotnet.gc.heap.total_allocated (By) 1,376,024
dotnet.gc.last_collection.heap.fragmentation.size (By)
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.heap.size (By)
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.memory.committed_size (By) 0
dotnet.gc.pause.time (s) 0
dotnet.jit.compilation.time (s) 0.253
dotnet.jit.compiled_il.size (By) 79,536
dotnet.jit.compiled_methods ({method}) 743
dotnet.monitor.lock_contentions ({contention}) 0
dotnet.process.cpu.count ({cpu}) 22
dotnet.process.cpu.time (s)
cpu.mode
--------
system 0.125
user 46.453
dotnet.process.memory.working_set (By) 34,447,360
dotnet.thread_pool.queue.length ({work_item}) 0
dotnet.thread_pool.thread.count ({thread}) 0
dotnet.thread_pool.work_item.count ({work_item}) 0
dotnet.timer.count ({timer}) 0
Untuk informasi selengkapnya, lihat dotnet-counters. Untuk mempelajari selengkapnya tentang metrik di .NET, lihat metrik bawaan.
Melihat metrik di Grafana dengan OpenTelemetry dan Prometheus
Gambaran Umum
- 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 . Data aliran metrik:
API metrik .NET merekam pengukuran dari aplikasi contoh.
Pustaka OpenTelemetry yang berjalan di aplikasi menggabungkan pengukuran.
Pustaka pengekspor Prometheus membuat data agregat tersedia melalui titik akhir metrik HTTP. Pengekspor adalah istilah yang digunakan oleh OpenTelemetry untuk pustaka yang mengirimkan telemetri ke backend khusus vendor.
Sebuah server Prometheus
- Memantau 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
Server Grafana:
- Mengkueri data yang disimpan di Prometheus dan menampilkannya di dasbor pemantauan berbasis web.
- Dapat berjalan pada komputer yang berbeda.
Konfigurasikan aplikasi contoh untuk menggunakan Prometheus exporter dari OpenTelemetry.
Tambahkan referensi ke pengekspor OpenTelemetry Prometheus ke aplikasi contoh:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Nota
Tutorial ini menggunakan build pra-rilis dari dukungan OpenTelemetry untuk Prometheus yang tersedia 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.Port = 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 didefinisikan dalam aplikasi. -
AddPrometheusHttpListenermengonfigurasi OpenTelemetry untuk:- Memaparkan titik akhir metrik Prometheus pada port
9184 - Gunakan HttpListener.
- Memaparkan titik akhir metrik Prometheus pada port
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 mengekstrak 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
Muat ulang konfigurasi atau mulai ulang server Prometheus.
Konfirmasikan bahwa OpenTelemetryTest berada dalam status UP di halaman Target Status>portal web Prometheus.
Pada halaman Grafik portal web Prometheus, masukkan
hatsdalam kotak teks ekspresi dan pilihhats_sold_Hats
. Di tab grafik, Prometheus menunjukkan peningkatan nilai Penghitung "topi-terjual" yang sedang dipancarkan oleh aplikasi contoh.
Pada gambar sebelumnya, waktu grafik diatur ke 5m, yaitu 5 menit.
Jika server Prometheus belum lama mengambil data dari aplikasi contoh, Anda mungkin perlu menunggu data untuk terakumulasi.
Tampilkan metrik pada dasbor Grafana
Ikuti instruksi standar untuk menginstal Grafana dan menyambungkannya ke sumber data Prometheus.
Buat dasbor Grafana dengan mengklik + ikon di toolbar kiri di portal web Grafana, lalu pilih Dasbor. Di editor dashboard yang muncul, masukkan Topi Terjual/Detik di kotak input Judul dan rate(hats_sold[5m]) di bidang ekspresi PromQL:
Klik Terapkan untuk menyimpan dan menampilkan dasbor baru.
]
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 Program.cs agar menggunakan 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}");
}
}
Berikut adalah keluaran dari aplikasi dengan panggilan balik khusus 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
...
Contoh penjelasan kode
Cuplikan kode di bagian ini berasal dari sampel sebelumnya.
Dalam kode yang disorot berikut, instans MeterListener dibuat untuk menerima pengukuran. Keyword using menyebabkan Dispose dipanggil saat 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 umum 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, Counter<int> menghasilkan int pengukuran, Counter<double> menghasilkan double pengukuran. Instrumen dapat dibuat dengan jenis byte, short, int, long, float, double, dan decimal. Sebaiknya daftarkan panggilan balik untuk setiap tipe data, kecuali Anda mengetahui skenario khusus di mana tidak semua tipe 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 sembarangan. 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 melacaknya setiap kali pengukuran. Menggunakan Dictionary jauh lebih lambat daripada mengaksesnya dari state.
meterListener.Start();
Kode di atas memulai MeterListener yang memungkinkan panggilan balik. Delegasi InstrumentPublished dipanggil untuk setiap Instrumen yang sudah ada sebelumnya selama proses. Objek Instrumen yang baru dibuat juga memicu InstrumentPublished untuk dipanggil.
using MeterListener meterListener = new MeterListener();
Ketika aplikasi selesai mendengarkan, menghapus pendengar menghentikan alur panggilan balik dan melepaskan referensi internal apa pun ke objek pendengar. Kata kunci using yang digunakan untuk mendeklarasikan meterListener menyebabkan 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 masih berlangsung setelah panggilan Dispose selesai.
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 beban kinerja di setiap panggilan balik pengukuran.
-
MeterListenerdirancang sebagai API sadar performa tinggi.