Aracılığıyla paylaş


Ölçüm oluşturma

Bu makale şunlar için geçerlidir: ✔️ .NET Framework 4.6.1 ve sonraki sürümleri ✔️ .NET Core 6 ve sonraki sürümleri

.NET uygulamaları, önemli ölçümleri izlemek için System.Diagnostics.Metrics API'leri kullanılarak izlenebilir. Bazı ölçümler standart .NET kitaplıklarına dahil edilir, ancak uygulamalarınız ve kitaplıklarınız için uygun yeni özel ölçümler eklemek isteyebilirsiniz. Bu öğreticide yeni ölçümler ekleyecek ve kullanılabilir ölçüm türlerini anlayacaksınız.

Note

.NET'in burada ele alınmayan EventCounters ve System.Diagnostics.PerformanceCountergibi bazı eski ölçüm API'leri vardır. Bu alternatifler hakkında daha fazla bilgi edinmek için bkz. Ölçüm API'lerini karşılaştırma.

Özel ölçüm oluşturma

Önkoşullar: .NET Core 6 SDK veya sonraki bir sürümü

Create a new console application that references the System.Diagnostics.DiagnosticSource NuGet package version 8 or greater. .NET 8+ hedefli uygulamalar varsayılan olarak bu başvuruya sahiptir. Ardından, Program.cs kodu aşağıdakilerle eşleşecek şekilde güncelleştirin:

> dotnet new console
> dotnet package add System.Diagnostics.DiagnosticSource

(.NET 9 veya önceki bir SDK sürümü kullanıyorsanız, bunun yerine formu kullanın 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);
        }
    }
}

System.Diagnostics.Metrics.Meter türü, adlandırılmış bir enstrüman grubu oluşturmak için bir kütüphanenin giriş noktasıdır. Aletler, metrikleri hesaplamak için gereken sayısal ölçümleri kaydeder. Here we used CreateCounter to create a Counter instrument named "hatco.store.hats_sold". During each pretend transaction, the code calls Add to record the measurement of hats that were sold, 4 in this case. "hatco.store.hats_sold" aracı, satılan toplam şapka sayısı veya satılan şapka/sn gibi bu ölçümlerden hesaplanabilecek bazı ölçümleri örtük olarak tanımlar. Sonuç olarak, hangi ölçümlerin hesaplanması ve bu hesaplamaların nasıl gerçekleştirileceğini belirlemek ölçüm toplama araçlarına bağlıdır, ancak her bir araçta geliştiricinin amacını belirten bazı varsayılan kurallar vardır. Sayaç araçları için kural, toplama araçlarının toplam sayıyı ve/veya sayımın artma hızını göstermesidir.

int ve Counter<int> üzerinde CreateCounter<int>(...) genel parametresi, bu sayacın Int32.MaxValue'a kadar olan değerleri depolayabilmesi gerektiğini tanımlar. Depolamanız gereken verilerin boyutuna ve kesirli değerlerin gerekip gerekmediğine bağlı olarak byte, short, int, long, float, doubleveya decimal kullanabilirsiniz.

Uygulamayı çalıştırın ve şimdilik böyle kalsın. Ölçümleri bir sonraki adımda görüntüleyeceğiz.

> dotnet run
Press any key to exit

En iyi yöntemler

  • Bağımlılık Enjeksiyonu (DI) kapsayıcısında kullanılmak üzere tasarlanmayan kodlar için Metre'yi bir kez oluşturmalı ve statik bir değişkende depolamalısınız. For usage in DI-aware libraries static variables are considered an anti-pattern and the DI example below shows a more idiomatic approach. Each library or library subcomponent can (and often should) create its own Meter. Uygulama geliştiricilerinin ölçüm gruplarını ayrı olarak kolayca etkinleştirip devre dışı bırakabilmeyi takdir edeceğini düşünüyorsanız mevcut ölçümlerden birini yeniden kullanmak yerine yeni bir Ölçüm oluşturmayı göz önünde bulundurun.

  • 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. Assembly names or namespace names for code being instrumented are usually a good choice. 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, Araçlar için herhangi bir adlandırma düzenini zorlamaz, ancak aynı öğedeki birden çok sözcük arasında ayırıcı olarak küçük harfli noktalı hiyerarşik adlar ve alt çizgi ('_') kullananOpenTelemetry adlandırma yönergelerini izlemenizi öneririz. Tüm metrik araçları, nihai metrik adının bir parçası olarak Metre adını korumaz. Bu yüzden, aracın adını kendi başına küresel olarak benzersiz hale getirmek faydalı olur.

    Örnek araç adları:

    • contoso.ticket_queue.duration
    • contoso.reserved_tickets
    • contoso.purchased_tickets
  • The APIs to create instruments and record measurements are thread-safe. .NET kitaplıklarında çoğu örnek yöntemi, birden çok iş parçacığından aynı nesnede çağrıldığında eşitleme gerektirir, ancak bu durumda gerekli değildir.

  • Ölçümleri kaydetmek için alet API'leri (bu örnekteAdd) genellikle veri toplanmadığında <10 ns veya ölçümler yüksek performanslı bir koleksiyon kitaplığı veya aracı tarafından toplanırken onlarca ile yüzlerce nanosaniye arasında çalışır. Bu, bu API'lerin çoğu durumda liberal olarak kullanılmasını sağlar, ancak performansa son derece duyarlı olan kodlara dikkat edin.

Yeni ölçümü görüntüleme

Ölçümleri depolamak ve görüntülemek için birçok seçenek vardır. Bu eğitimde anlık analiz için yararlı olan dotnet-counters aracı kullanılır. You can also see the metrics collection tutorial for other alternatives. dotnet-counters aracı henüz yüklü değilse, yüklemek için SDK'yı kullanın:

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

Örnek uygulama çalışmaya devam ederken yeni sayacı izlemek için dotnet-counters kullanın:

> 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

Beklendiği gibi, HatCo mağazasının her saniye sürekli olarak 4 şapka sattığını görebilirsiniz.

Get a Meter via dependency injection

Önceki örnekte Ölçüm, new ile oluşturup statik bir alana atayarak elde edildi. Bağımlılık ekleme (DI) kullanılırken statiklerin bu şekilde kullanılması iyi bir yaklaşım değildir. ASP.NET Core gibi DI veya Genel Ana Bilgisayarolan uygulamalar gibi DI kullanan kodda, IMeterFactorykullanarak Meter nesnesini oluşturun. Starting in .NET 8, hosts will automatically register IMeterFactory in the service container or you can manually register the type in any IServiceCollection by calling AddMetrics. Ölçüm fabrikası ölçümleri DI ile tümleştirerek Aynı adı kullansalar bile Farklı hizmet koleksiyonlarındaki Ölçümleri birbirinden yalıtılmış halde tutar. Bu, paralel çalışan birden çok testin yalnızca aynı test çalışması içinden üretilen ölçümleri gözlemlemesi için test için özellikle yararlıdır.

DI için tasarlanmış bir türde Ölçüm elde etmek için oluşturucuya bir IMeterFactory parametresi ekleyin, ardından Createçağrısı yapın. Bu örnekte bir ASP.NET Core uygulamasında IMeterFactory kullanımı gösterilmektedir.

Araçları tutmak için bir tür tanımlayın:

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

Ölçüm türünü ve kayıt değerlerini gerektiğinde ekleyin. Ölçüm türü DI'de kayıtlı olduğundan MVC denetleyicileri, minimum API'ler veya DI tarafından oluşturulan başka bir türle kullanılabilir:

app.MapPost("/complete-sale", ([FromBody] SaleModel model, HatCoMetrics metrics) =>
{
    // ... business logic such as saving the sale to a database ...

    metrics.HatsSold(model.QuantitySold);
});

En iyi yöntemler

  • System.Diagnostics.Metrics.Meter IDisposable uygular, ancak IMeterFactory oluşturduğu tüm Meter nesnelerin ömrünü otomatik olarak yönetir ve DI kapsayıcısı atıldığında bunları yok eder. Dispose() Meter çağırmak için ek kod eklemek gereksizdir ve hiçbir etkisi olmaz.

Alet türleri

Şu ana kadar yalnızca bir Counter<T> aracı gösterdik, ancak daha fazla araç türü mevcut. Araçlar iki şekilde farklılık gösterir:

  • Varsayılan ölçüm hesaplamaları - Ölçüm ölçümlerini toplayan ve analiz eden araçlar, alete bağlı olarak farklı varsayılan ölçümleri hesaplar.
  • Toplanan verilerin depolanması - En kullanışlı ölçümler, verilerin birçok ölçümden toplanması gerekir. One option is the caller provides individual measurements at arbitrary times and the collection tool manages the aggregation. Alternatively, the caller can manage the aggregate measurements and provide them on-demand in a callback.

Şu anda kullanılabilir araç türleri:

  • Sayacı (CreateCounter) - Bu araç zaman içinde artan bir değeri izler ve çağıran Addkullanarak artışları bildirir. Araçların çoğu toplamı ve toplamdaki değişiklik oranını hesaplar. For tools that only show one thing, the rate of change is recommended. Örneğin, çağıranın 1, 2, 4, 5, 4, 3 ardışık değerlerle her saniye Add() çağırdığını varsayalım. Toplama aracı her üç saniyede bir güncelleştirilirse, üç saniye sonra toplam 1+2+4=7 ve altı saniye sonra toplam 1+2+4+5+4+3=19 olur. Değişiklik oranı (current_total - previous_total) şeklindedir, bu nedenle araç üç saniyede 7-0=7 ve altı saniye sonra 19-7=12 değerini bildirir.

  • UpDownCounter (CreateUpDownCounter) - Bu araç, zaman içinde artabilecek veya düşebilecek bir değeri izler. Çağıran, Addkullanarak artışları ve düşüşleri bildirir. Örneğin, çağıranın 1, 5, -2, 3, -1, -3 ardışık değerlerle her saniye Add() çağırdığını varsayalım. Toplama aracı her üç saniyede bir güncelleştirilirse, üç saniye sonra toplam 1+5-2=4 ve altı saniye sonra toplam 1+5-2+3-1-3=3 olur.

  • ObservableCounter (CreateObservableCounter) - Bu araç Sayaç'a benzer, ancak çağıran artık toplamın korunmasından sorumludur. The caller provides a callback delegate when the ObservableCounter is created and the callback is invoked whenever tools need to observe the current total. Örneğin, bir koleksiyon aracı her üç saniyede bir güncelleştirilirse, geri çağırma işlevi de üç saniyede bir çağrılır. Çoğu araç, kullanılabilir toplamda hem toplam hem de değişiklik oranına sahip olur. If only one can be shown, rate of change is recommended. Geri arama ilk çağrıda 0, üç saniye sonra yeniden çağrıldığında 7 ve altı saniye sonra çağrıldığında 19 döndürürse, araç bu değerleri toplamlar olarak değişmeden bildirir. Değişiklik oranı için araç üç saniye sonra 7-0=7 ve altı saniye sonra 19-7=12 değerini gösterir.

  • ObservableUpDownCounter (CreateObservableUpDownCounter) - This instrument is similar to UpDownCounter except that the caller is now responsible for maintaining the aggregated total. The caller provides a callback delegate when the ObservableUpDownCounter is created and the callback is invoked whenever tools need to observe the current total. Örneğin, bir koleksiyon aracı her üç saniyede bir güncelleştirilirse, geri çağırma işlevi de üç saniyede bir çağrılır. Geri çağırma fonksiyonu tarafından döndürülen değer, toplama aracında toplam olarak değiştirilmeden gösterilecektir.

  • Ölçer (CreateGauge) - Bu araç, çağıranın Record yöntemini kullanarak ölçümün geçerli değerini ayarlamasına olanak tanır. Yöntemi yeniden çağırarak değer istediğiniz zaman güncelleştirilebilir ve ölçüm toplama aracı en son ayarlanan değeri görüntüler.

  • ObservableGauge (CreateObservableGauge) - This instrument allows the caller to provide a callback where the measured value is passed through directly as the metric. Koleksiyon aracı her güncelleştirildiğinde geri çağırım fonksiyonu çağrılır ve fonksiyon tarafından döndürülen değer araçta görüntülenir.

  • Histogram (CreateHistogram) - Bu araç ölçümlerin dağılımını izler. Bir ölçü kümesini açıklamanın tek bir kurallı yolu yoktur, ancak histogramları veya hesaplanan yüzdebirlik değerleri kullanmak için araçlar önerilir. Örneğin, çağıranın toplama aracının güncelleştirme aralığı sırasında bu ölçümleri kaydetmek için Record çağırmış olduğunu varsayalım: 1,5,2,3,10,9,7,4,6,8. Bir toplama aracı, bu ölçümlerin 50. , 90. ve 95. yüzdebirlik değerlerinin sırasıyla 5, 9 ve 9 olduğunu bildirebilir.

    Note

    For details about how to set the recommended bucket boundaries when creating a Histogram instrument see: Using Advice to customize Histogram instruments.

Bir alet türü seçerken en iyi yöntemler

  • Öğeleri veya yalnızca zaman içinde artan diğer değerleri saymak için Counter veya ObservableCounter kullanın. Mevcut koda eklenmesi daha kolay olana bağlı olarak Counter ve ObservableCounter arasında seçim yapın: her artış işlemi için bir API çağrısı veya kodun koruduğu bir değişkenden geçerli toplamı okuyacak bir geri çağırma fonksiyonu. Performansın önemli olduğu son derece sık erişimli kod yollarında ve Add kullanılması iş parçacığı başına saniyede bir milyondan fazla çağrı oluşturabilir ve ObservableCounter'ın kullanılması iyileştirme için daha fazla fırsat sunabilir.

  • Zamanlama için histogram genellikle tercih edilir. Genellikle ortalamalar veya toplamlar yerine bu dağılımların kuyruğunu (90. , 95. , 99. yüzdebirlik) anlamak yararlıdır.

  • Önbellek isabet oranları veya önbellek, kuyruk ve dosya boyutları gibi diğer yaygın durumlar genellikle UpDownCounter veya ObservableUpDownCounteriçin uygundur. Choose between them depending on which is easier to add to the existing code: either an API call for each increment and decrement operation or a callback that will read the current value from a variable the code maintains.

Note

.NET'in eski bir sürümünü veya UpDownCounter ve ObservableUpDownCounter (sürüm 7'den önce) desteklemeyen bir DiagnosticSource NuGet paketi kullanıyorsanız, ObservableGauge genellikle iyi bir alternatiftir.

Farklı alet türleri örneği

Daha önce başlatılan örnek işlemi durdurun ve Program.cs'daki örnek kodu şununla değiştirin:

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

Run the new process and use dotnet-counters as before in a second shell to view the metrics:

> 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

Bu örnekte, değerlerinizin biraz değişmesi için rastgele oluşturulan bazı sayılar kullanılır. Dotnet sayaçları Histogram ölçüm araçlarını üç yüzdelik istatistik (50. yüzdelik, 95. yüzdelik ve 99. yüzdelik) olarak işler, ancak diğer araçlar dağılımı farklı özetleyebilir veya daha fazla yapılandırma seçeneği sunabilir.

En iyi yöntemler

  • Histogramlar, bellekte diğer ölçüm türlerinden çok daha fazla veri depolama eğilimindedir. Ancak, tam bellek kullanımı kullanılan koleksiyon aracı tarafından belirlenir. Histogram ölçümlerinin büyük bir sayısını (>100) tanımlıyorsanız, kullanıcılara tümünü aynı anda etkinleştirmeme veya duyarlılığı azaltarak araçlarını bellek tasarrufu sağlayacak şekilde yapılandırma konusunda rehberlik vermeniz gerekebilir. Bazı koleksiyon araçlarının aşırı bellek kullanımını önlemek için izleyecekleri eşzamanlı Histogram sayısı üzerinde sabit sınırları olabilir.

  • Tüm gözlemlenebilir araçlar için geri çağırmalar sırayla çağrılır, bu nedenle uzun süren geri çağırmalar tüm ölçümlerin toplanmasını geciktirebilir veya engelleyebilir. Favor quickly reading a cached value, returning no measurements, or throwing an exception over performing any potentially long-running or blocking operation.

  • ObservableCounter, ObservableUpDownCounter ve ObservableGauge geri çağırmaları genellikle değerleri güncelleştiren kodla eşitlenmemiş bir iş parçacığında gerçekleşir. Bellek erişimini eşitlemek veya eşitlenmemiş erişimin kullanılmasından kaynaklanabilir tutarsız değerleri kabul etmek sizin sorumluluğunuzdadır. Erişimi eşitlemek için yaygın yaklaşımlar, kilit kullanmak veya Volatile.Read ve Volatile.Writeçağırmaktır.

  • CreateObservableGauge ve CreateObservableCounter işlevleri bir izleme nesnesi döndürür, ancak çoğu durumda nesneyle daha fazla etkileşime gerek olmadığından bunu bir değişkene kaydetmeniz gerekmez. Bunu diğer araçlar için yaptığımız gibi statik bir değişkene atamak yasaldır ancak hataya açıktır çünkü C# statik başlatma yavaştır ve değişkene genellikle hiçbir zaman başvurulmaz. İşte sorunun bir örneği:

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

Açıklamalar ve birimler

Araçlar isteğe bağlı açıklamalar ve birimler belirtebilir. Bu değerler tüm metrik hesaplamalarına göre belirsizdir, ancak mühendislerin verilerin nasıl yorumlanacağını anlamasına yardımcı olmak için toplama aracı kullanıcı arabiriminde gösterilebilir. Daha önce başlattığınız örnek işlemi durdurun ve Program.cs'daki örnek kodu şununla değiştirin:

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

Run the new process and use dotnet-counters as before in a second shell to view the metrics:

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 şu anda kullanıcı arabirimindeki açıklama metnini kullanmaz, ancak sağlandığında birimi gösterir. In this case, you see "{hats}" has replaced the generic term "Count" that is visible in previous descriptions.

En iyi yöntemler

  • .NET API'leri herhangi bir dizenin birim olarak kullanılmasına izin verir, ancak birim adları için uluslararası bir standart olan UCUMkullanmanızı öneririz. 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.

  • Oluşturucuda belirtilen birim, tek bir ölçüm için uygun birimleri açıklamalıdır. Bu bazen bildirilen son ölçümdeki birimlerden farklı olacaktır. In this example, each measurement is a number of hats, so "{hats}" is the appropriate unit to pass in the constructor. Koleksiyon aracı değişiklik oranını hesaplamış ve hesaplanmış hız ölçümü için uygun birimin {hats}/sn olduğunu kendi başına türetmiş olabilir.

  • Zaman ölçümlerini kaydederken, kayan nokta veya çift değer olarak kaydedilen saniye birimlerini tercih edin.

Çok boyutlu ölçümler

Ölçümler, verilerin analiz için kategorilere ayrılmasını sağlayan etiketler olarak adlandırılan anahtar-değer çiftleriyle de ilişkilendirilebilir. Örneğin, HatCo yalnızca satılan şapka sayısını değil, aynı zamanda hangi boyutta ve renkte olduklarını da kaydetmek isteyebilir. HatCo mühendisleri verileri daha sonra analiz ederken toplamları boyuta, renge veya her ikisinin herhangi bir bileşimine göre ayırabilir.

Counter and Histogram tags can be specified in overloads of the Add and Record that take one or more KeyValuePair arguments. For example:

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

Program.cs kodunu değiştirin ve uygulamayı ve dotnet-counters'ı daha önce olduğu gibi yeniden çalıştırın:

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

Dotnet sayaçları artık temel bir kategori gösteriyor:

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

Dotnet-counters ile daha önce olduğu gibi çalıştırıldığında sonuç şöyledir:

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

En iyi yöntemler

  • API, herhangi bir nesnenin etiket değeri olarak kullanılmasına izin vermesine rağmen, sayısal türler ve dizeler koleksiyon araçları tarafından tahmin edilir. Diğer türler belirli bir koleksiyon aracı tarafından desteklenebilir veya desteklenmeyebilir.

  • Etiket adlarının OpenTelemetry adlandırma yönergelerini izlemesini öneririz, aynı öğedeki birden çok sözcüğü ayırmak için '_' karakterleriyle küçük noktalı hiyerarşi adları kullanır. Etiket adları farklı ölçümlerde veya diğer telemetri kayıtlarında yeniden kullanılıyorsa, kullanıldıkları her yerde aynı anlama ve yasal değerler kümesine sahip olmaları gerekir.

    Örnek etiket adları:

    • customer.country
    • store.payment_method
    • store.purchase_result
  • Beware of having very large or unbounded combinations of tag values being recorded in practice. .NET API uygulaması bunu işleyebilse de, koleksiyon araçları büyük olasılıkla her etiket bileşimiyle ilişkili ölçüm verileri için depolama alanı ayırır ve bu çok büyük olabilir. Örneğin, HatCo'nun 10 farklı şapka rengine ve 25 şapka boyutuna sahip olması ve takip etmek için 10*25=250 satış toplamına kadar olması sorun değil. Ancak, HatCo satış için CustomerID olan üçüncü bir etiket eklediyse ve dünya çapında 100 milyon müşteriye satılıyorsa, şimdi büyük olasılıkla milyarlarca farklı etiket bileşimi kaydediliyor olabilir. Ölçüm toplama araçlarının çoğu, teknik sınırlar içinde kalmak için verileri düşürür veya veri depolama ve işleme maliyetlerini karşılamak için büyük parasal giderler ortaya çıkabilir. Her toplama aracının uygulanması sınırlarını belirler, ancak büyük olasılıkla bir alet için 1000'den az kombinasyon güvenlidir. 1000'in üzerindeki birleşimler için toplama aracının filtreleme uygulaması veya yüksek ölçekte çalışacak şekilde tasarlanmış olması gerekir. Histogram uygulamaları diğer ölçümlerden çok daha fazla bellek kullanma eğilimindedir, bu nedenle güvenli sınırlar 10-100 kat daha düşük olabilir. Çok sayıda benzersiz etiket bileşimi olmasını bekliyorsanız günlükler, işlem veritabanları veya büyük veri işleme sistemleri gerekli ölçekte çalışmak için daha uygun çözümler olabilir.

  • Çok fazla sayıda etiket bileşimine sahip olacak araçlar için bellek yükünü azaltmaya yardımcı olmak için daha küçük bir depolama türü kullanmayı tercih edin. Örneğin, short için Counter<short> depolamak etiket bileşimi başına yalnızca 2 bayt, double için Counter<double> ise etiket bileşimi başına 8 bayt kaplar.

  • Koleksiyon araçlarının, ölçümleri aynı alete kaydetmeye yönelik her çağrı için aynı etiket adları kümesini aynı sırada belirten kod için iyileştirmesi teşvik edilir. Add ve Record sık sık çağırması gereken yüksek performanslı kodlar için, her çağrı için aynı etiket adları dizisini kullanmayı tercih edin.

  • .NET API'si, Add ve Record çağrılarında tek tek belirtilen üç veya daha az etiket için bellek ayırma yapmadan çalışacak şekilde optimize edilmiştir. Daha fazla sayıda etiket içeren ayırmaları önlemek için TagListkullanın. Genel olarak, daha fazla etiket kullanıldıkçe bu çağrıların performans yükü artar.

Note

OpenTelemetry etiketleri 'öznitelikler' olarak ifade eder. Bunlar aynı işlev için iki farklı addır.

Using Advice to customize Histogram instruments

Histogramları kullanırken, kaydedilen değerlerin dağılımını en iyi şekilde nasıl temsil etmek gerektiğine karar vermek için verileri toplayan araç veya kitaplığın sorumluluğundadır. Yaygın bir strateji (ve OpenTelemetrykullanılırken varsayılan mod ), olası değer aralığını demet adı verilen alt aralıklara bölmek ve her demette kayıtlı değer sayısını raporlamaktır. Örneğin bir araç sayıları 1'den küçük olanlar, 1-10 arasındakiler ve 10'dan büyük olanlar olmak üzere üç demete bölebilir. Uygulamanız 0,5, 6, 0,1, 12 değerlerini kaydettiyse ilk demette biri ikinci, diğeri 3. demet olmak üzere iki veri noktası olacaktır.

Histogram verilerini toplayan araç veya kitaplık, kullanacağı demetleri tanımlamakla sorumludur. OpenTelemetry kullanılırken varsayılan demet yapılandırması: [ 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000 ].

Varsayılan değerler her Histogram için en iyi ayrıntı düzeyine neden olmayabilir. Örneğin, bir saniyenin altındaki istek sürelerinin tümü 0 kovasına girer.

Histogram verilerini toplayan araç veya kitaplık, kullanıcıların demet yapılandırmasını özelleştirmesine olanak sağlayan mekanizmalar sunabilir. For example, OpenTelemetry defines a View API. Ancak bu, son kullanıcı eylemi gerektirir ve doğru demetleri seçecek kadar veri dağıtımını iyi anlamak kullanıcının sorumluluğundadır.

Deneyimi geliştirmek için 9.0.0 paketinin System.Diagnostics.DiagnosticSource sürümü (InstrumentAdvice<T>) API'sini kullanıma sunar.

InstrumentAdvice API, araç yazarları tarafından önerilen varsayılan aralık sınırları kümesini belirli bir Histogram için belirtmek üzere kullanılabilir. Histogram verilerini toplayan araç veya kitaplık daha sonra toplamayı yapılandırırken bu değerleri kullanmayı seçebilir ve bu da kullanıcılar için daha sorunsuz bir ekleme deneyimi sağlar. Bu,1.10.0 sürümünden itibaren OpenTelemetry .NET SDK'sında desteklenir.

Önemli

Genel olarak daha fazla demet, belirli bir Histogram için daha hassas verilere yol açar, ancak her demet, toplanan ayrıntıları depolamak için belleğe ihtiyaç duyar ve bir ölçümü işlerken doğru demeti bulmak için CPU maliyeti vardır. InstrumentAdvice API aracılığıyla önerecek demet sayısını seçerken duyarlık ve CPU/bellek tüketimi arasındaki dengeleri anlamak önemlidir.

Aşağıdaki kodda önerilen varsayılan demetleri ayarlamak için InstrumentAdvice API'sinin kullanıldığı bir örnek gösterilmektedir.

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

Ek bilgiler

OpenTelemetry'deki açık demet Histogramları hakkında daha fazla ayrıntı için bkz:

Özel ölçümleri test edin

MetricCollector<T>kullanarak eklediğiniz özel ölçümleri test etmek mümkündür. Bu tür, belirli araçlardaki ölçümleri kaydetmeyi ve değerlerin doğru olduğunu onaylamayı kolaylaştırır.

Test with dependency injection

Aşağıdaki kodda bağımlılık ekleme ve IMeterFactory kullanan kod bileşenleri için örnek bir test çalışması gösterilmektedir.

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

Her MetricCollector nesnesi tek bir Alet için tüm ölçümleri kaydeder. Birden çok araçtan ölçümleri doğrulamanız gerekiyorsa, her biri için bir MetricCollector oluşturun.

Bağımlılık eklemeden test etme

Statik bir alanda paylaşılan genel Ölçüm nesnesi kullanan kodu test etmek de mümkündür, ancak bu tür testlerin paralel olarak çalıştırılmayacak şekilde yapılandırıldığından emin olun. Meter nesnesi paylaşıldığından, bir testteki MetricCollector paralel çalışan diğer testlerden oluşturulan ölçümleri gözlemler.

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