地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

opentelemetry-goを使ってメトリクスを送信する

opentelemetry-goを使ってメトリクスをotlpで送信して標準出力に出すまでのメモです

otel-collectorlのconfig

fileをstdoutにすることで送信先をstdoutにすることができます。送信されてきたメトリクスをこれでプロセスの出力としてみることができます。

receivers:
  otlp:
    protocols:
      grpc:
      http:

exporters:
  file:
    path: /dev/stdout

processors:
  batch:

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [file]

docker-composeを使って起動しておく

---
version: '3.8'
services:
  otel-collector:
    image: otel/opentelemetry-collector:0.67.0
    restart: always
    command: ["--config=/etc/otel-collector-config.yaml", ""]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - "4317:4317" # for grpc
      - "4318:4318" # for http

アプリケーションを実装する

github.com

opentelemetry-goの中のmetricを使えばよさそうです。また送信用のクライアントにはotelmetrichttpを使用します。

pkg.go.dev

package main

import (
    "context"
    "fmt"
    "runtime"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
    api "go.opentelemetry.io/otel/metric"
    "go.opentelemetry.io/otel/sdk/metric"
)

func main() {
    ctx := context.Background()
    exp, err := otlpmetrichttp.New(ctx, otlpmetrichttp.WithInsecure()) // 送信先などはここで設定できるが今回はlocalhostなので不要
    if err != nil {
        panic(err)
    }

    meterProvider := metric.NewMeterProvider(metric.WithReader(metric.NewPeriodicReader(exp)))
    defer func() {
        if err := meterProvider.Shutdown(ctx); err != nil {
            panic(err)
        }
    }()
    otel.SetMeterProvider(meterProvider)
    meter := otel.Meter("go.opentelemetry.io/otel/metric#MultiAsyncExample")

    // 計測する項目と概要を定義する
    cpuUsage, _ := meter.Int64ObservableGauge(
        "cpuUsage",
        api.WithDescription("CPU Usage in %"),
    )

        // コレクション中に呼び出される関数を登録する
    _, err = meter.RegisterCallback(
        func(_ context.Context, o api.Observer) error {
            memStats := &runtime.MemStats{}
            // This call does work
            runtime.ReadMemStats(memStats)
            o.ObserveInt64(cpuUsage,
                int64(60),
                api.WithAttributes(
                    attribute.String("label", "value"),  // 属性を設定
                    attribute.Bool("env-prod", true),  // valueの型ごとにメソッドが用意されている
                ),
            )
            return nil
        },
        cpuUsage,
    )
    if err != nil {
        fmt.Println("Failed to register callback")
        panic(err)
    }
}

実行する

実行することで登録したプロパイダーに対してメトリクスを送信する

{"resourceMetrics":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"unknown_service:main"}},{"key":"telemetry.sdk.language","value":{"stringValue":"go"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.16.0"}}]},"scopeMetrics":[{"scope":{"name":"go.opentelemetry.io/otel/metric#MultiAsyncExample"},"metrics":[{"name":"cpuUsage","description":"CPU Usage in %","gauge":{"dataPoints":[{"attributes":[{"key":"env-prod","value":{"boolValue":true}},{"key":"label","value":{"stringValue":"value"}}],"startTimeUnixNano":"11651379494838206464","timeUnixNano":"1692277452252071000","asInt":"60"}]}}]}],"schemaUrl":"https://opentelemetry.io/schemas/1.17.0"}]}