Metric#

Adicionado na versão 1.12.0.

O módulo ngx_stream_metric_module permite criar métricas arbitrárias calculadas em tempo real para tráfego stream (TCP e UDP). Esses valores de métricas são armazenados em memória compartilhada e exibidos em tempo real no ramo da API /status/stream/metric_zones/. Vários tipos de agregação de dados são suportados (contadores, histogramas, médias móveis, etc.) com agrupamento por chaves arbitrárias.

Para o esquema da resposta, consulte a referência da API de zona de métricas stream.

Exemplo de Configuração#

Contando conexões por endereço 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/;
        }
    }
}

Se uma conexão for estabelecida na porta 12345:

$ nc 127.0.0.1 12345 </dev/null

A métrica connections é atualizada em tempo real:

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

Diretivas#

metric#

Sintaxe

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

Padrão

Contexto

stream, server

Calcula o valor da métrica para a zona de memória compartilhada especificada name.

Parâmetros:

  • key — uma string arbitrária (frequentemente uma variável) usada para agrupar valores.

    O comprimento máximo é de 255 bytes; chaves mais longas são truncadas. Na saída da API, o Angie anexa ... a toda chave de 255 bytes, incluindo aquela cujo comprimento original era exatamente 255 bytes. Uma chave pode, ela própria, conter caracteres = — apenas o texto posterior ao último = é tratado como o valor, de modo que tudo o que vem antes dele (incluindo qualquer = incorporado) passa a fazer parte da chave;

  • value — um número (pode ser uma variável) processado pelo modo

    selecionado. Se omitido, o padrão é 0. Se o parâmetro não puder ser convertido para um número, o padrão é 1;

  • on — um parâmetro opcional que especifica em qual ponto do processamento da sessão a métrica é calculada:

    • Se on=connect, o cálculo ocorre durante a fase Pre-access, antes que qualquer dado seja lido do cliente;

    • Se on=preread, o cálculo ocorre uma vez por sessão, quando os primeiros dados do lado upstream — recebidos do servidor para o qual foi feito proxy com proxy_pass, ou gerados por return — são enviados ao cliente; nesse ponto, módulos como proxy_protocol, ssl_preread e mqtt_preread já processaram os dados iniciais do cliente;

    • Se on=end (padrão), o cálculo ocorre durante a fase Log, quando a sessão termina.

Exemplo de uso:

metric sessions $remote_addr=$session_time on=end;

Nota

Métricas com uma chave vazia ou com um par key=value inválido são ignoradas. Um value omitido é tratado como 0:

metric foo $bar;  # Equivalente a $bar=0

Isso é útil, por exemplo, para o modo count, que ignora valores numéricos e simplesmente reage ao fato de que uma métrica foi atualizada.

Nota

Lembre-se de que as variáveis são avaliadas em fases diferentes. Por exemplo, é impossível usar $upstream_bytes_sent (bytes enviados ao upstream) com on=connect (antes que a conexão tenha sido encaminhada via proxy para algum destino).

metric_complex_zone#

Sintaxe

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

Padrão

Contexto

stream

Define uma métrica complexa — um conjunto de métricas com modos independentes. Cada linha no corpo do bloco define um nome de submétrica, um modo e parâmetros opcionais do modo. O tamanho da zona deve ser de pelo menos oito páginas de memória do sistema.

Exemplo de uso:

metric_complex_zone sessions:1m expire=on discard_key="old" {
    # nome submétrica   modo          parâmetros
    min_time           min;
    avg_time           average exp   factor=60;
    max_time           max;
    total              count;
}

Na árvore da API, tal modelo de métrica complexa se parece com o seguinte:

{
    "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#

Sintaxe

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

Padrão

Contexto

stream

Cria uma zona de memória compartilhada do size especificado com o name fornecido para armazenar métricas. O nome da zona serve como um nó no ramo /status/stream/metric_zones/. O tamanho da zona deve ser de pelo menos oito páginas de memória do sistema.

Parâmetros:

  • expire=<on|off> — comportamento quando a zona está cheia:

    • Se on, as métricas mais antigas (por tempo de atualização)

      são descartadas para liberar memória para as novas;

    • Se off (padrão) — as novas métricas recebidas são

      descartadas, preservando as entradas existentes.

  • discard_key=<name> — define uma métrica com a chave name

    onde os valores das métricas descartadas são acumulados. Por padrão, nenhuma métrica desse tipo é criada. A chave reservada não pode ser atualizada manualmente.

  • mode — algoritmo de processamento de dados (veja a seção Modos de Operação);

  • parameters — configurações adicionais para o modo selecionado

    (por exemplo, factor para average exp).

Exemplo de uso:

metric_zone session_time:1m max;

Na árvore da API, o modelo de zona de memória compartilhada se parece com o seguinte:

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

Nota

Em uma zona de 1 MB, com um tamanho de chave de 39 bytes e um único modo de métrica, aproximadamente 8.000 entradas de chaves únicas podem ser armazenadas.

Modos de Operação#

Lista de modos de operação de métricas disponíveis:

  • count — contador;

  • gauge — medidor (incremento/decremento);

  • last — o último valor recebido;

  • min — valor mínimo;

  • max — valor máximo;

  • average exp — média móvel exponencial (EMA) (parâmetro factor);

  • average mean — média sobre uma janela (parâmetros window e count);

  • histogram — distribuição em "buckets" (uma lista de valores de limite).

Os exemplos abaixo usam o endpoint /status/ do exemplo de configuração. Cada resultado está disponível em /status/stream/metric_zones/<zone>/metrics/.

count#

Cada atualização da métrica incrementa o contador em 1, independentemente do valor fornecido.

Valor padrão — 0.

Exemplos:

metric_zone count:1m count;

# Como parte de uma métrica complexa:
#
# metric_complex_zone count:1m {
#     some_metric_name  count;
# }

server {
    listen 12345;

    metric count KEY;
}

Atualizando a 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 da métrica na API:

{
    "KEY": 4
}

gauge#

O gauge aumenta ou diminui seu valor dependendo do sinal do número informado. Um valor positivo aumenta o contador, enquanto um valor negativo o diminui. Um valor de 0 não altera o contador.

Valor padrão — 0.

Exemplos:

metric_zone gauge:1m gauge;

# Como parte de uma métrica complexa:
#
# 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;
}

Atualizando a métrica:

$ nc 127.0.0.1 12351 </dev/null

Valor esperado da métrica na API:

{
    "KEY": 0
}

Atualizações adicionais:

$ 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 da métrica na API:

{
    "KEY": 8
}

last#

Armazena o último valor recebido sem qualquer agregação. Se value for omitido, 0 é usado.

Exemplos:

metric_zone last:1m last;

# Como parte de uma métrica complexa:
#
# 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;
}

Atualizando a métrica:

$ nc 127.0.0.1 12361 </dev/null

Valor esperado da métrica na API:

{
    "KEY": 0
}

Atualizações adicionais:

$ 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 da métrica na API:

{
   "KEY": -3.5
}

min#

Salva o mínimo de dois valores — o valor atualmente armazenado e o novo.

Exemplos:

metric_zone min:1m min;

# Como parte de uma métrica complexa:
#
# 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;
}

Atualizando a 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 da métrica na API:

{
    "KEY": -512
}

max#

Salva o máximo de dois valores — o valor atualmente armazenado e o novo.

Exemplos:

metric_zone max:1m max;

# Como parte de uma métrica complexa:
#
# 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;
}

Atualizando a 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 da métrica na API:

{
    "KEY": 42.999
}

average exp#

Calcula o valor médio usando o algoritmo de suavização exponencial.

Aceita um parâmetro opcional factor=<number> — um coeficiente que determina o quanto o novo valor influencia a média. Valores inteiros de 0 a 99 são permitidos. O padrão é 90.

Quanto maior o coeficiente, mais peso os novos valores têm. Se você especificar 90, o resultado será 90% do novo valor e 10% da média anterior.

Exemplos:

metric_zone avg_exp:1m average exp factor=60;

# Como parte de uma métrica complexa:
#
# 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;
}

Atualizando a 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 da métrica na API:

{
    "KEY": 30.16
}

average mean#

Calcula a média aritmética. Aceita parâmetros opcionais window=<off|time> e count=<number>, definindo o intervalo de tempo e o tamanho da amostra para cálculo da média, respectivamente. Padrões: window=off (toda a amostra é usada) e count=10.

Nota

Por exemplo, window=5s considerará apenas eventos dos últimos 5 segundos. O parâmetro window não pode ser 0. O parâmetro count=number controla o tamanho da amostra (valores em cache) para um cálculo de média mais suave.

Exemplos:

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

# Como parte de uma métrica complexa:
#
# 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;
}

Atualizando a 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 da métrica na API:

{
    "KEY": 2.1
}

Se você aguardar 5 segundos desde a última atualização, o valor esperado será:

{
    "KEY": 0
}

histogram#

Cria um conjunto de "buckets", incrementando o contador correspondente se o novo valor não exceder o limite do bucket. Os parâmetros são fornecidos como uma lista de limites numéricos. Útil para analisar distribuições, como durações de sessão.

Os parâmetros obrigatórios são numbers — os valores de limite dos buckets, normalmente listados em ordem crescente.

Nota

O valor de bucket inf ou +Inf pode ser usado para capturar todos os valores que excedem o bucket mais alto especificado.

Exemplos:

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

# Como parte de uma métrica complexa:
#
# 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;
}

Atualizando a métrica:

$ nc 127.0.0.1 12411 </dev/null

Valor esperado da métrica na API:

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

Atualizações adicionais:

$ nc 127.0.0.1 12412 </dev/null

Valor esperado da métrica na API:

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

Atualização adicional:

$ nc 127.0.0.1 12413 </dev/null

Valor esperado da métrica na API:

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

Variáveis Integradas#

Variáveis são criadas para cada métrica:

  • $metric_<name>

  • $metric_<name>_key

  • $metric_<name>_value

Para métricas complexas, uma variável adicional é adicionada:

  • $metric_<name>_value_<metric>

$metric_<name>#

De forma semelhante à diretiva metric, o setter da variável $metric_<name> pode ser usado para atualizar uma métrica. O cálculo ocorre quando a variável é definida — por exemplo, por meio da diretiva set, que é avaliada durante a fase Pre-access (a mesma etapa que on=connect), ou de forma programática a partir do módulo njs, por exemplo em um manipulador js_access.

O setter aceita uma chave com um sufixo opcional =value. O último = separa a chave do valor; sem o sufixo, o valor é 0. A chave e o valor podem conter texto, variáveis, ou combinações de ambos. Essas são as mesmas regras de análise usadas em metric.

Exemplo de uso:

stream {
    metric_zone hits:1m count;

    # Neste ponto, a variável $metric_hits é adicionada

    server {
        listen 12345;

        metric hits client=1 on=connect;
    }

    server {
        listen 12346;

        set $metric_hits client=1;

        return "$metric_hits\n";
    }
}

Após três conexões à porta 12345 e uma conexão à porta 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

Ao ler $metric_<name> de volta, obtém-se a chave junto com o valor calculado atual da métrica, na forma key=value — e não a string literal que foi atribuída. No exemplo acima, a conexão atribui o valor literal client=1, mas como o modo count ignora o valor atribuído e simplesmente incrementa a cada atualização, ler $metric_hits posteriormente retorna client=4 — o novo total do contador, que já inclui as três atualizações anteriores feitas pela porta 12345.

Além disso, o valor armazenado na variável $metric_<name>_key passa a ser a chave especificada.

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

As variáveis $metric_<name>_key e $metric_<name>_value definem a chave e o valor, respectivamente. A atualização da métrica ocorre quando $metric_<name>_value é definida, desde que a chave em $metric_<name>_key já tenha sido definida.

Nota

Para métricas complexas, os valores das submétricas na variável $metric_<name>_value são unidos usando um separador ", ".

Exemplo de uso:

stream {
    metric_zone level:1m gauge;

    # As variáveis $metric_level, $metric_level_key e $metric_level_value são adicionadas aqui.

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

    # $metric_stats, $metric_stats_key e $metric_stats_value são adicionadas aqui.

    server {
        listen 12345;

        metric level sensor=10 on=connect;
    }

    server {
        listen 12346;

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

        # Ou: 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;

        # Ou: set $metric_stats bar=9;

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

Com esta configuração, uma conexão à porta 12345 (que define o valor inicial do gauge como 10) seguida de uma conexão à porta 12346 produz:

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

Uma conexão à porta 12347 produz:

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

Nota

Se uma string vazia for atribuída a $metric_<name>_value, o valor é reconhecido como 0. Se a string for composta por caracteres que não podem ser convertidos em um número, ela é reconhecida como 1.

O cálculo ocorre somente depois que tanto $metric_<name>_key quanto $metric_<name>_value tiverem sido definidas.

Nesse caso, o valor armazenado em $metric_<name> passa a ser igual ao novo par calculado key=value, e não aos valores literais recém-atribuídos.

O valor em $metric_<name>_key representa a última chave especificada por meio de variáveis.

O valor em $metric_<name>_value representa o último valor calculado para a chave definida em $metric_<name>_key.

$metric_<name>_value_<metric>#

Para métricas complexas, o valor de uma submétrica específica pode ser obtido usando a variável $metric_<name>_value_<metric>, onde <metric> é o nome da submétrica.

Exemplo de uso:

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

    # Adiciona $metric_foo, $metric_foo_key, $metric_foo_value,
    # e $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;

        # Ou: set $metric_foo bar=9;

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

Com esta configuração, uma conexão à porta 12345 produz:

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

Exemplos Adicionais#

Monitorando Conexões por Protocolo#

metric_zone protocols:1m count;

server {
    listen 12345;
    listen 12346 udp;

    metric protocols $protocol;
}

Resposta:

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

Distribuição do Tempo de Sessão do 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;
}

Resposta:

{
    "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
        }
    }
}

Conexões Ativas#

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/;
        }
    }
}

Resposta:

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

Suporte ao Prometheus#

O Angie inclui um módulo integrado para exibir métricas no formato Prometheus, que suporta métricas personalizadas, incluindo aquelas coletadas no lado stream. Como a exposição do Prometheus é um recurso exclusivo de HTTP, as diretivas prometheus_template e prometheus são configuradas em um bloco http, fazendo referência à zona de métricas stream por meio da mesma árvore unificada da API /status/.

Como exemplo de integração, considere a seguinte configuração:

stream {
    # Criando a métrica "upload"
    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;

        # Atualizando a métrica ao final da sessão com o total
        # de bytes recebidos do cliente
        proxy_pass backend;
        metric upload angie=$bytes_received on=end;
    }
}

http {
    # Descrevendo o template Prometheus para a métrica "upload"
    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;

        # Destino para coleta de métricas
        location /prometheus/upload_metric/ {
            prometheus upload_metric;
        }
    }
}

Após cinco conexões à porta 12345, enviando payloads de 16384, 64448, 64, 1028 e 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

Os valores da métrica serão:

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

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

No formato Prometheus, a métrica está disponível em /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