Пример. Использование OpenTelemetry с Prometheus, Grafana и Jaeger
В этом примере для сбора метрик используется Prometheus, Grafana для создания панели мониторинга и Jaeger для отображения распределенной трассировки.
1. Создание проекта
Создайте простой проект веб-API с помощью шаблона ASP.NET Core Empty в Visual Studio или следующей команды .NET CLI:
dotnet new web
2. Добавление метрик и определений действий
Следующий код определяет новую метрику (greetings.count
) для количества вызовов API и нового источника действия (OtPrGrYa.Example
).
// Custom metrics for the application
var greeterMeter = new Meter("OtPrGrYa.Example", "1.0.0");
var countGreetings = greeterMeter.CreateCounter<int>("greetings.count", description: "Counts the number of greetings");
// Custom ActivitySource for the application
var greeterActivitySource = new ActivitySource("OtPrGrJa.Example");
3. Создание конечной точки API
app.MapGet("/", SendGreeting);
async Task<String> SendGreeting(ILogger<Program> logger)
{
// Create a new Activity scoped to the method
using var activity = greeterActivitySource.StartActivity("GreeterActivity");
// Log a message
logger.LogInformation("Sending greeting");
// Increment the custom counter
countGreetings.Add(1);
// Add a tag to the Activity
activity?.SetTag("greeting", "Hello World!");
return "Hello World!";
}
Примечание.
Определение API не использует ничего конкретного для OpenTelemetry. Он использует API .NET для наблюдения.
4. Ссылка на пакеты OpenTelemetry
Используйте диспетчер пакетов NuGet или командную строку, чтобы добавить следующие пакеты NuGet:
<ItemGroup>
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
</ItemGroup>
Примечание.
Используйте последние версии, так как API OTel постоянно развиваются.
5. Настройка OpenTelemetry с правильными поставщиками
var tracingOtlpEndpoint = builder.Configuration["OTLP_ENDPOINT_URL"];
var otel = builder.Services.AddOpenTelemetry();
// Configure OpenTelemetry Resources with the application name
otel.ConfigureResource(resource => resource
.AddService(serviceName: builder.Environment.ApplicationName));
// Add Metrics for ASP.NET Core and our custom metrics and export to Prometheus
otel.WithMetrics(metrics => metrics
// Metrics provider from OpenTelemetry
.AddAspNetCoreInstrumentation()
.AddMeter(greeterMeter.Name)
// Metrics provides by ASP.NET Core in .NET 8
.AddMeter("Microsoft.AspNetCore.Hosting")
.AddMeter("Microsoft.AspNetCore.Server.Kestrel")
.AddPrometheusExporter());
// Add Tracing for ASP.NET Core and our custom ActivitySource and export to Jaeger
otel.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation();
tracing.AddHttpClientInstrumentation();
tracing.AddSource(greeterActivitySource.Name);
if (tracingOtlpEndpoint != null)
{
tracing.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(tracingOtlpEndpoint);
});
}
else
{
tracing.AddConsoleExporter();
}
});
Этот код использует инструментирование ASP.NET Core для получения метрик и действий из ASP.NET Core. Он также регистрирует Metrics
и ActivitySource
поставщики для метрик и трассировки соответственно.
В коде используется экспортер Prometheus для метрик, который использует ASP.NET Core для размещения конечной точки, поэтому вам также необходимо добавить следующее:
// Configure the Prometheus scraping endpoint
app.MapPrometheusScrapingEndpoint();
6. Запуск проекта
Запустите проект и получите доступ к API с помощью браузера или curl.
curl -k http://localhost:7275
Каждый раз, когда вы запрашиваете страницу, он увеличивает количество приветствий, которые были сделаны. Вы можете получить доступ к конечной точке метрик с помощью того же базового URL-адреса, используя путь /metrics
.
Выходные данные журнала 6.1
Операторы ведения журнала из кода выходные данные используются ILogger
. По умолчанию поставщик консоли включен, чтобы выходные данные направлялись в консоль.
Существует несколько вариантов выхода журналов из .NET:
stdout
иstderr
выходные данные перенаправляются в файлы журнала по системам контейнеров, таким как Kubernetes.- Используя библиотеки ведения журнала, которые будут интегрироваться с ILogger, к ним относятся Serilog или NLog.
- Использование поставщиков ведения журнала для OTel, таких как OTLP или экспортер Azure Monitor, показанный ниже.
6.2 Доступ к метрикам
Доступ к метрикам можно получить с помощью конечной /metrics
точки.
curl -k https://localhost:7275/
Hello World!
curl -k https://localhost:7275/metrics
# TYPE greetings_count counter
# HELP greetings_count Counts the number of greetings
greetings_count 1 1686894204856
# TYPE current_connections gauge
# HELP current_connections Number of connections that are currently active on the server.
current_connections{endpoint="127.0.0.1:7275"} 1 1686894204856
current_connections{endpoint="[::1]:7275"} 0 1686894204856
current_connections{endpoint="[::1]:5212"} 1 1686894204856
...
Выходные данные метрик — это моментальный снимок метрик во время запроса конечной точки. Результаты представлены в формате экспозиции Prometheus, который является удобочитаемым, но лучше понятен Prometheus. Этот раздел рассматривается на следующем этапе.
6.3 Доступ к трассировке
Если вы посмотрите на консоль для сервера, вы увидите выходные данные из экспортера трассировки консоли, который выводит информацию в удобочитаемом формате. Это должно отобразить два действия, один из пользовательских ActivitySource
и другой из ASP.NET Core:
Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18
Activity.SpanId: 3b7a891f55b97f1a
Activity.TraceFlags: Recorded
Activity.ParentSpanId: 645071fd0011faac
Activity.ActivitySourceName: OtPrGrYa.Example
Activity.DisplayName: GreeterActivity
Activity.Kind: Internal
Activity.StartTime: 2023-06-16T04:50:26.7675469Z
Activity.Duration: 00:00:00.0023974
Activity.Tags:
greeting: Hello World!
Resource associated with Activity:
service.name: OTel-Prometheus-Grafana-Jaeger
service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.5.0
Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18
Activity.SpanId: 645071fd0011faac
Activity.TraceFlags: Recorded
Activity.ActivitySourceName: Microsoft.AspNetCore
Activity.DisplayName: /
Activity.Kind: Server
Activity.StartTime: 2023-06-16T04:50:26.7672615Z
Activity.Duration: 00:00:00.0121259
Activity.Tags:
net.host.name: localhost
net.host.port: 7275
http.method: GET
http.scheme: https
http.target: /
http.url: https://localhost:7275/
http.flavor: 1.1
http.user_agent: curl/8.0.1
http.status_code: 200
Resource associated with Activity:
service.name: OTel-Prometheus-Grafana-Jaeger
service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.5.0
Первое — это созданное внутреннее настраиваемое действие. Второй создается ASP.NET для запроса и содержит теги для свойств HTTP-запроса. Вы увидите, что оба имеют одно и то же TraceId
, что идентифицирует одну транзакцию и в распределенной системе, можно использовать для сопоставления трассировок из каждой службы, связанной с транзакцией. Идентификаторы передаются в виде заголовков HTTP. ASP.NET Core назначает значение, TraceId
если оно отсутствует при получении запроса. HttpClient
включает заголовки по умолчанию для исходящих запросов. Каждое действие имеет сочетание SpanId
TraceId
и SpanId
уникально идентифицирует каждое действие. Действие Greeter
является родительским для действия HTTP через его ParentSpanId
, которое сопоставляется с SpanId
действием HTTP.
На более позднем этапе вы будете передавать эти данные в Jaeger, чтобы визуализировать распределенные трассировки.
7. Сбор метрик с помощью Prometheus
Prometheus — это коллекция метрик, агрегирование и система баз данных временных рядов. Вы настраиваете ее с конечными точками метрик для каждой службы и периодически удаляет значения и сохраняет их в базе данных временных рядов. Затем можно проанализировать и обработать их по мере необходимости.
Данные метрик, предоставляемые в формате Prometheus, — это моментальный снимок метрик процесса. Каждый раз, когда запрос выполняется в конечную точку метрик, он будет сообщать о текущих значениях. Хотя текущие значения интересны, они становятся более ценными по сравнению с историческими значениями, чтобы увидеть тенденции и определить, являются ли значения аномальными. Как правило, службы имеют пики использования на основе времени дня или мировых событий, таких как праздничные покупки. Сравнивая значения с историческими тенденциями, вы можете определить, являются ли они ненормальными, или если метрика медленно ухудшается с течением времени.
Процесс не сохраняет журнал этих моментальных снимков метрик. Добавление этой возможности в процесс может быть ресурсоемким. Кроме того, в распределенной системе обычно используется несколько экземпляров каждого узла, поэтому вы хотите иметь возможность собирать метрики из всех них, а затем агрегировать и сравнивать их с историческими значениями.
7.1. Установка и настройка Prometheus
Скачайте Prometheus для своей платформы https://prometheus.io/download/ и извлеките содержимое скачивания.
Просмотрите верхнюю часть выходных данных запущенного сервера, чтобы получить номер порта для конечной точки http . Например:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7275
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5212
Измените файл конфигурации YamL Prometheus, чтобы указать порт для конечной точки слом HTTP и задать более низкий интервал очистки. Например:
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
scrape_interval: 1s # poll very quickly for a more responsive demo
static_configs:
- targets: ["localhost:5212"]
Запустите Prometheus и просмотрите выходные данные порта, на котором он работает, обычно 9090:
>prometheus.exe
...
ts=2023-06-16T05:29:02.789Z caller=web.go:562 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090
Откройте этот URL-адрес в браузере. В пользовательском интерфейсе Prometheus теперь вы сможете запрашивать метрики. Используйте выделенную кнопку на следующем рисунке, чтобы открыть обозреватель метрик, в котором отображаются все доступные метрики.
Выберите метрику greetings_count
, чтобы просмотреть граф значений.
8. Использование Grafana для создания панели мониторинга метрик
Grafana — это продукт мониторинга, который может создавать панели мониторинга и оповещения на основе Prometheus или других источников данных.
Скачайте и установите версию OSS Grafana, следуя https://grafana.com/oss/grafana/ инструкциям для вашей платформы. После установки Grafana обычно выполняется через порт 3000, поэтому откройте http://localhost:3000
его в браузере. Вам потребуется войти в систему; Имя пользователя и пароль по умолчанию являются обоими admin
.
В меню гамбургера выберите подключения, а затем введите текст prometheus
, чтобы выбрать тип конечной точки. Выберите "Создать источник данных Prometheus", чтобы добавить новый источник данных.
Необходимо задать следующие свойства:
- URL-адрес сервера Prometheus:
http://localhost:9090/
изменение порта в соответствии с применимым
Нажмите кнопку "Сохранить" и " Проверить ", чтобы проверить конфигурацию.
После получения сообщения об успешном выполнении можно настроить панель мониторинга. Щелкните ссылку на панель мониторинга, показанную во всплывающем окне для сообщения об успешном выполнении.
Выберите " Добавить визуализацию", а затем выберите источник данных Prometheus, который вы только что добавили в качестве источника данных.
Должен появиться конструктор панели мониторинга. В нижней половине экрана можно определить запрос.
Выберите метрику greetings_count
и выберите команду "Выполнить запросы" , чтобы просмотреть результаты.
С помощью Grafana можно создавать сложные панели мониторинга, которые будут отслеживать любое количество метрик.
Каждая метрика в .NET может иметь дополнительные измерения, которые являются парами "ключ-значение", которые можно использовать для секционирования данных. Все метрики ASP.NET имеют ряд измерений, применимых к счетчику. Например, счетчик current-requests
Microsoft.AspNetCore.Hosting
имеет следующие измерения:
Атрибут | Тип | Описание | Примеры | Присутствие |
---|---|---|---|---|
method |
string |
Метод HTTP-запроса. | GET ; POST ; HEAD |
Всегда |
scheme |
string |
Схема URI, определяющая используемый протокол. | http ; https |
Всегда |
host |
string |
Имя локального HTTP-сервера, который получил запрос. | localhost |
Всегда |
port |
int |
Порт локального HTTP-сервера, который получил запрос. | 8080 |
Добавлено, если не по умолчанию (80 для http или 443 для https) |
Графы в Grafana обычно секционируются на основе каждого уникального сочетания измерений. Измерения можно использовать в запросах Grafana для фильтрации или статистической обработки данных. Например, если вы граф current_requests
, вы увидите значения, секционированные на основе каждого сочетания измерений. Чтобы отфильтровать только узел, добавьте операцию Sum
и используйте host
ее в качестве значения метки.
9. Распределенная трассировка с помощью Jaeger
На шаге 6 вы узнали, что информация распределенной трассировки предоставляется консоли. Эта информация отслеживает единицы работы с действиями. Некоторые действия создаются автоматически платформой, например одной, ASP.NET для представления обработки запроса, а библиотеки и код приложения также могут создавать действия. Пример приветствия имеет Greeter
действие. Действия коррелируются с помощью тегов TraceId
SpanId
, и ParentId
тегов.
Каждый процесс в распределенной системе создает собственный поток сведений о действиях, а также такие метрики, как и метрики, требуется система для сбора, хранения и сопоставления действий для визуализации работы для каждой транзакции. Jaeger — это проект с открытым исходным кодом для включения этой коллекции и визуализации.
Скачайте последний архив двоичного дистрибутива Jaeger для вашей платформы https://www.jaegertracing.io/download/.
Затем извлеките скачивание в локальное расположение, которое легко получить доступ. Запустите исполняемый файл jaeger-all-in-one(.exe ):
./jaeger-all-in-one --collector.otlp.enabled
Просмотрите выходные данные консоли, чтобы найти порт, где он прослушивает трафик OTLP через gRPC. Например:
{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"}
В этом выходных данных говорится, что он прослушивается 0.0.0.0:4317
, поэтому вы можете настроить этот порт в качестве назначения для экспортера OTLP.
AppSettings.json
Откройте файл для нашего проекта и добавьте следующую строку, изменив порт, если это применимо.
"OTLP_ENDPOINT_URL" : "http://localhost:4317/"
Перезапустите процесс приветствия, чтобы он смог забрать изменение свойства и начать перенаправление сведений трассировки в Jaeger.
Теперь вы сможете просмотреть пользовательский интерфейс http://localhost:16686/
Jaeger из веб-браузера.
Чтобы просмотреть список трассировок, выберите OTel-Prometheus-grafana-Jaeger
в раскрывающемся списке службы . При выборе трассировки должна отображаться диаграмма ганта действий в рамках этой трассировки. При щелчке по каждому из операций отображаются дополнительные сведения о действии.
В распределенной системе необходимо отправлять трассировки из всех процессов в одну установку Jaeger, чтобы она могли сопоставить транзакции в системе.
Вы можете сделать приложение более интересным, сделав его http-вызовами к себе.
Добавление фабрики
HttpClient
в приложениеbuilder.Services.AddHttpClient();
Добавление новой конечной точки для выполнения вложенных вызовов приветствия
app.MapGet("/NestedGreeting", SendNestedGreeting);
Реализуйте конечную точку, чтобы выполнять HTTP-вызовы, которые также можно отслеживать. В этом случае он возвращается к себе в искусственном цикле (действительно применимо только к демонстрационных сценариям).
async Task SendNestedGreeting(int nestlevel, ILogger<Program> logger, HttpContext context, IHttpClientFactory clientFactory) { // Create a new Activity scoped to the method using var activity = greeterActivitySource.StartActivity("GreeterActivity"); if (nestlevel <= 5) { // Log a message logger.LogInformation("Sending greeting, level {nestlevel}", nestlevel); // Increment the custom counter countGreetings.Add(1); // Add a tag to the Activity activity?.SetTag("nest-level", nestlevel); await context.Response.WriteAsync($"Nested Greeting, level: {nestlevel}\r\n"); if (nestlevel > 0) { var request = context.Request; var url = new Uri($"{request.Scheme}://{request.Host}{request.Path}?nestlevel={nestlevel - 1}"); // Makes an http call passing the activity information as http headers var nestedResult = await clientFactory.CreateClient().GetStringAsync(url); await context.Response.WriteAsync(nestedResult); } } else { // Log a message logger.LogError("Greeting nest level {nestlevel} too high", nestlevel); await context.Response.WriteAsync("Nest level too high, max is 5"); } }
Это приводит к более интересному графу с фигурой пирамиды для запросов, так как каждый уровень ожидает ответа от предыдущего вызова.