# Công thức metrics: máy chủ, hàng đợi, nghiệp vụ, Supabase

> Cấu hình Collector cho metrics máy chủ Linux và Windows, gauge độ sâu và tuổi hàng đợi, metrics nghiệp vụ, và metrics Postgres từ Supabase Metrics API.

Cấu hình sao chép dùng ngay cho các metric mà hầu hết app cần. Tất cả gửi metrics OTLP tới cùng endpoint với ingest key đã tích **Metrics** ([bật metrics](https://belvador.dev/vi/docs/otel/metrics#bat-metrics)).

Các công thức Collector dùng thành phần chuẩn của `otelcol-contrib`. Lượt chứng nhận của Belvador kiểm Collector chuyển tiếp metrics OTLP (đủ bốn loại); các receiver bên dưới (`hostmetrics`, `prometheus`) là thành phần contrib chuẩn mà lượt chạy đó không xoay quanh, nên sau khi khởi động hãy xem những điểm đầu tiên trên trang **Metrics**.

## Metrics máy chủ trên Linux

CPU, bộ nhớ, tải, ổ đĩa, hệ thống tệp và mạng của máy, một điểm mỗi phút, từ receiver `hostmetrics` của Collector. Chạy một Collector cho mỗi máy.

```yaml title="host-metrics.yaml"
receivers:
  hostmetrics:
    collection_interval: 60s
    scrapers:
      cpu: {}
      memory: {}
      load: {}
      disk: {}
      filesystem: {}
      network: {}

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  resourcedetection:
    detectors: [env, system]
    system:
      hostname_sources: [os]
  resource:
    attributes:
      - key: service.name
        value: ${env:BELVADOR_HOST_SERVICE}
        action: upsert
      - key: deployment.environment.name
        value: ${env:BELVADOR_ENVIRONMENT}
        action: upsert
  batch: {}

exporters:
  otlphttp/belvador:
    endpoint: ${env:BELVADOR_OTLP_ENDPOINT}
    headers:
      Authorization: Bearer ${env:BELVADOR_INGEST_KEY}
    compression: gzip
    retry_on_failure:
      enabled: true
    sending_queue:
      enabled: true

service:
  pipelines:
    metrics:
      receivers: [hostmetrics]
      processors: [memory_limiter, resourcedetection, resource, batch]
      exporters: [otlphttp/belvador]

```

```bash
export BELVADOR_OTLP_ENDPOINT=https://ingest.belvador.dev/otlp/<project-id>
export BELVADOR_INGEST_KEY=<your-ingest-key>
export BELVADOR_HOST_SERVICE=web-1
export BELVADOR_ENVIRONMENT=production
otelcol-contrib --config host-metrics.yaml
```

- **`resourcedetection`** thêm `host.name` từ hệ điều hành, nên mỗi máy là chuỗi riêng.
- **`resource`** đặt `service.name` (`BELVADOR_HOST_SERVICE`) và môi trường, để metrics máy chủ lọc giống dữ liệu của app.
- Trong container, receiver thấy container chứ không thấy máy chủ. Gắn thư mục gốc của máy chủ ở chế độ chỉ đọc và đặt `root_path` cho receiver ([tài liệu contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/hostmetricsreceiver)).
- Đã chạy [Collector chuyển tiếp](https://belvador.dev/vi/docs/otel/collector)? Thêm receiver `hostmetrics` và pipeline `metrics` vào tệp đó thay vì chạy Collector thứ hai.

Một máy gửi vài chục chuỗi; gói mặc định cho phép 10,000 chuỗi mỗi node.

## Metrics máy chủ trên Windows

Receiver này cũng chạy trên Windows. Windows không có tải trung bình (`load`); thay vào đó thêm `paging`:

```yaml title="host-metrics.yaml"
receivers:
  hostmetrics:
    collection_interval: 60s
    scrapers:
      cpu: {}
      memory: {}
      paging: {}
      disk: {}
      filesystem: {}
      network: {}

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  resourcedetection:
    detectors: [env, system]
    system:
      hostname_sources: [os]
  resource:
    attributes:
      - key: service.name
        value: ${env:BELVADOR_HOST_SERVICE}
        action: upsert
      - key: deployment.environment.name
        value: ${env:BELVADOR_ENVIRONMENT}
        action: upsert
  batch: {}

exporters:
  otlphttp/belvador:
    endpoint: ${env:BELVADOR_OTLP_ENDPOINT}
    headers:
      Authorization: Bearer ${env:BELVADOR_INGEST_KEY}
    compression: gzip
    retry_on_failure:
      enabled: true
    sending_queue:
      enabled: true

service:
  pipelines:
    metrics:
      receivers: [hostmetrics]
      processors: [memory_limiter, resourcedetection, resource, batch]
      exporters: [otlphttp/belvador]

```

Cài Collector thành Windows service, với biến môi trường chỉ thuộc service đó:

```powershell
# PowerShell as Administrator; otelcol-contrib.exe and host-metrics.yaml are in C:\otelcol
New-Service -Name otelcol -StartupType Automatic -BinaryPathName '"C:\otelcol\otelcol-contrib.exe" --config "C:\otelcol\host-metrics.yaml"'
# The variables belong to this service only (not the whole machine) and apply without a reboot.
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\otelcol -Name Environment -Type MultiString -Value @(
  "BELVADOR_OTLP_ENDPOINT=https://ingest.belvador.dev/otlp/<project-id>",
  "BELVADOR_INGEST_KEY=<your-ingest-key>",
  "BELVADOR_HOST_SERVICE=win-1",
  "BELVADOR_ENVIRONMENT=production"
)
Start-Service otelcol
```

`otelcol-contrib.exe` lấy từ [bản phát hành Collector](https://github.com/open-telemetry/opentelemetry-collector-releases/releases) (`otelcol-contrib_<version>_windows_amd64.tar.gz`). Belvador chưa tự chạy công thức này trên Windows.

## Độ sâu và tuổi của hàng đợi

Hai gauge cho biết việc chạy ngầm có theo kịp không: bao nhiêu job đang chờ, và job lâu nhất đã chờ bao lâu. Tuổi tăng dần là dấu hiệu sớm của một worker bị kẹt, trước khi có job nào thất bại.

```js title="metrics.js"
import { metrics } from "@opentelemetry/api";

const meter = metrics.getMeter("app");
const attrs = { "messaging.system": "postgres", "messaging.destination.name": "emails" };

// How many jobs wait, and how long the oldest one has waited.
meter
  .createObservableGauge("queue.depth", { unit: "{message}", description: "Jobs waiting" })
  .addCallback(async (r) => r.observe(await queue.count("emails"), attrs));
meter
  .createObservableGauge("queue.oldest_age", { unit: "s", description: "Age of the oldest waiting job" })
  .addCallback(async (r) => r.observe(await queue.oldestAgeSeconds("emails"), attrs));

// A business signal: free connections in your own pool.
meter
  .createObservableGauge("app.pool.available", { unit: "{connection}" })
  .addCallback((r) => r.observe(pool.idleCount, { "db.client.connection.pool.name": "main" }));

// A counter you increment yourself; Belvador shows it as a rate.
export const signups = meter.createCounter("app.signups", { unit: "{signup}" });
// signups.add(1, { plan: "pro" });
```

- Observable gauge được đọc mỗi lần xuất (mỗi 60 giây), nên callback là đoạn code duy nhất chạy. Giữ nó ở một truy vấn nhẹ.
- Thuộc tính theo [quy ước messaging](https://opentelemetry.io/docs/specs/semconv/messaging/) (`messaging.system`, `messaging.destination.name`): mỗi hàng đợi một chuỗi.
- Code dùng meter toàn cục, nên chạy được với cách cài không cần code trong [công thức Node.js](https://belvador.dev/vi/docs/otel/node) khi đã đặt `OTEL_METRICS_EXPORTER=otlp`. Python (`metrics.get_meter`), Java và Go có cùng các instrument.

Rồi đặt cảnh báo: monitor **Metric** trên `queue.oldest_age`, phép tổng hợp `max`, *trên* 600 trong 5 phút, nhóm theo `messaging.destination.name` — mỗi hàng đợi kẹt một cảnh báo ([Monitor theo tín hiệu](https://belvador.dev/vi/docs/monitors/signals#monitor-metric)).

## Metrics nghiệp vụ

Bất cứ thứ gì code đếm hoặc đọc được: lượt đăng ký, thanh toán, kết nối trống trong pool, số token model đã dùng. Dùng **Counter** cho việc xảy ra (`app.signups`, hiện dạng rate) và **observable gauge** cho mức (`app.pool.available`). Đặt tên ngắn có tiền tố riêng, có đơn vị, và thuộc tính ít giá trị (`plan`, không phải `user_id`).

## Metrics Supabase

Supabase công bố metrics sức khoẻ và hiệu năng của cơ sở dữ liệu Postgres trong mỗi project ở định dạng Prometheus ([Metrics API](https://supabase.com/docs/guides/telemetry/metrics)). Receiver `prometheus` của Collector đọc mỗi phút một lần và chuyển tiếp dạng OTLP:

```yaml title="supabase-metrics.yaml"
receivers:
  prometheus/supabase:
    config:
      scrape_configs:
        - job_name: supabase
          scrape_interval: 60s
          scrape_timeout: 30s
          scheme: https
          metrics_path: /customer/v1/privileged/metrics
          basic_auth:
            username: service_role
            password: ${env:SUPABASE_SECRET_KEY}
          static_configs:
            - targets: ["${env:SUPABASE_PROJECT_REF}.supabase.co"]

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 20
  resource:
    attributes:
      - key: service.name
        value: supabase
        action: upsert
      - key: deployment.environment.name
        value: ${env:BELVADOR_ENVIRONMENT}
        action: upsert
  batch: {}

exporters:
  otlphttp/belvador:
    endpoint: ${env:BELVADOR_OTLP_ENDPOINT}
    headers:
      Authorization: Bearer ${env:BELVADOR_INGEST_KEY}
    compression: gzip
    retry_on_failure:
      enabled: true
    sending_queue:
      enabled: true

service:
  pipelines:
    metrics:
      receivers: [prometheus/supabase]
      processors: [memory_limiter, resource, batch]
      exporters: [otlphttp/belvador]

```

```bash
export BELVADOR_OTLP_ENDPOINT=https://ingest.belvador.dev/otlp/<project-id>
export BELVADOR_INGEST_KEY=<your-ingest-key>
export SUPABASE_PROJECT_REF=<project-ref>
export SUPABASE_SECRET_KEY=<sb_secret_… or service_role key>
export BELVADOR_ENVIRONMENT=production
otelcol-contrib --config supabase-metrics.yaml
```

- Endpoint dùng HTTP Basic auth với user `service_role` và một khoá **bí mật**. Khoá đó đọc và ghi được toàn bộ cơ sở dữ liệu, nên chỉ để trên máy chạy Collector — không bao giờ để trong app, repository hay Belvador.
- Chạy Collector ở nơi luôn bật (một VPS nhỏ hoặc container cạnh app); Supabase không tự đẩy các metrics này.
- `service.name` là `supabase`, nên các chuỗi này tách khỏi chuỗi của app.

## Runtime khác

Mọi SDK OpenTelemetry xuất metrics OTLP/HTTP đều đọc cùng biến `OTEL_METRICS_EXPORTER=otlp`: Java agent và `opentelemetry-instrument` của Python xuất metrics runtime ngay khi đặt biến; Go cần một `MeterProvider` với exporter `otlpmetrichttp` trong code. Các exporter này theo cùng định dạng OTLP mà lượt chạy Collector kiểm, nhưng Belvador chưa chạy chúng cho metrics.
