以前の記事で紹介したとおり ocaml-opentelemetry を使うと OCaml で OpenTelemetry のメトリクスを出力することができます。
OpenTelemetry ではメトリクスに attribute を付与することができ[1]、Prometheus のラベルのように使えます。
ocaml-opentelemetry で用意されている Opentelemetry.Instrument.Int_gauge などの定義済み instrument には attribute のサポートがありません。そのため attribute を出力したい場合は自分で instrument を定義する必要があります。
前回同様、ocaml-opentelemetry の v0.91 と Eio 0.3 を対象とし、attribute を出力できる Int_gauge を作ってみます。カスタムの instrument を作るには Opentelemetry.Instrument.Make ファンクタを使います:
module Int_gauge = struct
open Opentelemetry
include Instrument.Make (struct
type data = key_value list * int option
type state = { mtx : Eio.Mutex.t; data : (key_value list, int) Hashtbl.t }
let kind = "gauge"
let init () = { mtx = Eio.Mutex.create (); data = Hashtbl.create 0 }
let update { mtx; data } (k, v) =
Eio.Mutex.use_rw ~protect:true mtx @@ fun () ->
match v with Some v -> Hashtbl.replace data k v | None -> Hashtbl.remove data k
let to_metrics { mtx; data } ~name ?description ?unit_ ~clock () =
let now = Clock.now clock in
match
Eio.Mutex.use_ro mtx @@ fun () ->
Hashtbl.to_seq data |> Seq.map (fun (attrs, value) -> Metrics.int ~attrs ~now value) |> List.of_seq
with
| [] -> []
| datapoints -> [ Metrics.gauge ~name ?description ?unit_ datapoints ]
end)
let record (instrument : (key_value list * int option) Instrument.t) k v = instrument.update (k, Some v)
let unrecord (instrument : (key_value list * int option) Instrument.t) k = instrument.update (k, None)
end
メトリクスの定義は通常の instrument と同様です:
let your_first_metric =
Int_gauge.create
~name:"your_first_metric"
~description:"Your first metric description"
()
メトリクスを出力する際も通常と同様に Int_guage.record を使いますが、このときに attribute の内容を指定します。attribute の型は Opentelemetry.key_value list になっていて、これは(定義を展開すると)以下のようになっています(参考):
type key_value =
(string * [
| `Int of int
| `String of string
| `Bool of bool
| `Float of float
| `None
])
例えば key1 に "foo" が紐づく attribute を出力する場合は以下のように書きます:
Int_gauge.record your_first_metric [("key1", `String "foo")] 1;
また、特定の attribute を持つメトリクスを削除するための Int_gauge.unrecord も定義されています:
Int_gauge.unrecord your_first_metric [("key1", `String "foo")];
例によって、動作確認は Docker Compose で Opentelemetry Collector と Prometheus を立ち上げて行うと便利です。以下のように compose.yaml を書きます:
services:
your-system:
image: your-system
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
otel-collector:
image: otel/opentelemetry-collector
volumes:
- otel-collector-config.yaml:/etc/otelcol/config.yaml
prometheus:
image: prom/prometheus:v3.11.3-distroless
command:
- --web.enable-otlp-receiver
ports:
- "59090:9090"
otel-collector-config.yaml は以下のように書いておきます:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
debug:
verbosity: detailed
otlphttp/prometheus:
endpoint: "http://prometheus:9090/api/v1/otlp"
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug]
metrics:
receivers: [otlp]
exporters: [debug, otlphttp/prometheus]
logs:
receivers: [otlp]
exporters: [debug]
http://localhost:59090 にブラウザでアクセスすると Prometheus の Web UI が開くので、メトリクスが出力されていることを確認できます。
注釈
-
さっぱり詳しくないのですが OpenTelemetry の仕様を読むと attribute はメトリクスに限らず Span などの他のものにも使える共通の仕組みっぽいです。 ↩