Bagikan melalui


Membuat metrik

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 System.Diagnostics.Metrics menggunakan API 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.

Catatan

.NET memiliki beberapa API metrik lama, yaitu EventCounters dan System.Diagnostics.PerformanceCounter, yang tidak tercakup di sini. Untuk mempelajari selengkapnya tentang alternatif ini, lihat Membandingkan 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 agar Program.cs sesuai:

> dotnet new console
> dotnet add package System.Diagnostics.DiagnosticSource
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);
        }
    }
}

Jenisnya System.Diagnostics.Metrics.Meter adalah titik masuk untuk pustaka untuk membuat sekelompok instrumen bernama. Instrumen merekam pengukuran numerik yang diperlukan untuk menghitung metrik. Di sini kami menggunakan CreateCounter untuk membuat instrumen Penghitung bernama "hatco.store.hats_sold". Selama setiap transaksi berpura-pura, kode memanggil Add untuk merekam pengukuran topi yang dijual, 4 dalam hal 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. Untuk instrumen Penghitung, konvensinya adalah bahwa alat pengumpulan menunjukkan jumlah total dan/atau tingkat peningkatan jumlah.

Parameter int generik pada Counter<int> dan CreateCounter<int>(...) mendefinisikan bahwa penghitung ini harus dapat menyimpan nilai hingga Int32.MaxValue. Anda dapat menggunakan salah satu dari byte, , intshort, 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 variabel statis pustaka yang sadar AKAN dianggap sebagai anti-pola dan contoh DI di bawah ini menunjukkan pendekatan yang lebih idiomatik. Setiap pustaka atau subkomponen pustaka dapat (dan seringkali harus) membuat sendiri 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.

  • Nama yang diteruskan ke Meter konstruktor harus unik untuk membedakannya dari Meter lainnya. Kami merekomendasikan pedoman penamaan OpenTelemetry, yang menggunakan nama hierarkis putus-putus. Nama rakitan atau nama namespace layanan untuk kode yang diinstrumentasikan biasanya merupakan pilihan yang baik. Jika rakitan menambahkan instrumentasi untuk kode dalam rakitan independen kedua, nama harus didasarkan pada rakitan yang mendefinisikan Meter, bukan rakitan yang kodenya sedang diinstrumentasikan.

  • .NET tidak memberlakukan 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
  • API untuk membuat instrumen dan pengukuran rekaman aman untuk utas. 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 pengumpulan performa 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 penghitung dotnet, yang berguna untuk analisis ad-hoc. Anda juga dapat melihat tutorial pengumpulan metrik untuk alternatif lain. Jika alat penghitung dotnet 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 penghitung dotnet 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.

Mendapatkan Meter melalui injeksi dependensi

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 Host Generik, buat objek Meter menggunakan IMeterFactory. Mulai dari .NET 8, host akan secara otomatis mendaftar IMeterFactory dalam kontainer layanan atau Anda dapat mendaftarkan jenis secara manual dengan IServiceCollection memanggil AddMetrics. Pabrik meteran mengintegrasikan metrik dengan DI, menjaga Meter dalam koleksi layanan yang berbeda terisolasi satu sama lain bahkan jika mereka menggunakan nama yang identik. Ini sangat berguna untuk pengujian sehingga beberapa pengujian yang berjalan secara paralel hanya mengamati pengukuran yang dihasilkan dari dalam kasus pengujian yang sama.

Untuk mendapatkan Meter dalam jenis yang dirancang untuk DI, tambahkan IMeterFactory parameter ke konstruktor, lalu panggil 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);
    }
}

Daftarkan jenis dengan kontainer DI di 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 apa pun Meter yang dibuatnya, membuangnya saat kontainer DI dibuang. Tidak perlu menambahkan kode tambahan untuk dipanggil Dispose() pada Meter, dan tidak akan berpengaruh apa pun.

Jenis instrumen

Sejauh ini kami hanya menunjukkan Counter<T> instrumen, 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 pemanggil menyediakan pengukuran individual pada waktu arbitrer dan alat pengumpulan mengelola agregasi. Atau, penelepon dapat mengelola pengukuran agregat dan menyediakannya sesuai permintaan dalam panggilan balik.

Jenis instrumen yang saat ini tersedia:

  • Penghitung (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. Untuk alat yang hanya menunjukkan satu hal, tingkat perubahan disarankan. Misalnya, asumsikan Add() bahwa penelepon memanggil setiap detik sekali 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. Tingkat perubahan adalah (current_total - previous_total), jadi pada tiga detik alat melaporkan 7-0=7, dan setelah enam detik, alat melaporkan 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 Add() bahwa penelepon memanggil setiap detik sekali 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) - Instrumen ini mirip dengan Penghitung kecuali bahwa pemanggil sekarang bertanggung jawab untuk mempertahankan total agregat. Pemanggil menyediakan delegasi panggilan balik saat ObservableCounter dibuat dan panggilan balik dipanggil setiap kali alat perlu mengamati total saat ini. Misalnya, jika alat koleksi diperbarui setiap tiga detik, maka fungsi panggilan balik juga akan dipanggil setiap tiga detik. Sebagian besar alat akan memiliki total dan tingkat perubahan dalam total yang tersedia. Jika hanya satu yang dapat ditampilkan, tingkat perubahan disarankan. 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 penelepon sekarang bertanggung jawab untuk mempertahankan total agregat. Pemanggil menyediakan delegasi panggilan balik saat ObservableUpDownCounter dibuat dan panggilan balik dipanggil setiap kali alat perlu mengamati total saat ini. Misalnya, jika alat koleksi diperbarui setiap tiga detik, maka fungsi panggilan balik juga akan dipanggil setiap tiga detik. Nilai apa pun yang dikembalikan oleh panggilan balik akan ditampilkan di alat koleksi yang tidak berubah sebagai total.

  • ObservableGauge (CreateObservableGauge) - Instrumen ini memungkinkan pemanggil untuk memberikan panggilan balik di mana nilai terukur 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 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 dipanggil Record untuk merekam pengukuran ini selama interval pembaruan alat koleksi: 1,5,2,3,10,9,7,4,6,8. Alat koleksi mungkin melaporkan bahwa persentil ke-50, 90, dan ke-95 dari pengukuran ini masing-masing adalah 5, 9, dan 9.

Praktik terbaik saat memilih jenis instrumen

  • Untuk menghitung hal-hal, atau nilai lain yang hanya meningkat dari waktu ke waktu, gunakan Penghitung atau ObservableCounter. Pilih antara Penghitung dan ObservableCounter tergantung pada mana yang lebih mudah ditambahkan ke kode yang ada: panggilan API untuk setiap operasi kenaikan, atau panggilan balik yang akan membaca total saat ini dari variabel yang dipertahankan kode. Dalam jalur kode yang sangat panas di mana performa penting dan menggunakan Add akan membuat lebih dari satu juta panggilan per detik per utas, menggunakan ObservableCounter dapat menawarkan lebih banyak kesempatan untuk pengoptimalan.

  • Untuk hal-hal waktu, Histogram biasanya lebih disukai. Seringkali berguna untuk memahami ekor distribusi ini (persentil ke-90, ke-95, ke-99) daripada rata-rata atau total.

  • Kasus umum lainnya, seperti tingkat hit cache atau ukuran cache, antrean, dan file biasanya cocok untuk UpDownCounter atau ObservableUpDownCounter. 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.

Catatan

Jika Anda menggunakan versi .NET yang lebih lama atau paket DiagnosticSource NuGet yang tidak mendukung UpDownCounter dan ObservableUpDownCounter (sebelum versi 7), ObservableGauge sering kali merupakan pengganti yang baik.

Contoh jenis instrumen yang berbeda

Hentikan proses contoh yang dimulai sebelumnya, dan ganti kode contoh dengan Program.cs :

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(0.005, 0.015));
        }
    }
}

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

[HatCo.Store]
    hatco.store.coats_sold (Count / 1 sec)                                27
    hatco.store.hats_sold (Count / 1 sec)                                 36
    hatco.store.order_processing_time
        Percentile=50                                                      0.012
        Percentile=95                                                      0.014
        Percentile=99                                                      0.014
    hatco.store.orders_pending                                             5

Contoh ini menggunakan beberapa angka yang dihasilkan secara acak sehingga nilai Anda akan sedikit bervariasi. Anda dapat melihat bahwa hatco.store.hats_sold (Penghitung) dan hatco.store.coats_sold (ObservableCounter) keduanya muncul sebagai tarif. ObservableGauge, hatco.store.orders_pending, muncul sebagai nilai absolut. Penghitung Dotnet merender instrumen Histogram sebagai tiga statistik 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 metrik Histogram dalam jumlah besar (>100), Anda mungkin perlu memberi pengguna panduan untuk tidak mengaktifkan semuanya secara bersamaan, atau mengonfigurasi alat mereka untuk menyimpan 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. Mendukung pembacaan nilai yang di-cache dengan cepat, tidak mengembalikan pengukuran, atau memberikan pengecualian atas melakukan operasi yang berpotensi berjalan lama atau memblokir.

  • Panggilan balik ObservableCounter, ObservableUpDownCounter, dan ObservableGauge terjadi pada utas yang biasanya tidak disinkronkan dengan kode yang memperbarui nilai. 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 banyak 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 unit

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 dengan Program.cs :

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

[HatCo.Store]
    hatco.store.hats_sold ({hats} / 1 sec)                                40

penghitung dotnet saat ini tidak menggunakan teks deskripsi di UI, tetapi menampilkan unit saat disediakan. Dalam hal ini, Anda melihat "{hats}" telah menggantikan istilah generik "Hitung" yang terlihat dalam deskripsi sebelumnya.

Praktik Terbaik

  • API .NET memungkinkan string apa pun digunakan sebagai unit, tetapi sebaiknya gunakan UCUM, standar internasional untuk nama unit. Kurung kurawal di sekitar "{hats}" adalah bagian dari standar UCUM, menunjukkan bahwa itu adalah anotasi deskriptif daripada nama unit dengan arti standar seperti detik atau byte.

  • Unit yang ditentukan dalam konstruktor harus menjelaskan unit yang sesuai untuk pengukuran individual. Ini kadang-kadang akan berbeda dari unit pada metrik akhir. Dalam contoh ini, setiap pengukuran adalah sejumlah topi, jadi "{hats}" adalah unit yang sesuai untuk diteruskan dalam konstruktor. Alat pengumpulan menghitung laju dan diturunkan sendiri bahwa unit yang sesuai untuk metrik yang dihitung adalah {hats}/detik.

  • Saat merekam pengukuran waktu, lebih memilih satuan detik yang dicatat sebagai titik mengambang atau nilai ganda.

Metrik multidimensi

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.

Tag Penghitung dan Histogram dapat ditentukan dalam kelebihan beban Add dan Record yang mengambil satu atau beberapa KeyValuePair argumen. Contohnya:

s_hatsSold.Add(2,
               new KeyValuePair<string, object>("product.color", "red"),
               new KeyValuePair<string, object>("product.size", 12));

Ganti kode Program.cs dan jalankan ulang aplikasi dan penghitung dotnet 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

[HatCo.Store]
    hatco.store.hats_sold (Count / 1 sec)
        product.color=blue,product.size=19                                 9
        product.color=red,product.size=12                                 18

Untuk ObservableCounter dan ObservableGauge, pengukuran yang ditandai dapat disediakan dalam panggilan balik yang diteruskan ke konstruktor:

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

[HatCo.Store]
    hatco.store.orders_pending
        customer.country=Italy                                             6
        customer.country=Mexico                                            1
        customer.country=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
  • Berhati-hatilah dengan kombinasi nilai tag yang sangat besar atau tidak terbatas yang dicatat dalam praktiknya. 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 untuk Counter<short> hanya menempati 2 byte per kombinasi tag, sedangkan double untuk Counter<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 dipanggil Add dan Record sering, lebih suka menggunakan urutan nama tag yang sama untuk setiap panggilan.

  • .NET API dioptimalkan untuk bebas Add alokasi dan Record panggilan dengan tiga atau lebih sedikit tag yang ditentukan satu per satu. Untuk menghindari alokasi dengan jumlah tag yang lebih besar, gunakan TagList. Secara umum, overhead performa panggilan ini meningkat saat lebih banyak tag digunakan.

Catatan

OpenTelemetry menyebut tag sebagai 'atribut'. Ini adalah dua nama yang berbeda untuk fungsionalitas yang sama.

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.

Uji dengan injeksi dependensi

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.

Uji tanpa injeksi dependensi

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