Поради

Дізнайтеся про найкращі практики використання OpenTelemetry .NET для метрик

Дотримуйтесь цих найкращих практик, щоб отримати максимальну вигоду від OpenTelemetry .NET для метрик.

Версія пакунка

Використовуйте System.Diagnostics.Metrics APIs з останньою стабільною версією пакунка System.Diagnostics.DiagnosticSource, незалежно від версії середовища виконання .NET, що використовується:

  • Якщо ви використовуєте останню стабільну версію OpenTelemetry .NET SDK, вам не потрібно турбуватися про версію пакунка System.Diagnostics.DiagnosticSource, оскільки це вже враховано для вас через залежність пакунка.
  • Команда .NET runtime дотримується високих стандартів зворотної сумісності для System.Diagnostics.DiagnosticSource, навіть під час великих версій, тому сумісність тут не є проблемою.
  • Ознайомтеся з .NET офіційним документом для отримання додаткової інформації про System.Diagnostics.Metrics.

Metrics API

Meter

Уникайте надто частого створення System.Diagnostics.Metrics.Meter. Meter є досить дорогим і призначений для повторного використання в усьому застосунку. Для більшості застосунків його можна моделювати як статичне поле тільки для читання або як синглтон за допомогою введення залежностей.

Використовуйте розділене крапками UpperCamelCase як Meter.Name. У багатьох випадках використання повного імені класу може бути хорошим варіантом. Наприклад:

static readonly Meter MyMeter = new("MyCompany.MyProduct.MyLibrary", "1.0");

Інструменти

Зрозумійте та виберіть правильний тип інструмента.

OpenTelemetry Specification.NET Instrument Type
Asynchronous CounterObservableCounter<T>
Asynchronous GaugeObservableGauge<T>
Asynchronous UpDownCounterObservableUpDownCounter<T>
CounterCounter<T>
GaugeGauge<T>
HistogramHistogram<T>
UpDownCounterUpDownCounter<T>

Уникайте надто частого створення інструментів (наприклад, Counter<T>). Інструменти є досить дорогими і призначені для повторного використання в усьому застосунку. Для більшості застосунків інструменти можна моделювати як статичні поля, доступні тільки для читання, або як синглтони за допомогою інʼєкції залежностей.

Уникайте недійсних імен інструментів.

Уникайте зміни порядку теґів під час звітування про вимірювання. Наприклад:

counter.Add(2, new("name", "apple"), new("color", "red"));
counter.Add(3, new("name", "lime"), new("color", "green"));
counter.Add(5, new("name", "lemon"), new("color", "yellow"));
counter.Add(8, new("color", "yellow"), new("name", "lemon")); // bad perf

Використовуйте TagList правильно, щоб досягти найкращої продуктивності. Існує два різних способи передачі теґів до API інструменту:

  • Передайте теґи безпосередньо в API інструменту:

    counter.Add(100, new("Key1", "Value1"), new("Key2", "Value2"));
    
  • Використовуйте TagList:

    var tags = new TagList
    {
        { "DimName1", "DimValue1" },
        { "DimName2", "DimValue2" },
        { "DimName3", "DimValue3" },
        { "DimName4", "DimValue4" },
    };
    
    counter.Add(100, tags);
    

Як правило:

  • При звітуванні про вимірювання з 3 теґами або менше, передайте теґи безпосередньо в API інструменту.
  • При звітуванні про вимірювання з 4 до 8 теґів (включно), використовуйте TagList щоб уникнути виділення памʼяті, якщо уникнення тиску на GC є основною метою продуктивності. Для коду з високою продуктивністю, який вважає зменшення використання ЦП більш важливим (наприклад, для зменшення затримки, економії батареї тощо), ніж оптимізація виділення памʼяті, використовуйте профайлер і стрес-тест, щоб визначити, який підхід є кращим.
  • При звітуванні про вимірювання з більш ніж 8 теґами, два підходи мають дуже схожу продуктивність ЦП і виділення купи. TagList рекомендується через його кращу читабельність і зручність підтримання.

Управління MeterProvider

Уникайте частого створення екземплярів MeterProvider. MeterProvider є досить дорогим і призначений для повторного використання в усьому застосунку. Для більшості застосунків достатньо одного екземпляра MeterProvider на процес. Наприклад:

graph LR

subgraph Meter A
  InstrumentX
end

subgraph Meter B
  InstrumentY
  InstrumentZ
end

subgraph Meter Provider 2
  MetricReader2
  MetricExporter2
  MetricReader3
  MetricExporter3
end

subgraph Meter Provider 1
  MetricReader1
  MetricExporter1
end

InstrumentX --> | Measurements | MetricReader1
InstrumentY --> | Measurements | MetricReader1 --> MetricExporter1
InstrumentZ --> | Measurements | MetricReader2 --> MetricExporter2
InstrumentZ --> | Measurements | MetricReader3 --> MetricExporter3

Керуйте життєвим циклом екземплярів MeterProvider, якщо вони створені вами.

Як загальне правило:

Управління памʼяттю

В OpenTelemetry вимірювання передаються через API метрик. SDK агрегує метрики за допомогою певних алгоритмів і стратегій управління памʼяттю для досягнення високої продуктивності та ефективності. Ось правила, яких дотримується OpenTelemetry .NET під час реалізації логіки агрегації метрик:

  1. Pre-Aggregation: агрегація відбувається в межах SDK.
  2. Обмеження кардинальності: логіка агрегації враховує обмеження кардинальності, тому SDK не використовує невизначену кількість памʼяті, коли відбувається вибух кардинальності.
  3. Попереднє виділення памʼяті: памʼять, що використовується логікою агрегації, виділяється під час ініціалізації SDK, тому SDK не повинен виділяти памʼять на льоту. Це робиться для уникнення спрацьовування збору сміття в гарячих кодових шляхах.

Приклад

Розглянемо наступний приклад:

  • Протягом часового інтервалу (T0, T1]:
    • value = 1, name = apple, color = red
    • value = 2, name = lemon, color = yellow
  • Протягом часового інтервалу (T1, T2]:
    • не було отримано жодного фрукта
  • Протягом часового інтервалу (T2, T3]:
    • value = 5, name = apple, color = red
    • value = 2, name = apple, color = green
    • value = 4, name = lemon, color = yellow
    • value = 2, name = lemon, color = yellow
    • value = 1, name = lemon, color = yellow
    • value = 3, name = lemon, color = yellow

Якщо ми агрегуємо та експортуємо метрики за допомогою Кумулятивної агрегації тимчасовості:

  • (T0, T1]
    • attributes: {name = apple, color = red}, count: 1
    • attributes: {verb = lemon, color = yellow}, count: 2
  • (T0, T2]
    • attributes: {name = apple, color = red}, count: 1
    • attributes: {verb = lemon, color = yellow}, count: 2
  • (T0, T3]
    • attributes: {name = apple, color = red}, count: 6
    • attributes: {name = apple, color = green}, count: 2
    • attributes: {verb = lemon, color = yellow}, count: 12

Якщо ми агрегуємо та експортуємо метрики за допомогою Дельта агрегації тимчасовості:

  • (T0, T1]
    • attributes: {name = apple, color = red}, count: 1
    • attributes: {verb = lemon, color = yellow}, count: 2
  • (T1, T2]
    • nothing since we do not have any measurement received
  • (T2, T3]
    • attributes: {name = apple, color = red}, count: 5
    • attributes: {name = apple, color = green}, count: 2
    • attributes: {verb = lemon, color = yellow}, count: 10

Попередня агрегація

Розглядаючи приклад з фруктами, ми бачимо, що під час (T2, T3] було зафіксовано 6 вимірювань. Замість того, щоб експортувати кожну окрему подію вимірювання, SDK агрегує їх і експортує лише узагальнені результати. Цей підхід, як показано на наступній діаграмі, називається попередньою агрегацією:

graph LR

subgraph SDK
  Instrument --> | Measurements | Pre-Aggregation[Pre-Aggregation]
end

subgraph Collector
  Aggregation
end

Pre-Aggregation --> | Metrics | Aggregation

Попереднє агрегування має кілька переваг:

  1. Хоча обсяг обчислень залишається незмінним, обсяг переданих даних може бути значно зменшено за допомогою попереднього агрегування, що покращує загальну ефективність.
  2. Попереднє агрегування дозволяє застосовувати обмеження кардинальності під час ініціалізації SDK, в поєднанні з попереднім виділенням памʼяті, вони роблять поведінку збору метрик більш передбачуваною (наприклад, сервер під атакою відмови в обслуговуванні все ще надаватиме постійний обсяг метрик, а не затоплюватиме систему спостереження великим обсягом подій вимірювання).

Є випадки, коли користувачі можуть захотіти експортувати необроблені події вимірювання замість використання попереднього агрегування, як показано на наступній діаграмі. OpenTelemetry наразі не підтримує цей сценарій, якщо вас це цікавить, будь ласка, приєднайтеся до обговорення, відповівши на цей запит на функцію.

graph LR

subgraph SDK
  Instrument
end

subgraph Collector
  Aggregation
end

Instrument --> | Measurements | Aggregation

Обмеження кардинальності

Кількість унікальних комбінацій атрибутів називається кардинальністю. Якщо взяти приклад з фруктами і знати, що ми можемо мати тільки яблука/лимони як назву та червоний/жовтий/зелений як колір, то можна сказати, що кардинальність дорівнює 6. Незалежно від того, скільки яблук і лимонів ми маємо, ми завжди можемо використовувати наступну таблицю, щоб підсумувати загальну кількість фруктів за назвою та кольором.

НазваКолірКількість
applered6
appleyellow0
applegreen2
lemonred0
lemonyellow12
lemongreen0

Іншими словами, нам відомо, скільки памʼяті та мережі потрібно для збору та передачі цих метрик, незалежно від шаблону трафіку.

У реальних застосунках кардинальність може бути надзвичайно високою. Уявіть, якщо у нас є довготривала служба, і ми збираємо метрики з 7 атрибутами, і кожен атрибут може мати 30 різних значень. Врешті-решт, нам, можливо, доведеться запамʼятати повний набір усіх 21,870,000,000 комбінацій! Цей вибух кардинальності є добре відомою проблемою в області метрик. Наприклад, це може призвести до несподівано високих витрат у системі спостереження або навіть бути використано хакерами для запуску атаки відмови в обслуговуванні.

Обмеження кардинальності — це механізм обмеження, який дозволяє системі збору метрик працювати передбачувано та надійно у разі надмірної кардинальності, незалежно від того, чи це сталося внаслідок зловмисної атаки, чи через помилки розробника під час написання коду.

OpenTelemetry має стандартне обмеження кардинальності 2000 на метрику. Це обмеження можна налаштувати на рівні окремої метрики за допомогою View API та параметра MetricStreamConfiguration.CardinalityLimit.

Станом на 1.10.0, як тільки метрика досягне обмеження кардинальності, будь-яке нове вимірювання, яке не може бути незалежно агреговане, буде автоматично агреговане за допомогою атрибута переповнення.

Станом на 1.10.0, коли використовується тимчасовість агрегації дельти, можна вибрати менше обмеження кардинальності, оскільки SDK відновить невикористані точки метрик.

Попереднє виділення памʼяті

OpenTelemetry .NET SDK має на меті уникнути виділення памʼяті в гарячих кодових шляхах. Коли це поєднується з правильним використанням Metrics API, можна уникнути виділення памʼяті в купі в гарячих кодових шляхах.

Вам слід вимірювати виділення памʼяті в гарячих кодових шляхах і, ідеально, уникати будь-якого виділення памʼяті в купі під час використання API та SDK метрик, особливо коли ви використовуєте метрики для вимірювання продуктивності вашого застосунку (наприклад, ви не хочете витрачати 2 секунди на збирання сміття під час вимірювання операції, яка зазвичай займає 10 мілісекунд).

Кореляція метрик

В OpenTelemetry метрики можуть бути повʼязані з трейсами через екземпляри. Перегляньте Екземпляри для отримання додаткової інформації.

Збагачення метрик

Коли метрики збираються, вони зазвичай зберігаються в базі даних часових рядів. З точки зору зберігання та споживання метрики можуть бути багатовимірними. Взявши приклад з фруктами, ми маємо два вимірювання — “назва” та “колір”. Для базових сценаріїв всі вимірювання можуть бути повідомлені під час виклику Metrics API, однак для менш тривіальних сценаріїв вимірювання можуть надходити з різних джерел:

Як правило:

  • Якщо вимірювання є статичним протягом усього життєвого циклу процесу (наприклад, імʼя машини, дата-центр):
    • Якщо вимірювання застосовується до всіх метрик, змоделюйте його як ресурс, або ще краще, дозвольте колектору додати ці вимірювання, якщо це можливо (наприклад, колектор, що працює в тому ж дата-центрі, повинен знати імʼя дата-центру, а не покладатися на кожен екземпляр служби для звітування про імʼя дата-центру).
    • Якщо вимірювання застосовується до підмножини метрик (наприклад, версія клієнтської бібліотеки), змоделюйте його як теґи рівня вимірювача.
  • Якщо значення вимірювання є динамічним, звітуйте про нього через Metrics API.

Загальні проблеми, які призводять до відсутності метрик

  • Meter, що використовується для створення інструментів, не додано до MeterProvider. Використовуйте метод AddMeter, щоб увімкнути обробку для потрібних метрик.

Востаннє змінено December 26, 2024: [uk] Ukrainian documentation for OpenTelemetry (2a3c5648)