Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Este artículo se aplica a: ✔️ .NET 8 y versiones posteriores ✔️ .NET Framework 4.6.1 y versiones posteriores
El código instrumentado puede registrar medidas numéricas, pero las medidas normalmente deben agregarse, transmitirse y almacenarse para crear métricas útiles para la supervisión. El proceso de agregación, transmisión y almacenamiento de datos se denomina recopilación. En este tutorial se muestran varios ejemplos de recopilación de métricas:
- Rellenar métricas en Grafana con OpenTelemetry y Prometheus.
- Visualización de métricas en tiempo real con
dotnet-counters - Creación de una herramienta de recopilación personalizada mediante la API de .NET MeterListener subyacente.
Para más información sobre la instrumentación y las opciones de métricas personalizadas, consulte Comparación de las API de métricas.
Prerrequisitos
- SDK de .NET 8 o posterior.
Crear una aplicación de ejemplo
Para poder recopilar métricas, se deben generar mediciones. En este tutorial se crea una aplicación que tiene instrumentación básica de métricas. El entorno de ejecución de .NET también tiene varias métricas integradas. Para más información sobre cómo crear nuevas métricas mediante la System.Diagnostics.Metrics.Meter API, consulte el tutorial de instrumentación.
dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource
Reemplace el contenido de Program.cs por el código siguiente:
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));
}
}
}
El código anterior simula la venta de sombreros a intervalos aleatorios y en momentos aleatorios.
Visualización de métricas con dotnet-counters
dotnet-counters es una herramienta de línea de comandos que permite ver métricas en tiempo real para aplicaciones de .NET Core a petición. No requiere configuración, lo que resulta útil para investigaciones ad hoc y para comprobar que la instrumentación de métricas funciona. Funciona tanto con API basadas en System.Diagnostics.Metrics como con EventCounters.
Si la herramienta dotnet-counters no está instalada, ejecute el siguiente comando:
dotnet tool update -g dotnet-counters
Si la aplicación ejecuta una versión de .NET anterior a .NET 9, la interfaz de usuario de salida de dotnet-counters tendrá un aspecto ligeramente diferente al siguiente; consulte dotnet-counters para obtener más información.
Mientras se ejecuta la aplicación de ejemplo, inicie dotnet-counters. El comando siguiente muestra un ejemplo de cómo dotnet-counters supervisa todas las métricas del medidor de HatCo.HatStore. El nombre del medidor distingue mayúsculas de minúsculas. Nuestra aplicación de ejemplo era metric-instr.exe, sustituya esto por el nombre de su aplicación de ejemplo.
dotnet-counters monitor -n metric-instr HatCo.HatStore
Se muestra una salida similar a la siguiente:
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.HatStore]
hats-sold (Count / 1 sec) 4
dotnet-counters se puede ejecutar con un conjunto diferente de métricas para ver parte de la instrumentación integrada del entorno de ejecución de .NET:
dotnet-counters monitor -n metric-instr
Se muestra una salida similar a la siguiente:
System.Runtime
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[System.Runtime]
dotnet.assembly.count ({assembly}) 11
dotnet.gc.collections ({collection})
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
dotnet.gc.heap.total_allocated (By) 1,376,024
dotnet.gc.last_collection.heap.fragmentation.size (By)
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.heap.size (By)
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.memory.committed_size (By) 0
dotnet.gc.pause.time (s) 0
dotnet.jit.compilation.time (s) 0.253
dotnet.jit.compiled_il.size (By) 79,536
dotnet.jit.compiled_methods ({method}) 743
dotnet.monitor.lock_contentions ({contention}) 0
dotnet.process.cpu.count ({cpu}) 22
dotnet.process.cpu.time (s)
cpu.mode
--------
system 0.125
user 46.453
dotnet.process.memory.working_set (By) 34,447,360
dotnet.thread_pool.queue.length ({work_item}) 0
dotnet.thread_pool.thread.count ({thread}) 0
dotnet.thread_pool.work_item.count ({work_item}) 0
dotnet.timer.count ({timer}) 0
Para obtener más información, consulte dotnet-counters. Para más información sobre las métricas en .NET, consulte Métricas integradas.
Ver métricas en Grafana con OpenTelemetry y Prometheus
Información general
- Es un proyecto de código abierto independiente del proveedor respaldado por la Cloud Native Computing Foundation.
- Estandariza la generación y recopilación de telemetría para software nativo en la nube.
- Funciona con .NET mediante las API de métricas de .NET.
- Está respaldado por Azure Monitor y por muchos proveedores de APM.
En este tutorial se muestra una de las integraciones disponibles para las métricas de OpenTelemetry mediante los proyectos de OSS Prometheus y Grafana . Flujo de datos de métricas:
Las API de métricas de .NET registran medidas de la aplicación de ejemplo.
La biblioteca OpenTelemetry que se ejecuta en la aplicación agrega las medidas.
La biblioteca exportadora de Prometheus hace que los datos agregados estén disponibles a través de un punto de conexión de métricas HTTP. "Exporter" es lo que OpenTelemetry llama a las bibliotecas que transmiten telemetría a back-end específicos del proveedor.
Un servidor de Prometheus:
- Sondea el punto de conexión de métricas.
- Lee los datos
- Almacena los datos en una base de datos para la persistencia a largo plazo. Prometheus hace referencia a la lectura y el almacenamiento de datos como extracción de un punto de conexión.
- Se puede ejecutar en otra máquina
El servidor de Grafana:
- Consulta los datos almacenados en Prometheus y los muestra en un panel de supervisión basado en web.
- Se puede ejecutar en otra máquina.
Configure la aplicación de ejemplo para usar el exportador de Prometheus de OpenTelemetry
Agregue una referencia al exportador de OpenTelemetry Prometheus a la aplicación de ejemplo:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Nota:
En este tutorial se usa una compilación preliminar de la compatibilidad con Prometheus de OpenTelemetry disponible en el momento de redactar este texto.
Actualizar Program.cs con la configuración de 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.Port = 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));
}
}
}
En el código anterior:
-
AddMeter("HatCo.HatStore")configura OpenTelemetry para transmitir todas las métricas recopiladas por el medidor definido en la aplicación. -
AddPrometheusHttpListenerconfigura OpenTelemetry para:- Exponga el punto de conexión de métricas de Prometheus en el puerto
9184 - Usar HttpListener.
- Exponga el punto de conexión de métricas de Prometheus en el puerto
Consulte la documentación de OpenTelemetry para obtener más información sobre las opciones de configuración de OpenTelemetry. En la documentación de OpenTelemetry se muestran las opciones de hospedaje de las aplicaciones de ASP.NET.
Ejecute la aplicación y déjela en ejecución para que se puedan recopilar medidas:
dotnet run
Configuración de Prometheus
Siga los primeros pasos de Prometheus para configurar un servidor de Prometheus y confirmar que funciona.
Modifique el archivo de configuración prometheus.yml para que Prometheus extraiga el punto de conexión de métricas que se expone en la aplicación de ejemplo. Agregue el texto resaltado siguiente en la scrape_configs sección:
# 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
Vuelva a cargar la configuración o reinicie el servidor prometheus.
Confirme que OpenTelemetryTest se encuentre en el estado UP en la página Estado>Objetivos del portal web de Prometheus.
En la página Gráfico del portal web de Prometheus, escriba
hatsen el cuadro de texto de expresión y seleccionehats_sold_Hats
En la pestaña gráfico, Prometheus muestra el valor creciente del contador "hats-sold" que emite la aplicación de ejemplo.
En la imagen anterior, el tiempo del gráfico se establece en 5m, que es de 5 minutos.
Si el servidor de Prometheus no ha estado recopilando datos de la aplicación de ejemplo durante mucho tiempo, es posible que tenga que esperar a que se acumulen los datos.
Mostrar métricas en un panel de Grafana
Siga las instrucciones estándar para instalar Grafana y conectarla a un origen de datos de Prometheus.
Cree un panel de Grafana haciendo clic en el + icono de la barra de herramientas izquierda del portal web de Grafana y, a continuación, seleccione Panel. En el editor del panel que aparece, escriba Hats Sold/Sec en el cuadro de entrada Título y rate(hats_sold[5m]) en el campo expresión PromQL:
Haga clic en Aplicar para guardar y ver el nuevo panel.
]
Cree una herramienta de recopilación personalizada mediante la API de .NET MeterListener
La API de .NET MeterListener permite crear lógica personalizada en proceso para observar las medidas que registra System.Diagnostics.Metrics.Meter. Para obtener instrucciones sobre cómo crear lógica personalizada compatible con la instrumentación de EventCounters anterior, consulte EventCounters.
Modifique el 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}");
}
}
En la salida siguiente se muestra la salida de la aplicación con callback personalizada en cada medición:
> 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
...
Explicación del código de ejemplo
Los fragmentos de código de esta sección proceden del ejemplo anterior.
En el código resaltado siguiente, se crea una instancia de MeterListener para recibir medidas. La palabra clave using hace que se llame a Dispose cuando meterListener sale del ámbito.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
El siguiente código resaltado configura de qué instrumentos recibe medidas el listener. InstrumentPublished es un delegado que se invoca cuando se crea un nuevo instrumento dentro de la aplicación.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
El delegado puede examinar el instrumento para decidir si quiere suscribirse. Por ejemplo, el delegado puede comprobar el nombre, el Meter o cualquier otra propiedad pública. EnableMeasurementEvents habilita la recepción de medidas del instrumento especificado. Código que obtiene una referencia a un instrumento mediante otro enfoque:
- Normalmente no se realiza.
- Puede invocar
EnableMeasurementEvents()en cualquier momento con la referencia.
El delegado que se invoca cuando se reciben medidas de un instrumento se configura mediante una llamada a 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}");
}
El parámetro genérico controla qué tipo de datos de medida recibe la devolución de llamada. Por ejemplo, una Counter<int> genera int medidas, Counter<double> genera double medidas. Los instrumentos se pueden crear con los tipos byte, short, int, long, float, double y decimal. Se recomienda registrar un callback para cada tipo de datos, a menos que tenga conocimientos específicos del escenario de que no se necesitan todos los tipos de datos. Realizar llamadas repetidas a SetMeasurementEventCallback con distintos argumentos genéricos puede parecer un poco inusual. La API está diseñada de esta manera para permitir que MeterListener reciba medidas con una sobrecarga de rendimiento baja, normalmente de unos pocos nanosegundos.
Cuando se llama a MeterListener.EnableMeasurementEvents, se puede proporcionar un objeto state como uno de los parámetros. El state objeto es arbitrario. Si proporciona un objeto de estado en esa llamada, se almacena con ese instrumento y se le devolverá como parámetro state en la devolución de llamada. Esto está pensado tanto por comodidad como para optimizar el rendimiento. A menudo, los clientes de escucha necesitan:
- Crear un objeto para cada instrumento que almacena medidas en la memoria.
- Tener código para realizar cálculos en esas medidas.
Como alternativa, cree un Dictionary que asocie el instrumento con el objeto de almacenamiento y consúltelo en cada medición. Usar un Dictionary es mucho más lento que acceder a él desde state.
meterListener.Start();
El código anterior inicia MeterListener, que habilita las devoluciones de llamada. El InstrumentPublished delegate se invoca para cada Instrument preexistente en el proceso. Los objetos Instrument recién creados también hacen que se invoque InstrumentPublished.
using MeterListener meterListener = new MeterListener();
Cuando la aplicación haya terminado la escucha, al eliminar el cliente de escucha, se detendrá el flujo de devoluciones de llamada y se liberarán las referencias internas al objeto del cliente de escucha. La palabra clave using usada al declarar meterListener hace que se llame a Dispose cuando la variable sale del ámbito. Tenga en cuenta que Dispose solo promete que no iniciará nuevos callbacks. Dado que las devoluciones de llamada se producen en subprocesos diferentes, puede haber devoluciones de llamada en curso después de que la llamada a Dispose devuelva el control.
Para garantizar que una determinada región de código en el callback no se está ejecutando actualmente y no se ejecutará en el futuro, se debe agregar la sincronización de hilos.
Dispose no incluye la sincronización de forma predeterminada porque:
- La sincronización agrega sobrecarga de rendimiento en cada callback de medición.
-
MeterListenerestá diseñada como una API muy orientada al rendimiento.
Para los oyentes personalizados que utilizan inyección de dependencias y configuración, consulte Configurar métricas.