Metric#

O módulo ngx_http_metric_module permite criar métricas arbitrárias calculadas em tempo real. Esses valores de métricas são armazenados em memória compartilhada e exibidos em tempo real no ramo da API /status/http/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.

Exemplo de Configuração#

Contando requisições de API:

http {
    metric_zone api_requests:1m count;

    server {
        listen 80;

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

            metric api_requests $http_user_agent on=request;
        }
    }
}

Se uma requisição for feita para /api/ com esta configuração:

$ curl 127.0.0.1/api/ --user-agent "Firefox"

A métrica api_requests é atualizada em tempo real:

{
    "http": {
       "metric_zones": {
           "api_requests": {
               "discarded": 0,
               "metrics": {
                   "Firefox": 1
               }
           }
       }
    }
}

Diretivas#

metric_zone#

Sintaxe

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

Padrão

Contexto

http

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/http/metric_zones/.

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 novas;

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

      preservando as entradas existentes.

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

    onde valores de 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 request_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,
    }
}

discarded

Número; a contagem de métricas descartadas na zona de memória compartilhada

metrics

Objeto; seus membros são métricas com chaves definidas e valores calculados

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.

metric_complex_zone#

Sintaxe

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

Padrão

Contexto

http

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.

Exemplo de uso:

metric_complex_zone requests: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
        }
    }
}

discarded

Número; a contagem de métricas descartadas na zona de memória compartilhada

metrics

Objeto; seus membros são métricas complexas com chaves definidas. Eles são objetos contendo um conjunto de submétricas com valores calculados

metric#

Sintaxe

metric name key=value [on=request| response| end];

Padrão

Contexto

http, server, location

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. Se a chave for maior, ela será truncada para 255 bytes e anexada com reticências ...;

  • 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 especificando quando a métrica é calculada:

    • Se on=request, o cálculo ocorre quando a requisição é recebida;

    • Se on=response, o cálculo ocorre durante a preparação da resposta;

    • Se on=end (padrão), o cálculo ocorre após o envio da resposta.

Nota

No caso de redirecionamento interno, métricas no estágio on=request são calculadas no location original. No entanto, métricas on=response e on=end serão calculadas no novo location.

Exemplo de uso:

metric requests $http_user_agent=$request_time;

Nota

Métricas com uma chave vazia ou 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 variáveis são avaliadas em diferentes fases. Por exemplo, é impossível usar $bytes_sent (bytes enviados ao cliente) com on=request (quando a requisição é recebida).

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).

count#

O contador aumenta seu valor em 1 a cada atualização de métrica.

Valor padrão — 0.

Nota

Qualquer atualização de métrica (com qualquer valor) aumenta monotonicamente o contador em 1.

Exemplos:

metric_zone count:1m count;

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

server {
    listen 80;

    location /metric/ {
        metric count KEY;
    }

    location ~ ^/metric/set/(.+)$ {
        metric count KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/count/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/set/23
$ curl 127.0.0.1/metric/set/-32

Valor esperado da métrica na API:

{
    "KEY": 4
}

gauge#

O gauge aumenta ou diminui seu valor dependendo do sinal do número passado. 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 80;

    location /metric/ {
        metric gauge KEY;
    }

    location ~ ^/metric/set/(.+)$ {
        metric gauge KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/gauge/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/

Valor esperado da métrica na API:

{
    "KEY": 0
}

Atualizações adicionais:

$ curl 127.0.0.1/metric/set/5
$ curl 127.0.0.1/metric/set/-5
$ curl 127.0.0.1/metric/set/8

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 80;

    location /metric/ {
        metric last KEY;
    }

    location ~ ^/metric/set/(.+)$ {
        metric last KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/last/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/

Valor esperado da métrica na API:

{
    "KEY": 0
}

Atualizações adicionais:

$ curl 127.0.0.1/metric/set/8000
$ curl 127.0.0.1/metric/set/37
$ curl 127.0.0.1/metric/set/-3.5

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 80;

    location /metric/ {
        metric min KEY;
    }

    location ~ ^/metric/set/(.+)$ {
        metric min KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/min/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/set/42.999
$ curl 127.0.0.1/metric/set/-512
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/

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 80;

    location /metric/ {
        metric max KEY;
    }

    location ~ ^/metric/set/(.+)$ {
        metric max KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/max/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/set/42.999
$ curl 127.0.0.1/metric/set/-512
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/

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 80;

    location ~ ^/metric/set/(.+)$ {
        metric avg_exp KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/avg_exp/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/set/100
$ curl 127.0.0.1/metric/set/200
$ curl 127.0.0.1/metric/set/0
$ curl 127.0.0.1/metric/set/8
$ curl 127.0.0.1/metric/set/30

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 80;

    location ~ ^/metric/set/(.+)$ {
        metric avg_mean KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/avg_mean/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/set/0.1
$ curl 127.0.0.1/metric/set/0.1
$ curl 127.0.0.1/metric/set/0.4
$ curl 127.0.0.1/metric/set/10
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/set/1

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 relevante 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 tempos de resposta.

Parâmetros obrigatórios são numbers — os valores de limite dos buckets, 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 80;

    location ~ ^/metric/set/(.+)$ {
        metric histogram KEY=$1;
    }

    location /api/ {
        api /status/http/metric_zones/hist/metrics/;
    }
}

Atualizando a métrica:

$ curl 127.0.0.1/metric/set/0.25

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:

$ curl 127.0.0.1/metric/set/2

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:

$ curl 127.0.0.1/metric/set/1000

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

Semelhante à diretiva metric, a variável setter $metric_<name> pode ser usada para atualizar uma métrica. O cálculo ocorre durante a fase de Rewrite, permitindo o processamento de métricas a partir do módulo njs, por exemplo.

O valor usado para definir a variável deve seguir a estrutura chave=valor. Tanto a chave quanto o valor podem consistir em texto, variáveis e combinações destes. A chave é uma string arbitrária para agrupar valores. O valor é um número 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.

Exemplo de uso:

http {
    metric_zone counter:1m count;

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

    server {
        listen 80;

        location /metric/ {
            set $metric_counter $http_user_agent;  # Equivalente a $http_user_agent=0
        }

        location /api/ {
            allow 127.0.0.1;
            deny all;
            api /status/http/metric_zones/counter/;
        }
    }
}

Calculando métricas usando o módulo njs:

http {
    js_import metrics.js;

    resolver 127.0.0.53;

    metric_complex_zone requests:1m {
        min_time        min;
        max_time        max;
        total           count;
    }

    location /metric/ {
        js_content metrics.js_request;
        js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
    }

    location /api/ {
        allow 127.0.0.1;
        deny all;
        api /static/http/metric_zones/requests/;
    }
}

Arquivo metrics.js:

async function js_request(r) {
    let start_time = Date.now();

    let results = await Promise.all([ngx.fetch('https://google.com/'),
                                     ngx.fetch('https://google.ru/')]);

    // Usando a variável setter $metric_requests
    r.variables.metric_requests = `google={Date.now() - start_time}`;
}

export default {js_request};

Após várias requisições para location /metric/, os valores podem parecer assim:

{
    "discarded": 0,
    "metrics": {
        "google": {
            "min_time": 70,
            "max_time": 432,
            "total": 6
        }
    }
}

Nota

Após definir a variável, você pode recuperar seu valor; ele será igual ao par chave=valor especificado.

Além disso, o valor armazenado na variável $metric_<name>_key mudará para 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:

http {
    metric_zone gauge:1m gauge;

    # As variáveis $metric_gauge, $metric_gauge_key e $metric_gauge_value são adicionadas aqui.

    metric_complex_zone complex:1m {
        hist histogram 1 2 3;
        avg  average exp;
    }

    # $metric_complex, $metric_complex_key e $metric_complex_value são adicionadas aqui.

    server {
        listen 80;

        location /gauge/ {
            set $metric_gauge_key "foo";
            set $metric_gauge_value 1;

            # Ou: set $metric_gauge foo=1;

            return 200 "Updated with '$metric_gauge'\nValue='$metric_gauge_value'\n";
        }

        location /complex/ {
            set $metric_complex_key "foo";
            set $metric_complex_value 3;

            # Ou: set $metric_complex foo=3;

            return 200 "Updated with '$metric_complex'\nValue='$metric_complex_value'\n";
        }
    }
}

Com esta configuração, uma requisição para /gauge/ produz:

$ curl 127.0.0.1/gauge/
Updated with 'foo=1'
Value='1'

Para /complex/:

$ curl 127.0.0.1/complex/
Updated with 'foo=3'
Value='0 0 1, 3'

Nota

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

O cálculo ocorre apenas após $metric_<name>_key e $metric_<name>_value terem sido definidas.

Neste caso, o valor armazenado em $metric_<name> torna-se igual ao novo par chave=valor.

O valor em $metric_<name>_key representa a última chave especificada via 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 recuperado usando a variável $metric_<name>_value_<metric>, onde <metric> é o nome da submétrica.

Exemplo de uso:

http {
    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 80;

        location /foo/ {
            set $metric_foo_key   bar;
            set $metric_foo_value 9;

            # Ou: set $metric_foo bar=9;

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

Com esta configuração, uma requisição para /foo/ produz:

$ curl 127.0.0.1/foo/
Updated with 'bar=9'
Values='1, 9, 9'
Count='1'

Exemplos Adicionais#

Monitorando Métodos HTTP#

metric_zone http_methods:1m count;

server {
    listen 80;

    location / {
        metric http_methods $request_method;
    }

    location /metrics/ {
        allow 127.0.0.1;
        deny all;
        api /status/http/metric_zones/http_methods/metrics/;
    }
}

Resposta:

{
    "GET": 65,
    "POST": 20,
    "PUT": 10,
    "DELETE": 5
}

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

    location /backend/ {
        proxy_pass http://backend;
        metric upstream_time $upstream_addr=$upstream_response_time on=end;
    }

    location /metrics/ {
        allow 127.0.0.1;
        deny all;
        api /status/http/metric_zones/upstream_time/;
    }
}

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#

metric_zone active_connections:2m gauge;

server {
    listen 80;
    server_name site1.com;

    location / {
        # Incrementamos ao conectar
        metric active_connections site1=1 on=request;

        # Decrementamos ao finalizar
        metric active_connections site1=-1 on=end;
    }
}

server {
    listen 80;
    server_name site2.com;

    location / {
        metric active_connections site2=1 on=request;
        metric active_connections site2=-1 on=end;
    }
}

server {
    listen 8080;

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

Resposta:

{
    "site1": 42,
    "site2": 17
}

Suporte ao Prometheus#

O Angie inclui um módulo integrado para exibir métricas no formato Prometheus, que suporta métricas personalizadas.

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

http {
    # 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;
    }

    # Descrevendo o template Prometheus para a métrica "upload"
    prometheus_template upload_metric {
        'stats{le="$1"}' $p8s_value
                         path=~^/http/metric_zones/upload/metrics/angie/stats/(.+)$
                         type=histogram;

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

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

    server {
        listen 80;

        # Atualizando a métrica
        location ~ ^/upload/(.*)$ {
            api /status/http/metric_zones/upload/metrics/angie/;
            metric upload angie=$1 on=request;
        }

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

Após várias requisições para /upload/...:

$ curl 127.0.0.1/upload/16384
$ curl 127.0.0.1/upload/64448
$ curl 127.0.0.1/upload/64
$ curl 127.0.0.1/upload/1028
$ curl 127.0.0.1/upload/1028

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