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 Core 6 dan versi yang lebih baru ✔️ .NET Framework 4.6.1 dan versi yang lebih baru
Aplikasi .NET dapat diinstrumentasikan menggunakan API System.Diagnostics.Metrics untuk melacak metrik penting. Beberapa metrik disertakan dalam pustaka .NET standar, tetapi Anda mungkin ingin menambahkan metrik kustom baru yang relevan untuk aplikasi dan pustaka Anda. Dalam tutorial ini, Anda akan menambahkan metrik baru dan memahami jenis metrik apa yang tersedia.
Nota
.NET memiliki beberapa API metrik lama, yaitu EventCounters dan System.Diagnostics.PerformanceCounter, yang tidak tercakup di sini. Untuk mempelajari selengkapnya tentang alternatif ini, lihat Bandingkan API metrik.
Membuat metrik kustom
Prasyarat: .NET Core 6 SDK atau versi yang lebih baru
Buat aplikasi konsol baru yang mereferensikan paket NuGet System.Diagnostics.DiagnosticSource versi 8 atau yang lebih tinggi. Aplikasi yang menargetkan .NET 8+ menyertakan referensi ini secara default. Kemudian, perbarui kode di Program.cs
agar sesuai:
> dotnet new console
> dotnet package add System.Diagnostics.DiagnosticSource
(Jika Anda menggunakan versi SDK .NET 9 atau yang lebih lama, gunakan formulir sebagai gantinya dotnet add package
.)
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has a transaction each second that sells 4 hats
Thread.Sleep(1000);
s_hatsSold.Add(4);
}
}
}
The System.Diagnostics.Metrics.Meter type is the entry point for a library to create a named group of instruments. Instrumen merekam pengukuran numerik yang diperlukan untuk menghitung metrik. Here we used CreateCounter to create a Counter instrument named "hatco.store.hats_sold". Selama setiap transaksi simulasi, kode memanggil Add untuk merekam jumlah topi yang dijual, 4 dalam kasus ini. Instrumen "hatco.store.hats_sold" secara implisit mendefinisikan beberapa metrik yang dapat dihitung dari pengukuran ini, seperti jumlah total topi yang dijual atau topi yang dijual/detik. Pada akhirnya terserah alat pengumpulan metrik untuk menentukan metrik mana yang akan dihitung dan cara melakukan komputasi tersebut, tetapi setiap instrumen memiliki beberapa konvensi default yang menyampaikan niat pengembang. For Counter instruments, the convention is that collection tools show the total count and/or the rate at which the count is increasing.
Parameter generik int
pada Counter<int>
dan CreateCounter<int>(...)
menentukan bahwa penghitung ini harus dapat menyimpan nilai hingga Int32.MaxValue. Anda dapat menggunakan salah satu byte
, short
, int
, long
, float
, double
, atau decimal
tergantung pada ukuran data yang perlu Anda simpan dan apakah nilai pecahan diperlukan.
Jalankan aplikasi dan biarkan berjalan untuk saat ini. Kami akan melihat metrik berikutnya.
> dotnet run
Press any key to exit
Praktik terbaik
Untuk kode yang tidak dirancang untuk digunakan dalam kontainer Dependency Injection (DI), buat Meter sekali dan simpan dalam variabel statis. Untuk penggunaan dalam pustaka yang sadar akan DI, variabel statis dianggap sebagai anti-pola dan contoh DI di bawah ini menunjukkan pendekatan yang lebih idiomatik. Each library or library subcomponent can (and often should) create its own Meter. Pertimbangkan untuk membuat Meter baru daripada menggunakan kembali yang sudah ada jika Anda mengantisipasi pengembang aplikasi akan menghargai mampu mengaktifkan dan menonaktifkan grup metrik secara terpisah dengan mudah.
The name passed to the Meter constructor should be unique to distinguish it from other Meters. We recommend OpenTelemetry naming guidelines, which use dotted hierarchical names. Nama-nama assembly atau namespace untuk kode yang diinstrumentasikan biasanya merupakan pilihan yang baik. If an assembly adds instrumentation for code in a second, independent assembly, the name should be based on the assembly that defines the Meter, not the assembly whose code is being instrumented.
.NET tidak menerapkan skema penamaan apa pun untuk Instruments, tetapi sebaiknya ikuti panduan penamaan OpenTelemetry , yang menggunakan nama hierarkis bertitik huruf kecil dan garis bawah ('_') sebagai pemisah antara beberapa kata dalam elemen yang sama. Tidak semua alat metrik mempertahankan nama Meter sebagai bagian dari nama metrik akhir, sehingga bermanfaat untuk membuat nama instrumen unik secara global sendiri.
Contoh nama instrumen:
contoso.ticket_queue.duration
contoso.reserved_tickets
contoso.purchased_tickets
The APIs to create instruments and record measurements are thread-safe. Di pustaka .NET, sebagian besar metode instans memerlukan sinkronisasi saat dipanggil pada objek yang sama dari beberapa utas, tetapi itu tidak diperlukan dalam kasus ini.
API Instrumen untuk merekam pengukuran (Add dalam contoh ini) biasanya berjalan dalam <10 ns ketika tidak ada data yang dikumpulkan, atau puluhan hingga ratusan nanodetik saat pengukuran dikumpulkan oleh pustaka atau alat koleksi berperforma tinggi. Ini memungkinkan API ini digunakan secara liberal dalam banyak kasus, tetapi berhati-hatilah terhadap kode yang sangat sensitif terhadap performa.
Menampilkan metrik baru
Ada banyak opsi untuk menyimpan dan melihat metrik. Tutorial ini menggunakan alat dotnet-counters, yang berguna untuk analisis ad-hoc. Anda juga dapat melihat tutorial pengumpulan metrik untuk alternatif lainnya. Jika alat dotnet-counters belum diinstal, gunakan SDK untuk menginstalnya.
> dotnet tool update -g dotnet-counters
You can invoke the tool using the following command: dotnet-counters
Tool 'dotnet-counters' (version '7.0.430602') was successfully installed.
Saat aplikasi contoh masih berjalan, gunakan dotnet-counters untuk memantau penghitung baru:
> dotnet-counters monitor -n metric-demo.exe --counters HatCo.Store
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.Store]
hatco.store.hats_sold (Count / 1 sec) 4
Seperti yang diharapkan, Anda dapat melihat bahwa toko HatCo terus menjual 4 topi setiap detik.
Get a Meter via dependency injection
Dalam contoh sebelumnya, Meter diperoleh dengan membangunnya dengan new
dan menetapkannya ke bidang statis. Menggunakan statis dengan cara ini bukan pendekatan yang baik saat menggunakan injeksi dependensi (DI). Dalam kode yang menggunakan DI, seperti ASP.NET Core atau aplikasi dengan Generic Host, buat objek Meter menggunakan IMeterFactory. Mulai dari .NET 8, host akan secara otomatis mendaftarkan IMeterFactory dalam kontainer layanan atau Anda dapat mendaftarkan jenis secara manual di IServiceCollection apa pun dengan memanggil AddMetrics.
The meter factory integrates metrics with DI, keeping Meters in different service collections isolated from each other even if they use an identical name. Ini sangat berguna untuk pengujian sehingga beberapa pengujian yang berjalan secara paralel hanya mengamati pengukuran yang dihasilkan dari dalam kasus pengujian yang sama.
To obtain a Meter in a type designed for DI, add an IMeterFactory parameter to the constructor, then call Create. Contoh ini menunjukkan penggunaan IMeterFactory di aplikasi ASP.NET Core.
Tentukan jenis untuk menahan instrumen:
public class HatCoMetrics
{
private readonly Counter<int> _hatsSold;
public HatCoMetrics(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("HatCo.Store");
_hatsSold = meter.CreateCounter<int>("hatco.store.hats_sold");
}
public void HatsSold(int quantity)
{
_hatsSold.Add(quantity);
}
}
Register the type with DI container in Program.cs
.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<HatCoMetrics>();
Masukkan jenis metrik dan nilai rekaman jika diperlukan. Karena jenis metrik terdaftar di DI, metrik dapat digunakan dengan pengontrol MVC, API minimal, atau jenis lain yang dibuat oleh DI:
app.MapPost("/complete-sale", ([FromBody] SaleModel model, HatCoMetrics metrics) =>
{
// ... business logic such as saving the sale to a database ...
metrics.HatsSold(model.QuantitySold);
});
Praktik terbaik
-
System.Diagnostics.Metrics.Meter mengimplementasikan IDisposable, tetapi IMeterFactory secara otomatis mengelola masa pakai objek
Meter
apa pun yang dibuatnya, membuangnya saat kontainer DI dibuang. Tidak perlu menambahkan kode tambahan untuk memanggilDispose()
padaMeter
, dan tidak akan berpengaruh apa pun.
Jenis instrumen
Sejauh ini kami hanya menunjukkan instrumen Counter<T>, tetapi ada lebih banyak jenis instrumen yang tersedia. Instrumen berbeda dengan dua cara:
- Komputasi metrik default - Alat yang mengumpulkan dan menganalisis pengukuran instrumen akan menghitung metrik default yang berbeda tergantung pada instrumen.
- Penyimpanan data agregat - Metrik yang paling berguna memerlukan data untuk dikumpulkan dari banyak pengukuran. Salah satu opsinya adalah penelepon menyediakan pengukuran individual pada waktu yang tidak ditentukan dan alat pengumpulan mengelola penggabungan. Atau, penelepon dapat mengelola pengukuran agregat dan menyediakannya sesuai permintaan dalam panggilan balik.
Jenis instrumen yang saat ini tersedia:
Counter (CreateCounter) - Instrumen ini melacak nilai yang meningkat dari waktu ke waktu dan pemanggil melaporkan kenaikan menggunakan Add. Sebagian besar alat akan menghitung total dan tingkat perubahan total. For tools that only show one thing, the rate of change is recommended. Misalnya, asumsikan bahwa penelepon memanggil
Add()
sekali setiap detik dengan nilai berturut-turut 1, 2, 4, 5, 4, 3. Jika alat pengumpulan diperbarui setiap tiga detik, maka total setelah tiga detik adalah 1+2+4=7 dan total setelah enam detik adalah 1+2+4+5+4+3=19. The rate of change is the (current_total - previous_total), so at three seconds the tool reports 7-0=7, and after six seconds, it reports 19-7=12.UpDownCounter (CreateUpDownCounter) - Instrumen ini melacak nilai yang dapat meningkat atau berkurang dari waktu ke waktu. Pemanggil melaporkan kenaikan dan penurunan menggunakan Add. Misalnya, asumsikan bahwa penelepon memanggil
Add()
sekali setiap detik dengan nilai berturut-turut 1, 5, -2, 3, -1, -3. Jika alat pengumpulan diperbarui setiap tiga detik, maka total setelah tiga detik adalah 1+5-2=4 dan total setelah enam detik adalah 1+5-2+3-1-3=3.ObservableCounter (CreateObservableCounter) - This instrument is similar to Counter except that the caller is now responsible for maintaining the aggregated total. Pemanggil menyediakan fungsi panggilan balik saat ObservableCounter dibuat dan fungsi tersebut diaktifkan setiap kali perangkat lunak perlu mengamati total saat ini. Misalnya, jika alat koleksi diperbarui setiap tiga detik, maka fungsi panggilan balik juga akan dipanggil setiap tiga detik. Most tools will have both the total and rate of change in the total available. If only one can be shown, rate of change is recommended. Jika panggilan balik mengembalikan 0 pada panggilan awal, 7 ketika dipanggil lagi setelah tiga detik, dan 19 ketika dipanggil setelah enam detik, maka alat akan melaporkan nilai-nilai tersebut tidak berubah sebagai total. Untuk laju perubahan, alat akan menunjukkan 7-0=7 setelah tiga detik dan 19-7=12 setelah enam detik.
ObservableUpDownCounter (CreateObservableUpDownCounter) - Instrumen ini mirip dengan UpDownCounter kecuali bahwa pemanggil sekarang bertanggung jawab untuk mempertahankan total agregat. The caller provides a callback delegate when the ObservableUpDownCounter is created and the callback is invoked whenever tools need to observe the current total. Misalnya, jika alat koleksi diperbarui setiap tiga detik, maka fungsi panggilan balik juga akan dipanggil setiap tiga detik. Whatever value is returned by the callback will be shown in the collection tool unchanged as the total.
Mengukur (CreateGauge) - Instrumen ini memungkinkan pemanggil untuk mengatur nilai metrik saat ini menggunakan metode Record. Nilai dapat diperbarui kapan saja dengan memanggil metode lagi dan alat pengumpulan metrik akan menampilkan nilai apa pun yang terakhir ditetapkan.
ObservableGauge (CreateObservableGauge) - Instrumen ini memungkinkan pemanggil untuk memberikan panggilan balik di mana nilai yang diukur diteruskan langsung sebagai metrik. Setiap kali alat pengumpulan diperbarui, panggilan balik dipanggil, dan nilai apa pun yang dikembalikan oleh panggilan balik ditampilkan di alat.
Histogram (CreateHistogram) - Instrumen ini digunakan untuk melacak distribusi pengukuran. Tidak ada satu pun cara kanonis untuk menggambarkan serangkaian pengukuran, tetapi alat disarankan untuk menggunakan histogram atau persentil komputasi. Misalnya, asumsikan pemanggil memanggil Record untuk merekam pengukuran ini selama interval pembaruan alat pengumpulan: 1,5,2,3,10,9,7,4,6,8. Alat koleksi mungkin melaporkan bahwa persentil ke-50, ke-90, dan ke-95 dari pengukuran ini masing-masing adalah 5, 9, dan 9.
Nota
For details about how to set the recommended bucket boundaries when creating a Histogram instrument see: Using Advice to customize Histogram instruments.
Praktik terbaik saat memilih jenis instrumen
For counting things, or any other value that solely increases over time, use Counter or ObservableCounter. Choose between Counter and ObservableCounter depending on which is easier to add to the existing code: either an API call for each increment operation, or a callback that will read the current total from a variable the code maintains. Dalam jalur kode yang sangat cepat di mana performa penting dan menggunakan Add akan membuat lebih dari satu juta panggilan per detik per utas, penggunaan ObservableCounter dapat menawarkan lebih banyak kesempatan untuk pengoptimalan.
Untuk mengukur waktu, Histogram biasanya lebih disukai. Seringkali lebih berguna untuk memahami bagian akhir dari distribusi ini (persentil ke-90, ke-95, ke-99) daripada rata-rata atau total.
Other common cases, such as cache hit rates or sizes of caches, queues, and files are usually well suited for
UpDownCounter
orObservableUpDownCounter
. Pilih di antara mereka tergantung pada mana yang lebih mudah ditambahkan ke kode yang ada: panggilan API untuk setiap kenaikan dan operasi penurunan atau panggilan balik yang akan membaca nilai saat ini dari variabel yang dipertahankan kode.
Nota
Jika Anda menggunakan versi .NET yang lebih lama atau paket DiagnosticSource NuGet yang tidak mendukung UpDownCounter
dan ObservableUpDownCounter
(sebelum versi 7), ObservableGauge
sering menjadi pengganti yang baik.
Contoh jenis instrumen yang berbeda
Hentikan proses contoh yang dimulai sebelumnya, dan ganti kode contoh di Program.cs
dengan:
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
static Histogram<double> s_orderProcessingTime = s_meter.CreateHistogram<double>("hatco.store.order_processing_time");
static int s_coatsSold;
static int s_ordersPending;
static Random s_rand = new Random();
static void Main(string[] args)
{
s_meter.CreateObservableCounter<int>("hatco.store.coats_sold", () => s_coatsSold);
s_meter.CreateObservableGauge<int>("hatco.store.orders_pending", () => s_ordersPending);
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has one transaction each 100ms that each sell 4 hats
Thread.Sleep(100);
s_hatsSold.Add(4);
// Pretend we also sold 3 coats. For an ObservableCounter we track the value in our variable and report it
// on demand in the callback
s_coatsSold += 3;
// Pretend we have some queue of orders that varies over time. The callback for the orders_pending gauge will report
// this value on-demand.
s_ordersPending = s_rand.Next(0, 20);
// Last we pretend that we measured how long it took to do the transaction (for example we could time it with Stopwatch)
s_orderProcessingTime.Record(s_rand.Next(5, 15)/1000.0);
}
}
}
Jalankan proses baru dan gunakan penghitung dotnet seperti sebelumnya di shell kedua untuk melihat metrik:
> dotnet-counters monitor -n metric-demo.exe --counters HatCo.Store
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.coats_sold (Count) 8,181
hatco.store.hats_sold (Count) 548
hatco.store.order_processing_time
Percentile
50 0.012
95 0.013
99 0.013
hatco.store.orders_pending 9
Contoh ini menggunakan beberapa angka yang dihasilkan secara acak sehingga nilai Anda akan sedikit bervariasi. Dotnet-counters menampilkan instrumen Histogram dalam bentuk tiga statistik persentil (persentil ke-50, 95, dan 99) tetapi alat lain dapat meringkas distribusi secara berbeda atau menawarkan lebih banyak opsi konfigurasi.
Praktik terbaik
Histogram cenderung menyimpan lebih banyak data dalam memori daripada jenis metrik lainnya. Namun, penggunaan memori yang tepat ditentukan oleh alat pengumpulan yang digunakan. Jika Anda menentukan sejumlah besar (>100) metrik Histogram, Anda mungkin perlu memberi pengguna panduan untuk tidak mengaktifkan semuanya pada saat yang sama, atau untuk mengonfigurasi alat mereka untuk menghemat memori dengan mengurangi presisi. Beberapa alat koleksi mungkin memiliki batasan keras pada jumlah Histogram bersamaan yang akan mereka pantau untuk mencegah penggunaan memori yang berlebihan.
Panggilan balik untuk semua instrumen yang dapat diamati dipanggil secara berurutan, sehingga panggilan balik apa pun yang membutuhkan waktu lama dapat menunda atau mencegah semua metrik dikumpulkan. Favor quickly reading a cached value, returning no measurements, or throwing an exception over performing any potentially long-running or blocking operation.
The ObservableCounter, ObservableUpDownCounter, and ObservableGauge callbacks occur on a thread that's not usually synchronized with the code that updates the values. Anda bertanggung jawab untuk menyinkronkan akses memori atau menerima nilai yang tidak konsisten yang dapat dihasilkan dari penggunaan akses yang tidak disinkronkan. Pendekatan umum untuk menyinkronkan akses adalah menggunakan kunci atau panggilan Volatile.Read dan Volatile.Write.
Fungsi CreateObservableGauge dan CreateObservableCounter mengembalikan objek instrumen, tetapi dalam kebanyakan kasus Anda tidak perlu menyimpannya dalam variabel karena tidak ada interaksi lebih lanjut dengan objek yang diperlukan. Menetapkannya ke variabel statis seperti yang kami lakukan untuk instrumen lain adalah legal tetapi rawan kesalahan, karena inisialisasi statis C# malas dan variabel biasanya tidak pernah dirujuk. Berikut adalah contoh masalahnya:
using System; using System.Diagnostics.Metrics; class Program { // BEWARE! Static initializers only run when code in a running method refers to a static variable. // These statics will never be initialized because none of them were referenced in Main(). // static Meter s_meter = new Meter("HatCo.Store"); static ObservableCounter<int> s_coatsSold = s_meter.CreateObservableCounter<int>("hatco.store.coats_sold", () => s_rand.Next(1,10)); static Random s_rand = new Random(); static void Main(string[] args) { Console.ReadLine(); } }
Deskripsi dan satuan
Instrumen dapat menentukan deskripsi dan unit opsional. Nilai-nilai ini buram untuk semua perhitungan metrik tetapi dapat ditampilkan di UI alat pengumpulan untuk membantu teknisi memahami cara menginterpretasikan data. Hentikan proses contoh yang Anda mulai sebelumnya, dan ganti kode contoh di Program.cs
dengan:
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(name: "hatco.store.hats_sold",
unit: "{hats}",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has a transaction each 100ms that sells 4 hats
Thread.Sleep(100);
s_hatsSold.Add(4);
}
}
}
Jalankan proses baru dan gunakan penghitung dotnet seperti sebelumnya di shell kedua untuk melihat metrik:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.hats_sold ({hats}) 40
dotnet-counters saat ini tidak menggunakan teks deskripsi di UI, tetapi menampilkan satuan saat disediakan. In this case, you see "{hats}" has replaced the generic term "Count" that is visible in previous descriptions.
Praktik terbaik
API .NET memungkinkan string apa pun digunakan sebagai unit, tetapi sebaiknya gunakan UCUM, standar internasional untuk nama unit. The curly braces around "{hats}" is part of the UCUM standard, indicating that it is a descriptive annotation rather than a unit name with a standardized meaning like seconds or bytes.
Unit yang ditentukan dalam konstruktor harus menggambarkan satuan yang tepat untuk setiap pengukuran. Ini terkadang akan berbeda dari unit pada metrik akhir yang dilaporkan. In this example, each measurement is a number of hats, so "{hats}" is the appropriate unit to pass in the constructor. Alat pengumpulan bisa menghitung tingkat perubahan dan mengetahui sendiri bahwa unit yang tepat untuk laju yang dihitung adalah {hats}/detik.
Saat merekam pengukuran waktu, lebih memilih satuan detik yang dicatat sebagai titik mengambang atau nilai ganda.
Metrik multi-dimensi
Pengukuran juga dapat dikaitkan dengan pasangan kunci-nilai yang disebut tag yang memungkinkan data dikategorikan untuk analisis. Misalnya, HatCo mungkin ingin merekam tidak hanya jumlah topi yang dijual, tetapi juga ukuran dan warnanya. Saat menganalisis data nanti, teknisi HatCo dapat memecah total berdasarkan ukuran, warna, atau kombinasi keduanya.
Counter and Histogram tags can be specified in overloads of the Add and Record that take one or more KeyValuePair
arguments. Misalnya:
s_hatsSold.Add(2,
new KeyValuePair<string, object?>("product.color", "red"),
new KeyValuePair<string, object?>("product.size", 12));
Ganti kode Program.cs
kemudian jalankan ulang aplikasi dan dotnet-counters seperti sebelumnya.
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has a transaction, every 100ms, that sells two size 12 red hats, and one size 19 blue hat.
Thread.Sleep(100);
s_hatsSold.Add(2,
new KeyValuePair<string,object?>("product.color", "red"),
new KeyValuePair<string,object?>("product.size", 12));
s_hatsSold.Add(1,
new KeyValuePair<string,object?>("product.color", "blue"),
new KeyValuePair<string,object?>("product.size", 19));
}
}
}
Penghitung Dotnet sekarang menunjukkan kategorisasi dasar:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.hats_sold (Count)
product.color product.size
blue 19 73
red 12 146
For ObservableCounter and ObservableGauge, tagged measurements can be provided in the callback passed to the constructor:
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static void Main(string[] args)
{
s_meter.CreateObservableGauge<int>("hatco.store.orders_pending", GetOrdersPending);
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
static IEnumerable<Measurement<int>> GetOrdersPending()
{
return new Measurement<int>[]
{
// pretend these measurements were read from a real queue somewhere
new Measurement<int>(6, new KeyValuePair<string,object?>("customer.country", "Italy")),
new Measurement<int>(3, new KeyValuePair<string,object?>("customer.country", "Spain")),
new Measurement<int>(1, new KeyValuePair<string,object?>("customer.country", "Mexico")),
};
}
}
Saat dijalankan dengan penghitung dotnet seperti sebelumnya, hasilnya adalah:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.orders_pending
customer.country
Italy 6
Mexico 1
Spain 3
Praktik terbaik
Meskipun API memungkinkan objek apa pun digunakan sebagai nilai tag, jenis numerik dan string diantisipasi oleh alat pengumpulan. Jenis lain mungkin atau mungkin tidak didukung oleh alat koleksi tertentu.
Kami menyarankan nama tag mengikuti panduan penamaan OpenTelemetry , yang menggunakan nama hierarkis bertitik kecil dengan karakter '_' untuk memisahkan beberapa kata dalam elemen yang sama. Jika nama tag digunakan kembali dalam metrik yang berbeda atau rekaman telemetri lainnya, maka nama tersebut harus memiliki arti dan set nilai hukum yang sama di mana pun mereka digunakan.
Contoh nama tag:
customer.country
store.payment_method
store.purchase_result
Beware of having very large or unbounded combinations of tag values being recorded in practice. Meskipun implementasi .NET API dapat menanganinya, alat pengumpulan kemungkinan akan mengalokasikan penyimpanan untuk data metrik yang terkait dengan setiap kombinasi tag dan ini bisa menjadi sangat besar. Misalnya, tidak masalah jika HatCo memiliki 10 warna topi yang berbeda dan ukuran topi 25 hingga 10 * 25 = 250 total penjualan untuk dilacak. Namun, jika HatCo menambahkan tag ketiga yang merupakan CustomerID untuk penjualan dan mereka menjual ke 100 juta pelanggan di seluruh dunia, sekarang kemungkinan sekarang ada miliaran kombinasi tag yang berbeda yang dicatat. Sebagian besar alat pengumpulan metrik akan menghilangkan data agar tetap dalam batas teknis atau mungkin ada biaya moneter yang besar untuk mencakup penyimpanan dan pemrosesan data. Implementasi setiap alat koleksi akan menentukan batasnya, tetapi kemungkinan kurang dari 1000 kombinasi untuk satu instrumen aman. Apa pun di atas kombinasi 1000 akan mengharuskan alat pengumpulan untuk menerapkan pemfilteran atau direkayasa untuk beroperasi pada skala tinggi. Implementasi histogram cenderung menggunakan memori yang jauh lebih banyak daripada metrik lain, sehingga batas aman bisa 10-100 kali lebih rendah. Jika Anda mengantisipasi sejumlah besar kombinasi tag unik, maka log, database transaksional, atau sistem pemrosesan big data mungkin merupakan solusi yang lebih tepat untuk beroperasi pada skala yang diperlukan.
Untuk instrumen yang akan memiliki kombinasi tag dalam jumlah yang sangat besar, lebih suka menggunakan jenis penyimpanan yang lebih kecil untuk membantu mengurangi overhead memori. Misalnya, menyimpan
short
untukCounter<short>
hanya menempati 2 byte per kombinasi tag, sedangkandouble
untukCounter<double>
menempati 8 byte per kombinasi tag.Alat koleksi didorong untuk mengoptimalkan kode yang menentukan kumpulan nama tag yang sama dalam urutan yang sama untuk setiap panggilan untuk merekam pengukuran pada instrumen yang sama. Untuk kode berkinerja tinggi yang perlu sering memanggil Add dan Record, lebih suka menggunakan urutan nama tag yang sama untuk setiap panggilan.
The .NET API is optimized to be allocation-free for Add and Record calls with three or fewer tags specified individually. Untuk menghindari alokasi dengan jumlah tag yang lebih besar, gunakan TagList. Secara umum, beban performa dari pemanggilan ini meningkat saat lebih banyak tag digunakan.
Nota
OpenTelemetry menyebut tag sebagai 'atribut'. Ini adalah dua nama yang berbeda untuk fungsionalitas yang sama.
Using Advice to customize Histogram instruments
When using Histograms, it is the responsibility of the tool or library collecting the data to decide how best to represent the distribution of values that were recorded. Strategi umum (dan mode default saat menggunakan OpenTelemetry) adalah membagi rentang nilai yang mungkin menjadi sub-rentang yang disebut wadah dan melaporkan berapa banyak nilai yang direkam di setiap wadah. Misalnya alat dapat membagi angka menjadi tiga wadah, yang kurang dari 1, yang antara 1-10, dan yang lebih besar dari 10. Jika aplikasi Anda merekam nilai 0,5, 6, 0,1, 12, maka akan ada dua titik data di wadah pertama, satu di wadah kedua, dan satu lagi di wadah ketiga.
Alat atau pustaka yang mengumpulkan data Histogram bertanggung jawab untuk menentukan wadah yang akan digunakannya. Konfigurasi wadah default saat menggunakan OpenTelemetry adalah: [ 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000 ]
.
Nilai default mungkin tidak mengarah pada granularitas terbaik untuk setiap Histogram. Misalnya, durasi permintaan yang kurang dari satu detik semuanya akan masuk ke dalam kategori 0
.
Alat atau pustaka yang mengumpulkan data Histogram mungkin menawarkan mekanisme untuk memungkinkan pengguna menyesuaikan konfigurasi wadah. Misalnya, OpenTelemetry mendefinisikan View API. Namun, ini memerlukan tindakan pengguna akhir dan menjadikannya tanggung jawab pengguna untuk memahami distribusi data dengan cukup baik untuk memilih wadah yang benar.
Untuk meningkatkan pengalaman versi 9.0.0
dari paket System.Diagnostics.DiagnosticSource
memperkenalkan API (InstrumentAdvice<T>).
API InstrumentAdvice
dapat digunakan oleh penulis instrumentasi untuk menentukan serangkaian batas wadah default yang direkomendasikan untuk Histogram tertentu. Alat atau pustaka yang mengumpulkan data Histogram kemudian dapat memilih untuk menggunakan nilai-nilai tersebut saat mengonfigurasi agregasi yang mengarah ke pengalaman onboarding yang lebih mulus bagi pengguna. Ini didukung dalam OpenTelemetry .NET SDK pada versi 1.10.0.
Penting
Secara umum lebih banyak wadah akan menyebabkan data yang lebih tepat untuk Histogram tertentu tetapi setiap wadah memerlukan memori untuk menyimpan detail agregat dan ada biaya CPU untuk menemukan wadah yang benar saat memproses pengukuran. It is important to understand the tradeoffs between precision and CPU/memory consumption when choosing the number of buckets to recommend via the InstrumentAdvice
API.
Kode berikut menunjukkan contoh menggunakan API InstrumentAdvice
untuk mengatur wadah default yang direkomendasikan.
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Histogram<double> s_orderProcessingTime = s_meter.CreateHistogram<double>(
name: "hatco.store.order_processing_time",
unit: "s",
description: "Order processing duration",
advice: new InstrumentAdvice<double> { HistogramBucketBoundaries = [0.01, 0.05, 0.1, 0.5, 1, 5] });
static Random s_rand = new Random();
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
// Pretend our store has one transaction each 100ms
Thread.Sleep(100);
// Pretend that we measured how long it took to do the transaction (for example we could time it with Stopwatch)
s_orderProcessingTime.Record(s_rand.Next(5, 15) / 1000.0);
}
}
}
Informasi tambahan
For more details about explicit bucket Histograms in OpenTelemetry see:
Menguji metrik kustom
Dimungkinkan untuk menguji metrik kustom apa pun yang Anda tambahkan menggunakan MetricCollector<T>. Jenis ini memudahkan untuk merekam pengukuran dari instrumen tertentu dan menegaskan nilainya sudah benar.
Test with dependency injection
Kode berikut menunjukkan contoh kasus pengujian untuk komponen kode yang menggunakan injeksi dependensi dan IMeterFactory.
public class MetricTests
{
[Fact]
public void SaleIncrementsHatsSoldCounter()
{
// Arrange
var services = CreateServiceProvider();
var metrics = services.GetRequiredService<HatCoMetrics>();
var meterFactory = services.GetRequiredService<IMeterFactory>();
var collector = new MetricCollector<int>(meterFactory, "HatCo.Store", "hatco.store.hats_sold");
// Act
metrics.HatsSold(15);
// Assert
var measurements = collector.GetMeasurementSnapshot();
Assert.Equal(1, measurements.Count);
Assert.Equal(15, measurements[0].Value);
}
// Setup a new service provider. This example creates the collection explicitly but you might leverage
// a host or some other application setup code to do this as well.
private static IServiceProvider CreateServiceProvider()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddMetrics();
serviceCollection.AddSingleton<HatCoMetrics>();
return serviceCollection.BuildServiceProvider();
}
}
Setiap objek MetricCollector merekam semua pengukuran untuk satu Instrumen. Jika Anda perlu memverifikasi pengukuran dari beberapa instrumen, buat satu MetricCollector untuk masing-masing instrumen.
Test without dependency injection
Dimungkinkan juga untuk menguji kode yang menggunakan objek Meter global bersama di bidang statis, tetapi pastikan pengujian tersebut dikonfigurasi untuk tidak berjalan secara paralel. Karena objek Meter sedang dibagikan, MetricCollector dalam satu pengujian akan mengamati pengukuran yang dibuat dari pengujian lain yang berjalan secara paralel.
class HatCoMetricsWithGlobalMeter
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
public void HatsSold(int quantity)
{
s_hatsSold.Add(quantity);
}
}
public class MetricTests
{
[Fact]
public void SaleIncrementsHatsSoldCounter()
{
// Arrange
var metrics = new HatCoMetricsWithGlobalMeter();
// Be careful specifying scope=null. This binds the collector to a global Meter and tests
// that use global state should not be configured to run in parallel.
var collector = new MetricCollector<int>(null, "HatCo.Store", "hatco.store.hats_sold");
// Act
metrics.HatsSold(15);
// Assert
var measurements = collector.GetMeasurementSnapshot();
Assert.Equal(1, measurements.Count);
Assert.Equal(15, measurements[0].Value);
}
}