Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo aplica-se a: ✔️ .NET 6.0 e posterior ✔️ .NET Framework 4.6.1 e posterior
O código instrumentado pode registrar medições numéricas, mas as medições geralmente precisam ser agregadas, transmitidas e armazenadas para criar métricas úteis para monitoramento. O processo de agregação, transmissão e armazenamento de dados é chamado de coleta. Este tutorial mostra vários exemplos de coleta de métricas:
- Preenchendo métricas no Grafana com OpenTelemetry e Prometheus.
- Visualização de métricas em tempo real com
dotnet-counters
- Criando uma ferramenta de coleta personalizada usando a API .NET MeterListener subjacente.
Para obter mais informações sobre instrumentação e opções de métricas personalizadas, consulte Comparar APIs de métricas.
Pré-requisitos
- SDK do .NET 6.0 ou posterior
Criar um aplicativo de exemplo
Antes que as métricas possam ser coletadas, as medições devem ser produzidas. Este tutorial cria um aplicativo que tem instrumentação métrica básica. O tempo de execução do .NET também tem várias métricas internas. Para obter mais informações sobre como criar novas métricas usando a System.Diagnostics.Metrics.Meter API, consulte o tutorial de instrumentação.
dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource
Substitua o conteúdo do Program.cs
pelo seguinte código:
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hats-sold");
static void Main(string[] args)
{
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
}
O código anterior simula a venda de chapéus em intervalos aleatórios e tempos aleatórios.
Veja métricas com o dotnet-counters
dotnet-counters é uma ferramenta de linha de comando que pode exibir métricas em tempo real para aplicações .NET Core sob demanda. Ele não requer configuração, tornando-o útil para investigações ad hoc ou verificação de que a instrumentação métrica está funcionando. Funciona tanto com APIs baseadas em System.Diagnostics.Metrics quanto com Contadores de Eventos.
Se a ferramenta dotnet-counters não estiver instalada, execute o seguinte comando:
dotnet tool update -g dotnet-counters
Enquanto a aplicação de exemplo estiver em execução, inicie dotnet-counters. O comando a seguir mostra um exemplo de monitorização de dotnet-counters
todas as métricas do HatCo.HatStore
sensor. O nome do medidor é sensível a maiúsculas e minúsculas. Nosso aplicativo de exemplo foi metric-instr.exe, substitua isso pelo nome do seu aplicativo de exemplo.
dotnet-counters monitor -n metric-instr HatCo.HatStore
Saída semelhante à seguinte é exibida:
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.HatStore]
hats-sold (Count / 1 sec) 4
dotnet-counters
pode ser executado com um conjunto diferente de métricas para ver algumas das funcionalidades internas incorporadas do tempo de execução do .NET:
dotnet-counters monitor -n metric-instr
Saída semelhante à seguinte é exibida:
Press p to pause, r to resume, q to quit.
Status: Running
[System.Runtime]
% Time in GC since last GC (%) 0
Allocation Rate (B / 1 sec) 8,168
CPU Usage (%) 0
Exception Count (Count / 1 sec) 0
GC Heap Size (MB) 2
Gen 0 GC Count (Count / 1 sec) 0
Gen 0 Size (B) 2,216,256
Gen 1 GC Count (Count / 1 sec) 0
Gen 1 Size (B) 423,392
Gen 2 GC Count (Count / 1 sec) 0
Gen 2 Size (B) 203,248
LOH Size (B) 933,216
Monitor Lock Contention Count (Count / 1 sec) 0
Number of Active Timers 1
Number of Assemblies Loaded 39
ThreadPool Completed Work Item Count (Count / 1 sec) 0
ThreadPool Queue Length 0
ThreadPool Thread Count 3
Working Set (MB) 30
Para obter mais informações, consulte dotnet-counters. Para saber mais sobre métricas no .NET, consulte Métricas internas.
Veja métricas no Grafana com OpenTelemetry e Prometheus
Visão geral
- É um projeto de código aberto neutro em relação ao fornecedor, suportado pela Cloud Native Computing Foundation.
- Padroniza a geração e coleta de telemetria para software nativo da nuvem.
- Funciona com .NET usando as APIs de métrica .NET.
- É endossado pelo Azure Monitor e muitos fornecedores de APM.
Este tutorial mostra uma das integrações disponíveis para métricas OpenTelemetry usando os projetos OSS Prometheus e Grafana. O fluxo de dados das métricas:
As APIs de métrica .NET registram medições do aplicativo de exemplo.
A biblioteca OpenTelemetry em execução no aplicativo agrega as medidas.
A biblioteca do exportador Prometheus disponibiliza os dados agregados através de um endpoint de métricas HTTP. 'Exportador' é o que a OpenTelemetry chama de bibliotecas que transmitem telemetria para back-ends específicos do fornecedor.
Um servidor Prometheus:
- Sonda o ponto de extremidade das métricas
- Lê os dados
- Armazena os dados em um banco de dados para persistência de longo prazo. Prometheus refere-se a ler e armazenar dados como scraping de um endpoint.
- Pode ser executado em uma máquina diferente
O servidor Grafana:
- Consulta os dados armazenados no Prometheus e os exibe em um painel de monitoramento baseado na Web.
- Pode ser executado em uma máquina diferente.
Configurar o aplicativo de exemplo para usar o exportador Prometheus da OpenTelemetry
Adicione uma referência ao exportador OpenTelemetry Prometheus ao aplicativo de exemplo:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Observação
Este tutorial usa uma compilação de pré-lançamento do suporte Prometheus da OpenTelemetry, disponível no momento da escrita.
Atualize Program.cs
com a configuração OpenTelemetry :
using OpenTelemetry;
using OpenTelemetry.Metrics;
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
name: "hats-sold",
unit: "Hats",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter("HatCo.HatStore")
.AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { "http://localhost:9184/" })
.Build();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0,1000));
}
}
}
No código anterior:
-
AddMeter("HatCo.HatStore")
configura o OpenTelemetry para transmitir todas as métricas coletadas pelo Medidor definido no aplicativo. -
AddPrometheusHttpListener
configura o OpenTelemetry para:- Torne visível o endpoint de métricas do Prometheus na porta
9184
- Utilize o HttpListener.
- Torne visível o endpoint de métricas do Prometheus na porta
Consulte a documentação do OpenTelemetry para obter mais informações sobre as opções de configuração do OpenTelemetry . A documentação do OpenTelemetry mostra opções de hospedagem para aplicativos ASP.NET.
Execute o aplicativo e deixe-o em execução para que as medições possam ser coletadas:
dotnet run
Instalar e configurar o Prometheus
Siga os primeiros passos do Prometheus para configurar um servidor Prometheus e confirme se está a funcionar.
Modifique o arquivo de configuração prometheus.yml para que o Prometheus colete o endpoint de métricas que o aplicativo de exemplo disponibiliza. Adicione o seguinte texto realçado na secção scrape_configs
:
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
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'.
static_configs:
- targets: ["localhost:9090"]
- job_name: 'OpenTelemetryTest'
scrape_interval: 1s # poll very quickly for a more responsive demo
static_configs:
- targets: ['localhost:9184']
Iniciar Prometheus
Recarregue a configuração ou reinicie o servidor Prometheus.
Confirme que OpenTelemetryTest está no estado UP na página Status>Targets do portal web do Prometheus.
Na página Gráfico do portal Web Prometheus, introduza
hats
a caixa de texto da expressão e selecionehats_sold_Hats
No separador gráfico, Prometheus mostra o valor crescente do Contador "chapéus-vendidos" que está a ser emitido pela aplicação de exemplo.
Na imagem anterior, o tempo do gráfico é definido como 5m, ou seja, 5 minutos.
Se o servidor Prometheus não estiver coletando informações do aplicativo de exemplo há muito tempo, talvez seja necessário aguardar que os dados se acumulem.
Mostrar métricas em um painel do Grafana
Siga as instruções padrão para instalar o Grafana e conectá-lo a uma fonte de dados Prometheus.
Crie um painel do Grafana clicando + no ícone na barra de ferramentas à esquerda no portal da Web do Grafana e selecione Painel. No editor de painel exibido, digite Chapéus vendidos/s na caixa de título e rate(hats_sold[5m]) no campo da expressão PromQL.
Clique em Aplicar para salvar e exibir o novo painel.
]
Criar uma ferramenta de coleta personalizada usando a API .NET MeterListener
A API .NET MeterListener permite que você crie uma lógica personalizada no processo para observar as medições que estão sendo registradas pelo System.Diagnostics.Metrics.Meter. Para obter orientação sobre como criar lógica personalizada compatível com a instrumentação EventCounters mais antiga , consulte EventCounters.
Modifique o código de Program.cs
para usar MeterListener:
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
name: "hats-sold",
unit: "Hats",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
// Start the meterListener, enabling InstrumentPublished callbacks.
meterListener.Start();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
static void OnMeasurementRecorded<T>(
Instrument instrument,
T measurement,
ReadOnlySpan<KeyValuePair<string, object?>> tags,
object? state)
{
Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
}
}
A saída a seguir mostra a saída do aplicativo com retorno de chamada personalizado em cada medição:
> dotnet run
Press any key to exit
hats-sold recorded measurement 978
hats-sold recorded measurement 775
hats-sold recorded measurement 666
hats-sold recorded measurement 66
hats-sold recorded measurement 914
hats-sold recorded measurement 912
...
Explicando o código de exemplo
Os trechos de código nesta seção vêm do exemplo anterior.
No código destacado a seguir, uma instância do MeterListener é criada para receber medições. A using
palavra-chave faz com que Dispose
seja chamada quando o meterListener
sai do escopo.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
O código realçado a seguir configura de quais instrumentos o ouvinte recebe medições. InstrumentPublished é um delegado que é invocado quando um novo instrumento é criado dentro do aplicativo.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
O delegado pode examinar o instrumento para decidir se subscreve. Por exemplo, o delegado pode verificar o nome, o medidor ou qualquer outra propriedade pública. EnableMeasurementEvents Permite receber medições do instrumento especificado. Código que obtém uma referência a um instrumento por outra abordagem:
- Não é normalmente feito.
- Pode invocar
EnableMeasurementEvents()
a qualquer momento com a referência.
O delegado que é invocado quando as medições são recebidas de um instrumento é configurado chamando SetMeasurementEventCallback:
meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
// Start the meterListener, enabling InstrumentPublished callbacks.
meterListener.Start();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
static void OnMeasurementRecorded<T>(
Instrument instrument,
T measurement,
ReadOnlySpan<KeyValuePair<string, object?>> tags,
object? state)
{
Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
}
O parâmetro genérico controla qual tipo de dado de medição é recebido pelo retorno de chamada. Por exemplo, a Counter<int>
gera int
medições, Counter<double>
gera double
medições. Os instrumentos podem ser criados com byte
, short
, int
, long
, float
, double
e decimal
tipos. Recomendamos registrar um retorno de chamada para cada tipo de dados, a menos que você tenha conhecimento específico do cenário de que nem todos os tipos de dados são necessários. Fazer chamadas repetidas ao SetMeasurementEventCallback
com diferentes argumentos genéricos pode parecer um pouco incomum. A API foi projetada dessa forma para permitir que um MeterListener
receba medições com sobrecarga de baixo desempenho, normalmente apenas alguns nanossegundos.
Quando MeterListener.EnableMeasurementEvents
é chamado, um state
objeto pode ser fornecido como um dos parâmetros. O state
objeto é arbitrário. Se você fornecer um objeto de estado nessa chamada, ele será armazenado com esse instrumento e retornado a você como o state
parâmetro no retorno de chamada. Isso é pretendido tanto como uma conveniência e como uma otimização de desempenho. Muitas vezes, os ouvintes precisam:
- Crie um objeto para cada instrumento que esteja armazenando medições na memória.
- Tenha código para fazer cálculos a partir dessas medições.
Como alternativa, crie um Dictionary
que mapeie do instrumento para o objeto de armazenamento e consulte-o em cada medição. Usar um Dictionary
é muito mais lento do que acessá-lo a partir de state
.
meterListener.Start();
O código anterior inicia o MeterListener
, o que habilita os callbacks. O InstrumentPublished
delegado é invocado para cada Instrumento pré-existente no processo. Os objetos Instrument recém-criados também desencadeiam a invocação de InstrumentPublished
.
using MeterListener meterListener = new MeterListener();
Quando o aplicativo termina de ouvir, eliminar o ouvinte interrompe o fluxo de retornos de chamada e libera todas as referências internas ao objeto do ouvinte. A using
palavra-chave usada quando se declara meterListener
causa que Dispose
seja chamado quando a variável sai do escopo. Observe que Dispose
está apenas prometendo que não iniciará novos retornos de chamada. Como os callbacks ocorrem em diferentes threads, ainda pode haver callbacks em progresso depois que a chamada para Dispose
terminar.
Para garantir que uma determinada região de código no retorno de chamada não esteja em execução no momento e não seja executada no futuro, a sincronização de threads deve ser adicionada.
Dispose
não inclui sincronização por padrão porque:
- A sincronização adiciona sobrecarga de desempenho em cada callback de medição.
-
MeterListener
é projetado como uma API altamente consciente do desempenho.