Поради
Дотримуйтесь цих найкращих практик, щоб отримати максимальну вигоду від 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");
Інструменти
Зрозумійте та виберіть правильний тип інструмента.
.NET runtime надав кілька типів інструментів на основі OpenTelemetry Specification. Вибір правильного типу інструмента для вашого випадку використання є критично важливим для забезпечення правильних семантик і продуктивності. Ознайомтеся з розділом Вибір інструмента з додатковими вказівками для отримання додаткової інформації.
Уникайте надто частого створення інструментів (наприклад, Counter<T>). Інструменти є досить дорогими і призначені для повторного використання в усьому застосунку. Для більшості застосунків інструменти можна моделювати як статичні поля, доступні тільки для читання, або як синглтони за допомогою інʼєкції залежностей.
Уникайте недійсних імен інструментів.
OpenTelemetry не збиратиме метрики з інструментів, які використовують недійсні імена. Дізнайтеся про дійсну синтаксичну структуру в специфікації OpenTelemetry.
Уникайте зміни порядку теґів під час звітування про вимірювання. Наприклад:
Останній рядок коду має погану продуктивність, оскільки теґи не дотримуються однакового порядку:
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рекомендується через його кращу читабельність і зручність підтримання.
При передачі вимірювань з більш ніж 8 теґами API виділяє памʼять на гарячому шляху коду. Ви ПОВИННІ намагатися, щоб кількість теґів була менше або дорівнювала 8. Якщо ви перевищуєте цю кількість, перевірте, чи можна деякі теґи моделювати як ресурс, як показано тут.
Управління 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, якщо вони створені вами.
Як загальне правило:
- Якщо ви створюєте застосунок з інʼєкцією залежностей (DI, dependency injection) (наприклад, ASP.NET Core та .NET Worker), у більшості випадків ви повинні створити екземпляр
MeterProviderі дозволити DI керувати його життєвим циклом. Ознайомтеся з Початком роботи з метриками OpenTelemetry .NET за 5 хвилин — застосунок ASP.NET Core для отримання додаткової інформації. - Якщо ви створюєте застосунок без DI, створіть екземпляр
MeterProviderі керуйте життєвим циклом явно. Ознайомтеся з Початком роботи з метриками OpenTelemetry .NET за 5 хвилин — консольний застосунок для отримання додаткової інформації. - Якщо ви забудете звільнити екземпляр
MeterProviderперед завершенням застосунку, метрики можуть бути втрачені через відсутність належного скидання. - Якщо ви звільните екземпляр
MeterProviderзанадто рано, будь-які подальші вимірювання не будуть зібрані.
Управління памʼяттю
В OpenTelemetry вимірювання передаються через API метрик. SDK агрегує метрики за допомогою певних алгоритмів і стратегій управління памʼяттю для досягнення високої продуктивності та ефективності. Ось правила, яких дотримується OpenTelemetry .NET під час реалізації логіки агрегації метрик:
- Pre-Aggregation: агрегація відбувається в межах SDK.
- Обмеження кардинальності: логіка агрегації враховує обмеження кардинальності, тому SDK не використовує невизначену кількість памʼяті, коли відбувається вибух кардинальності.
- Попереднє виділення памʼяті: памʼять, що використовується логікою агрегації, виділяється під час ініціалізації SDK, тому SDK не повинен виділяти памʼять на льоту. Це робиться для уникнення спрацьовування збору сміття в гарячих кодових шляхах.
Приклад
Розглянемо наступний приклад:
- Протягом часового інтервалу (T0, T1]:
- value = 1, name =
apple, color =red - value = 2, name =
lemon, color =yellow
- value = 1, name =
- Протягом часового інтервалу (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
- value = 5, name =
Якщо ми агрегуємо та експортуємо метрики за допомогою Кумулятивної агрегації тимчасовості:
- (T0, T1]
- attributes: {name =
apple, color =red}, count:1 - attributes: {verb =
lemon, color =yellow}, count:2
- attributes: {name =
- (T0, T2]
- attributes: {name =
apple, color =red}, count:1 - attributes: {verb =
lemon, color =yellow}, count:2
- attributes: {name =
- (T0, T3]
- attributes: {name =
apple, color =red}, count:6 - attributes: {name =
apple, color =green}, count:2 - attributes: {verb =
lemon, color =yellow}, count:12
- attributes: {name =
Якщо ми агрегуємо та експортуємо метрики за допомогою Дельта агрегації тимчасовості:
- (T0, T1]
- attributes: {name =
apple, color =red}, count:1 - attributes: {verb =
lemon, color =yellow}, count:2
- attributes: {name =
- (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
- attributes: {name =
Попередня агрегація
Розглядаючи приклад з фруктами, ми бачимо, що під час (T2, T3] було зафіксовано 6 вимірювань. Замість того, щоб експортувати кожну окрему подію вимірювання, SDK агрегує їх і експортує лише узагальнені результати. Цей підхід, як показано на наступній діаграмі, називається попередньою агрегацією:
graph LR subgraph SDK Instrument --> | Measurements | Pre-Aggregation[Pre-Aggregation] end subgraph Collector Aggregation end Pre-Aggregation --> | Metrics | Aggregation
Попереднє агрегування має кілька переваг:
- Хоча обсяг обчислень залишається незмінним, обсяг переданих даних може бути значно зменшено за допомогою попереднього агрегування, що покращує загальну ефективність.
- Попереднє агрегування дозволяє застосовувати обмеження кардинальності під час ініціалізації SDK, в поєднанні з попереднім виділенням памʼяті, вони роблять поведінку збору метрик більш передбачуваною (наприклад, сервер під атакою відмови в обслуговуванні все ще надаватиме постійний обсяг метрик, а не затоплюватиме систему спостереження великим обсягом подій вимірювання).
Є випадки, коли користувачі можуть захотіти експортувати необроблені події вимірювання замість використання попереднього агрегування, як показано на наступній діаграмі. OpenTelemetry наразі не підтримує цей сценарій, якщо вас це цікавить, будь ласка, приєднайтеся до обговорення, відповівши на цей запит на функцію.
graph LR subgraph SDK Instrument end subgraph Collector Aggregation end Instrument --> | Measurements | Aggregation
Обмеження кардинальності
Кількість унікальних комбінацій атрибутів називається кардинальністю. Якщо взяти приклад з фруктами і знати, що ми можемо мати тільки яблука/лимони як назву та червоний/жовтий/зелений як колір, то можна сказати, що кардинальність дорівнює 6. Незалежно від того, скільки яблук і лимонів ми маємо, ми завжди можемо використовувати наступну таблицю, щоб підсумувати загальну кількість фруктів за назвою та кольором.
| Назва | Колір | Кількість |
|---|---|---|
| apple | red | 6 |
| apple | yellow | 0 |
| apple | green | 2 |
| lemon | red | 0 |
| lemon | yellow | 12 |
| lemon | green | 0 |
Іншими словами, нам відомо, скільки памʼяті та мережі потрібно для збору та передачі цих метрик, незалежно від шаблону трафіку.
У реальних застосунках кардинальність може бути надзвичайно високою. Уявіть, якщо у нас є довготривала служба, і ми збираємо метрики з 7 атрибутами, і кожен атрибут може мати 30 різних значень. Врешті-решт, нам, можливо, доведеться запамʼятати повний набір усіх 21,870,000,000 комбінацій! Цей вибух кардинальності є добре відомою проблемою в області метрик. Наприклад, це може призвести до несподівано високих витрат у системі спостереження або навіть бути використано хакерами для запуску атаки відмови в обслуговуванні.
Обмеження кардинальності — це механізм обмеження, який дозволяє системі збору метрик працювати передбачувано та надійно у разі надмірної кардинальності, незалежно від того, чи це сталося внаслідок зловмисної атаки, чи через помилки розробника під час написання коду.
OpenTelemetry має стандартне обмеження кардинальності 2000 на метрику. Це обмеження можна налаштувати на рівні окремої метрики за допомогою View API та параметра MetricStreamConfiguration.CardinalityLimit.
Станом на 1.10.0, як тільки метрика досягне обмеження кардинальності, будь-яке нове вимірювання, яке не може бути незалежно агреговане, буде автоматично агреговане за допомогою атрибута переповнення.
У версіях SDK 1.6.0 — 1.9.0 атрибут переповнення був експериментальною функцією, яку можна було активувати, встановивши змінну середовища OTEL_DOTNET_EXPERIMENTAL_METRICS_EMIT_OVERFLOW_ATTRIBUTE=true.
Станом на 1.10.0, коли використовується тимчасовість агрегації дельти, можна вибрати менше обмеження кардинальності, оскільки SDK відновить невикористані точки метрик.
У версіях SDK 1.7.0 — 1.9.0, відновлення точок метрик було експериментальною функцією, яку можна було активувати, встановивши змінну середовища OTEL_DOTNET_EXPERIMENTAL_METRICS_RECLAIM_UNUSED_METRIC_POINTS=true.
Попереднє виділення памʼяті
OpenTelemetry .NET SDK має на меті уникнути виділення памʼяті в гарячих кодових шляхах. Коли це поєднується з правильним використанням Metrics API, можна уникнути виділення памʼяті в купі в гарячих кодових шляхах.
Вам слід вимірювати виділення памʼяті в гарячих кодових шляхах і, ідеально, уникати будь-якого виділення памʼяті в купі під час використання API та SDK метрик, особливо коли ви використовуєте метрики для вимірювання продуктивності вашого застосунку (наприклад, ви не хочете витрачати 2 секунди на збирання сміття під час вимірювання операції, яка зазвичай займає 10 мілісекунд).
Кореляція метрик
В OpenTelemetry метрики можуть бути повʼязані з трейсами через екземпляри. Перегляньте Екземпляри для отримання додаткової інформації.
Збагачення метрик
Коли метрики збираються, вони зазвичай зберігаються в базі даних часових рядів. З точки зору зберігання та споживання метрики можуть бути багатовимірними. Взявши приклад з фруктами, ми маємо два вимірювання — “назва” та “колір”. Для базових сценаріїв всі вимірювання можуть бути повідомлені під час виклику Metrics API, однак для менш тривіальних сценаріїв вимірювання можуть надходити з різних джерел:
- Вимірювання повідомляються через Metrics API.
- Додаткові теґи, надані під час створення інструмента. Наприклад, перевантаження
Meter.CreateCounter<T>(name, unit, description, tags). - Додаткові теґи, надані під час створення вимірювача. Наприклад, перевантаження
Meter(name, version, tags, scope). - Ресурси, налаштовані на рівні
MeterProvider. - Додаткові атрибути, надані експортером або колектором. Наприклад, роботи та екземпляри у Prometheus.
Підтримка теґів рівня інструменту ще не реалізована в OpenTelemetry .NET, оскільки специфікація OpenTelemetry її не підтримує.
Як правило:
- Якщо вимірювання є статичним протягом усього життєвого циклу процесу (наприклад, імʼя машини, дата-центр):
- Якщо вимірювання застосовується до всіх метрик, змоделюйте його як ресурс, або ще краще, дозвольте колектору додати ці вимірювання, якщо це можливо (наприклад, колектор, що працює в тому ж дата-центрі, повинен знати імʼя дата-центру, а не покладатися на кожен екземпляр служби для звітування про імʼя дата-центру).
- Якщо вимірювання застосовується до підмножини метрик (наприклад, версія клієнтської бібліотеки), змоделюйте його як теґи рівня вимірювача.
- Якщо значення вимірювання є динамічним, звітуйте про нього через Metrics API.
Відбулися дискусії щодо додавання нової концепції під назвою «MeasurementProcessor», яка дозволяє динамічно додавати/видаляти виміри з вимірювань. Ця ідея не отримала підтримки через складність та наслідки для продуктивності. Більш детальну інформацію можна знайти в цьому PR #1938.
Загальні проблеми, які призводять до відсутності метрик
Meter, що використовується для створення інструментів, не додано доMeterProvider. Використовуйте методAddMeter, щоб увімкнути обробку для потрібних метрик.
Відгук
Чи це було корисним?
Дякуємо. Ми цінуємо ваші відгуки!
Будь ласка, дайте нам знати як ми можемо покращити цю сторінку. Ми цінуємо ваші відгуки!