Metric#

Added in version 1.12.0.

El módulo ngx_stream_metric_module permite crear métricas calculadas arbitrarias en tiempo real para tráfico stream (TCP y UDP). Estos valores de métrica se almacenan en memoria compartida y se muestran en tiempo real dentro de la rama de la API /status/stream/metric_zones/. Se admiten varios tipos de agregación de datos (contadores, histogramas, medias móviles, etc.) con agrupación por claves arbitrarias.

Para conocer el esquema de la respuesta, consulte la referencia de la API de la zona de métricas stream.

Ejemplo de configuración#

Contando conexiones por dirección de cliente:

stream {
    metric_zone connections:1m count;

    server {
        listen 12345;

        metric connections $remote_addr on=connect;
    }
}

http {
    server {
        listen 80;

        location /status/ {
            allow 127.0.0.1;
            deny all;
            api /status/;
        }
    }
}

Si se establece una conexión al puerto 12345:

$ nc 127.0.0.1 12345 </dev/null

La métrica connections se actualiza en tiempo real:

{
    "stream": {
       "metric_zones": {
           "connections": {
               "discarded": 0,
               "metrics": {
                   "127.0.0.1": 1
               }
           }
       }
    }
}

Directivas#

metric#

Sintaxis

metric name key=value [on=connect| preread| end];

Predeterminado

Contexto

stream, server

Calcula el valor de la métrica para la zona de memoria compartida name especificada.

Parámetros:

  • key — una cadena arbitraria (a menudo una variable) usada para agrupar valores.

    La longitud máxima es de 255 bytes; las claves más largas se truncan. En la salida de la API, Angie añade ... a toda clave de 255 bytes, incluida aquella cuya longitud original era exactamente de 255 bytes. Una clave puede contener a su vez caracteres = — solo el texto posterior al último = se trata como el valor, de modo que todo lo anterior (incluido cualquier = incrustado) pasa a formar parte de la clave;

  • value — un número (puede ser una variable) procesado por el modo

    seleccionado. Si se omite, el valor predeterminado es 0. Si el parámetro no puede convertirse en un número, el valor predeterminado es 1;

  • on — un parámetro opcional que especifica en qué punto del procesamiento de la sesión se calcula la métrica:

    • Si on=connect, el cálculo se realiza durante la fase Pre-access, antes de leer cualquier dato del cliente;

    • Si on=preread, el cálculo se realiza una vez por sesión, cuando se envían al cliente los primeros datos procedentes del lado upstream — recibidos del servidor al que se hizo proxy con proxy_pass, o generados por return —; en ese punto, módulos como proxy_protocol, ssl_preread y mqtt_preread ya han procesado los datos iniciales del cliente;

    • Si on=end (predeterminado), el cálculo se realiza durante la fase Log, cuando finaliza la sesión.

Ejemplo de uso:

metric sessions $remote_addr=$session_time on=end;

Nota

Se ignoran las métricas con una clave vacía o con un par key=value no válido. Un value omitido se trata como 0:

metric foo $bar;  # Equivalent to $bar=0

Esto resulta útil, por ejemplo, para el modo count, que ignora los valores numéricos y simplemente reacciona al hecho de que una métrica se actualizó.

Nota

Recuerde que las variables se evalúan en distintas fases. Por ejemplo, es imposible usar $upstream_bytes_sent (bytes enviados al upstream) con on=connect (antes de que la conexión se haya proxificado a algún destino).

metric_complex_zone#

Sintaxis

metric_complex_zone name:size [expire=on| off] [discard_key=name] { ... }

Predeterminado

Contexto

stream

Define una métrica compleja — un conjunto de métricas con modos independientes. Cada línea en el cuerpo del bloque define un nombre de submétrica, un modo y parámetros de modo opcionales. El tamaño de la zona debe ser de al menos ocho páginas de memoria del sistema.

Ejemplo de uso:

metric_complex_zone sessions:1m expire=on discard_key="old" {
    # submetric name   mode          parameters
    min_time           min;
    avg_time           average exp   factor=60;
    max_time           max;
    total              count;
}

En el árbol de la API, una plantilla de este tipo de métrica compleja se ve de la siguiente manera:

{
    "discarded": 3,
    "metrics": {
        "key1": {
            "min_time": 20,
            "avg_time": 50,
            "max_time": 80,
            "total": 2
        },
        "old": {
             "min_time": 3,
             "avg_time": 40,
             "max_time": 152,
             "total": 80
        }
    }
}

metric_zone#

Sintaxis

metric_zone name:size [expire=on| off] [discard_key=name] mode [parameters];

Predeterminado

Contexto

stream

Crea una zona de memoria compartida del size especificado con el name dado para almacenar métricas. El nombre de la zona sirve como nodo en la rama /status/stream/metric_zones/. El tamaño de la zona debe ser de al menos ocho páginas de memoria del sistema.

Parámetros:

  • expire=<on|off> — comportamiento cuando la zona está llena:

    • Si es on, las métricas más antiguas (según el momento de la

      última actualización) se descartan para liberar memoria para las nuevas;

    • Si es off (predeterminado) — las métricas entrantes nuevas se

      descartan, conservando las entradas existentes.

  • discard_key=<name> — define una métrica con la clave name

    donde se acumulan los valores de las métricas descartadas. De forma predeterminada, no se crea dicha métrica. La clave reservada no puede actualizarse manualmente.

  • mode — algoritmo de procesamiento de datos (véase la sección

    Modos de funcionamiento);

  • parameters — ajustes adicionales para el modo seleccionado

    (por ejemplo, factor para average exp).

Ejemplo de uso:

metric_zone session_time:1m max;

En el árbol de la API, la plantilla de la zona de memoria compartida se ve de la siguiente manera:

{
    "discarded": 0,
    "metrics": {
        "key1": 123,
        "key2": 10.5
    }
}

Nota

En una zona de 1 MB, con un tamaño de clave de 39 bytes y un único modo de métrica, pueden almacenarse aproximadamente 8000 entradas de clave únicas.

Modos de funcionamiento#

Lista de los modos de funcionamiento de métricas disponibles:

  • count — contador;

  • gauge — medidor (incremento/decremento);

  • last — el último valor recibido;

  • min — valor mínimo;

  • max — valor máximo;

  • average exp — media móvil exponencial (EMA) (parámetro factor);

  • average mean — media sobre una ventana (parámetros window y count);

  • histogram — distribución en "buckets" (una lista de valores umbral).

Los ejemplos siguientes usan el endpoint /status/ del ejemplo de configuración. Cada resultado está disponible bajo /status/stream/metric_zones/<zone>/metrics/.

count#

Cada actualización de la métrica incrementa el contador en 1, independientemente del valor proporcionado.

Valor predeterminado — 0.

Ejemplos:

metric_zone count:1m count;

# As part of a complex metric:
#
# metric_complex_zone count:1m {
#     some_metric_name  count;
# }

server {
    listen 12345;

    metric count KEY;
}

Actualizando la métrica:

$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": 4
}

gauge#

El medidor incrementa o disminuye su valor según el signo del número proporcionado. Un valor positivo incrementa el contador, mientras que un valor negativo lo disminuye. Un valor de 0 no modifica el contador.

Valor predeterminado — 0.

Ejemplos:

metric_zone gauge:1m gauge;

# As part of a complex metric:
#
# metric_complex_zone gauge:1m {
#     some_metric_name  gauge;
# }

server {
    listen 12351;
    metric gauge KEY;
}

server {
    listen 12352;
    metric gauge KEY=5;
}

server {
    listen 12353;
    metric gauge KEY=-5;
}

server {
    listen 12354;
    metric gauge KEY=8;
}

Actualizando la métrica:

$ nc 127.0.0.1 12351 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": 0
}

Actualizaciones adicionales:

$ nc 127.0.0.1 12352 </dev/null
$ nc 127.0.0.1 12353 </dev/null
$ nc 127.0.0.1 12354 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": 8
}

last#

Almacena el último valor recibido sin ningún tipo de agregación. Si se omite value, se usa 0.

Ejemplos:

metric_zone last:1m last;

# As part of a complex metric:
#
# metric_complex_zone last:1m {
#     some_metric_name  last;
# }

server {
    listen 12361;
    metric last KEY;
}

server {
    listen 12362;
    metric last KEY=8000;
}

server {
    listen 12363;
    metric last KEY=37;
}

server {
    listen 12364;
    metric last KEY=-3.5;
}

Actualizando la métrica:

$ nc 127.0.0.1 12361 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": 0
}

Actualizaciones adicionales:

$ nc 127.0.0.1 12362 </dev/null
$ nc 127.0.0.1 12363 </dev/null
$ nc 127.0.0.1 12364 </dev/null

Valor esperado de la métrica en la API:

{
   "KEY": -3.5
}

min#

Guarda el mínimo de dos valores — el valor actualmente almacenado y el nuevo.

Ejemplos:

metric_zone min:1m min;

# As part of a complex metric:
#
# metric_complex_zone min:1m {
#     some_metric_name  min;
# }

server {
    listen 12371;
    metric min KEY=42.999;
}

server {
    listen 12372;
    metric min KEY=-512;
}

server {
    listen 12373;
    metric min KEY=1;
}

Actualizando la métrica:

$ nc 127.0.0.1 12371 </dev/null
$ nc 127.0.0.1 12372 </dev/null
$ nc 127.0.0.1 12373 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": -512
}

max#

Guarda el máximo de dos valores — el valor actualmente almacenado y el nuevo.

Ejemplos:

metric_zone max:1m max;

# As part of a complex metric:
#
# metric_complex_zone max:1m {
#     some_metric_name  max;
# }

server {
    listen 12381;
    metric max KEY=42.999;
}

server {
    listen 12382;
    metric max KEY=-512;
}

server {
    listen 12383;
    metric max KEY=1;
}

Actualizando la métrica:

$ nc 127.0.0.1 12381 </dev/null
$ nc 127.0.0.1 12382 </dev/null
$ nc 127.0.0.1 12383 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": 42.999
}

average exp#

Calcula el valor medio usando el algoritmo de suavizado exponencial.

Acepta un parámetro opcional factor=<number> — un coeficiente que determina cuánto influye el nuevo valor en la media. Se permiten valores enteros de 0 a 99. El valor predeterminado es 90.

Cuanto mayor sea el coeficiente, más peso tendrán los nuevos valores. Si especifica 90, el resultado será 90% del nuevo valor y 10% de la media anterior.

Ejemplos:

metric_zone avg_exp:1m average exp factor=60;

# As part of a complex metric:
#
# metric_complex_zone avg_exp:1m {
#     some_metric_name  average exp  factor=60;
# }

server {
    listen 12391;
    metric avg_exp KEY=100;
}

server {
    listen 12392;
    metric avg_exp KEY=200;
}

server {
    listen 12393;
    metric avg_exp KEY=0;
}

server {
    listen 12394;
    metric avg_exp KEY=8;
}

server {
    listen 12395;
    metric avg_exp KEY=30;
}

Actualizando la métrica:

$ nc 127.0.0.1 12391 </dev/null
$ nc 127.0.0.1 12392 </dev/null
$ nc 127.0.0.1 12393 </dev/null
$ nc 127.0.0.1 12394 </dev/null
$ nc 127.0.0.1 12395 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": 30.16
}

average mean#

Calcula la media aritmética. Acepta los parámetros opcionales window=<off|time> y count=<number>, que definen, respectivamente, el intervalo de tiempo y el tamaño de la muestra para el promediado. Valores predeterminados: window=off (se usa toda la muestra) y count=10.

Nota

Por ejemplo, window=5s solo tendrá en cuenta eventos de los últimos 5 segundos. El parámetro window no puede ser 0. El parámetro count=number controla el tamaño de la muestra (valores en caché) para un cálculo de la media más suave.

Ejemplos:

metric_zone avg_mean:1m average mean window=5s count=8;

# As part of a complex metric:
#
# metric_complex_zone avg_mean:1m {
#     some_metric_name  average mean  window=5s count=8;
# }

server {
    listen 12401;
    metric avg_mean KEY=0.1;
}

server {
    listen 12402;
    metric avg_mean KEY=0.4;
}

server {
    listen 12403;
    metric avg_mean KEY=10;
}

server {
    listen 12404;
    metric avg_mean KEY=1;
}

Actualizando la métrica:

$ nc 127.0.0.1 12401 </dev/null
$ nc 127.0.0.1 12401 </dev/null
$ nc 127.0.0.1 12402 </dev/null
$ nc 127.0.0.1 12403 </dev/null
$ nc 127.0.0.1 12404 </dev/null
$ nc 127.0.0.1 12404 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": 2.1
}

Si espera 5 segundos desde la última actualización, el valor esperado será:

{
    "KEY": 0
}

histogram#

Crea un conjunto de "buckets", incrementando el contador correspondiente si el nuevo valor no supera el umbral del bucket. Los parámetros se proporcionan como una lista de umbrales numéricos. Es útil para analizar distribuciones, como la duración de las sesiones.

Los parámetros obligatorios son numbers — los valores umbral de los buckets, normalmente listados en orden ascendente.

Nota

El valor de bucket inf o +Inf puede usarse para capturar todos los valores que superen el bucket más alto especificado.

Ejemplos:

metric_zone hist:1m histogram 0.1 0.2 0.5 1 2 inf;

# As part of a complex metric:
#
# metric_complex_zone hist:1m {
#     some_metric_name  histogram  0.1 0.2 0.5 1 2 inf;
# }

server {
    listen 12411;
    metric hist KEY=0.25;
}

server {
    listen 12412;
    metric hist KEY=2;
}

server {
    listen 12413;
    metric hist KEY=1000;
}

Actualizando la métrica:

$ nc 127.0.0.1 12411 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": {
        "0.1": 0,
        "0.2": 0,
        "0.5": 1,
        "1": 1,
        "2": 1,
        "inf": 1
    }
}

Actualizaciones adicionales:

$ nc 127.0.0.1 12412 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": {
        "0.1": 0,
        "0.2": 0,
        "0.5": 1,
        "1": 1,
        "2": 2,
        "inf": 2
    }
}

Actualización adicional:

$ nc 127.0.0.1 12413 </dev/null

Valor esperado de la métrica en la API:

{
    "KEY": {
       "0.1": 0,
       "0.2": 0,
       "0.5": 1,
       "1": 1,
       "2": 2,
       "inf": 3
    }
}

Variables integradas#

Se crean variables para cada métrica:

  • $metric_<name>

  • $metric_<name>_key

  • $metric_<name>_value

Para las métricas complejas, se añade una variable adicional:

  • $metric_<name>_value_<metric>

$metric_<name>#

De forma similar a la directiva metric, el establecedor de la variable $metric_<name> puede usarse para actualizar una métrica. El cálculo se produce cuando se asigna la variable — por ejemplo, mediante la directiva set, que se evalúa durante la fase Pre-access (la misma etapa que on=connect), o de forma programática desde el módulo njs, por ejemplo en un controlador js_access.

El establecedor acepta una clave con un sufijo =value opcional. El último = separa la clave del valor; sin el sufijo, el valor es 0. La clave y el valor pueden contener texto, variables, o combinaciones de ambos. Estas son las mismas reglas de análisis que para metric.

Ejemplo de uso:

stream {
    metric_zone hits:1m count;

    # At this point, the $metric_hits variable is added

    server {
        listen 12345;

        metric hits client=1 on=connect;
    }

    server {
        listen 12346;

        set $metric_hits client=1;

        return "$metric_hits\n";
    }
}

Tras tres conexiones al puerto 12345 y una conexión al puerto 12346:

$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12346 </dev/null
client=4

Nota

Al leer $metric_<name> se obtiene la clave junto con el valor calculado actual de la métrica, en forma key=value — no la cadena literal que se asignó. En el ejemplo anterior, la conexión asigna el valor literal client=1, pero como el modo count ignora el valor asignado y simplemente incrementa en cada actualización, leer $metric_hits posteriormente devuelve client=4 — el nuevo total del contador, que ya incluye las tres actualizaciones anteriores desde el puerto 12345.

Además, el valor almacenado en la variable $metric_<name>_key cambia a la clave especificada.

$metric_<name>_key and $metric_<name>_value#

Las variables $metric_<name>_key y $metric_<name>_value definen la clave y el valor respectivamente. La actualización de la métrica se produce cuando se asigna $metric_<name>_value, siempre que la clave en $metric_<name>_key ya se haya definido.

Nota

Para las métricas complejas, los valores de las submétricas en la variable $metric_<name>_value se unen usando un separador ", ".

Ejemplo de uso:

stream {
    metric_zone level:1m gauge;

    # The variables $metric_level, $metric_level_key, and $metric_level_value are added here.

    metric_complex_zone stats:1m {
        count count;
        min   min;
        avg   average exp;
    }

    # $metric_stats, $metric_stats_key, and $metric_stats_value are added here.

    server {
        listen 12345;

        metric level sensor=10 on=connect;
    }

    server {
        listen 12346;

        set $metric_level_key   "sensor";
        set $metric_level_value 5;

        # Or: set $metric_level sensor=5;

        return "Updated with '$metric_level'\nValue='$metric_level_value'\n";
    }

    server {
        listen 12347;

        set $metric_stats_key   bar;
        set $metric_stats_value 9;

        # Or: set $metric_stats bar=9;

        return "Updated with '$metric_stats'\nValues='$metric_stats_value'\n";
    }
}

Con esta configuración, una conexión al puerto 12345 (que fija el valor inicial del medidor en 10) seguida de una conexión al puerto 12346 produce:

$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12346 </dev/null
Updated with 'sensor=15'
Value='15'

Una conexión al puerto 12347 produce:

$ nc 127.0.0.1 12347 </dev/null
Updated with 'bar=1, 9, 9'
Values='1, 9, 9'

Nota

Si se asigna una cadena vacía a $metric_<name>_value, el valor se reconoce como 0. Si la cadena consta de caracteres que no pueden convertirse en un número, se reconoce como 1.

El cálculo se produce solo después de que se hayan asignado tanto $metric_<name>_key como $metric_<name>_value.

En este caso, el valor almacenado en $metric_<name> pasa a ser igual al nuevo par calculado key=value, no a los valores literales recién asignados.

El valor en $metric_<name>_key representa la última clave especificada mediante variables.

El valor en $metric_<name>_value representa el último valor calculado para la clave establecida en $metric_<name>_key.

$metric_<name>_value_<metric>#

Para las métricas complejas, el valor de una submétrica específica puede obtenerse mediante la variable $metric_<name>_value_<metric>, donde <metric> es el nombre de la submétrica.

Ejemplo de uso:

stream {
    metric_complex_zone foo:1m {
        count count;
        min   min;
        avg   average exp;
    }

    # Adds $metric_foo, $metric_foo_key, $metric_foo_value,
    # and $metric_foo_value_count, $metric_foo_value_min, $metric_foo_value_avg.

    server {
        listen 12345;

        set $metric_foo_key   bar;
        set $metric_foo_value 9;

        # Or: set $metric_foo bar=9;

        return "Updated with '$metric_foo'\nValues='$metric_foo_value'\nCount='$metric_foo_value_count'\n";
    }
}

Con esta configuración, una conexión al puerto 12345 produce:

$ nc 127.0.0.1 12345 </dev/null
Updated with 'bar=1, 9, 9'
Values='1, 9, 9'
Count='1'

Ejemplos adicionales#

Supervisión de conexiones por protocolo#

metric_zone protocols:1m count;

server {
    listen 12345;
    listen 12346 udp;

    metric protocols $protocol;
}

Respuesta:

{
    "TCP": 340,
    "UDP": 58
}

Distribución del tiempo de sesión de upstream#

metric_zone upstream_time:10m expire=on histogram
    0.05 0.1 0.3 0.5 1 2 5 10 inf;

server {
    listen 12345;

    proxy_pass backend;
    metric upstream_time $upstream_addr=$upstream_session_time on=end;
}

Respuesta:

{
    "discarded": 0,
    "metrics": {
        "backend1:8080": {
            "0.05": 12,
            "0.1": 28,
            "0.3": 56,
            "0.5": 78,
            "1": 92,
            "2": 97,
            "5": 99,
            "10": 100,
            "inf": 100
        }
    }
}

Conexiones activas#

stream {
    metric_zone active_connections:2m gauge;

    server {
        listen 12345;

        metric active_connections service_a=1 on=connect;
        metric active_connections service_a=-1 on=end;
    }

    server {
        listen 12346;

        metric active_connections service_b=1 on=connect;
        metric active_connections service_b=-1 on=end;
    }
}

http {
    server {
        listen 8080;

        location /connections/ {
            allow 127.0.0.1;
            deny all;
            api /status/stream/metric_zones/active_connections/metrics/;
        }
    }
}

Respuesta:

{
    "service_a": 42,
    "service_b": 17
}

Soporte de Prometheus#

Angie incluye un módulo integrado para mostrar métricas en formato de Prometheus, que admite métricas personalizadas, incluidas las recopiladas en el lado stream. Dado que la exposición de Prometheus es una funcionalidad exclusiva de HTTP, las directivas prometheus_template y prometheus se configuran en un bloque http, haciendo referencia a la zona de métricas stream a través del mismo árbol unificado de la API /status/.

Como ejemplo de integración, considere la siguiente configuración:

stream {
    # Creating the "upload" metric
    metric_complex_zone upload:1m discard_key="other" {
        stats    histogram 64 256 1024 4096 16384 +Inf;
        sum      gauge;
        count    count;
        avg_size average exp;
    }

    upstream backend {
        server 127.0.0.1:15001;
    }

    server {
        listen 12345;

        # Updating the metric at session end with the total
        # number of bytes received from the client
        proxy_pass backend;
        metric upload angie=$bytes_received on=end;
    }
}

http {
    # Describing the Prometheus template for the "upload" metric
    prometheus_template upload_metric {
        'stats{le="$1"}' $p8s_value
                         path=~^/stream/metric_zones/upload/metrics/angie/stats/(.+)$
                         type=histogram;

        'stats_sum'      $p8s_value
                         path=/stream/metric_zones/upload/metrics/angie/sum;
        'stats_count'    $p8s_value
                         path=/stream/metric_zones/upload/metrics/angie/count;

        'avg_size'       $p8s_value
                         path=/stream/metric_zones/upload/metrics/angie/avg_size;
    }

    server {
        listen 80;

        # Target for metric scraping
        location /prometheus/upload_metric/ {
            prometheus upload_metric;
        }
    }
}

Tras cinco conexiones al puerto 12345, enviando payloads de 16384, 64448, 64, 1028 y 1028 bytes:

$ head -c 16384 /dev/urandom | nc 127.0.0.1 12345
$ head -c 64448 /dev/urandom | nc 127.0.0.1 12345
$ head -c 64 /dev/urandom | nc 127.0.0.1 12345
$ head -c 1028 /dev/urandom | nc 127.0.0.1 12345
$ head -c 1028 /dev/urandom | nc 127.0.0.1 12345

Los valores de la métrica serán:

{
    "stats": {
        "64": 1,
        "256": 1,
        "1024": 1,
        "4096": 3,
        "16384": 4,
        "+Inf": 5
    },

    "sum": 82952,
    "count": 5,
    "avg_size": 1077.9376
}

En formato de Prometheus, la métrica está disponible en /prometheus/upload_metric/:

# Angie Prometheus template "upload_metric"
# TYPE stats histogram
stats{le="64"} 1
stats{le="256"} 1
stats{le="1024"} 1
stats{le="4096"} 3
stats{le="16384"} 4
stats{le="+Inf"} 5
stats_sum 82952
stats_count 5
avg_size 1077.9376