VaxilaはOpenTelemetryの仕組み (計装)を利用してデータを送信しています。
このページではGoのデータをVaxilaに送信する方法を解説します。
Go向けOpenTelemetry
OpenTelemetry には Go用のSDKが用意されています。
このSDKに加えて、GinやGorm用のSDKを使用すると、色々な範囲を計装することができます。
Collectorを使用するべきか
Vaxilaを使用する場合、SDKからデータを直接Vaxilaに送信するのではなく、Collectorを使うこともできます。
Collectorを使うかどうかを決める際は以下のページを参考にしてください。
導入方法
Goには複数のWebフレームワークが存在しますが、このページでは最も基本的な net/http
への導入方法を説明します。 他のフレームワークを使っている場合も以下とほぼ同じ方法で計装することができます。
以下のステップでVaxilaを導入できます。
1. go get
以下のモジュールをインストールします。
go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/exporters/otlp/otlptrace
このコマンドでは基本的なモジュールのみインストールしています。
しかし、OpenTelemetryにはGinやGorm、AWSなど色々な用途似対応したモジュールがあり、アプリケーションに合わせて追加することでより詳細に計装することが出来るようになります。
各モジュールは、OpenTelemetryのページから見つけることができます。
2. 初期設定
OpenTelemetryのデータをVaxilaに送信するためには、TracerProviderに以下の項目を設定する必要があります。
例えば、以下のようにすると、SDKから直接Vaxilaへ送信することができます。
import (
...
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
)
func initTracerProvider(ctx context.Context) (func(context.Context) error, error) {
client := otlptracehttp.NewClient(
otlptracehttp.WithEndpoint("telemetry.vaxila-labs.com"),
otlptracehttp.WithHeaders(map[string]string{
"Accept": "*/*",
"X-Vaxila-Token": os.Getenv("VAXILA_TOKEN"),
}),
otlptracehttp.WithCompression(otlptracehttp.GzipCompression),
)
exporter, err := otlptrace.New(ctx, client)
// デバッグ時にはstdouttraceが便利
// exporter, err := stdouttrace.New()
if err != nil {
return nil, err
}
resources, err := resource.New(
ctx,
resource.WithProcessPID(),
resource.WithHost(),
resource.WithAttributes(
semconv.ServiceName("acme_service"),
semconv.ServiceVersion("vX.Y.Z"),
semconv.DeploymentEnvironment("production"),
),
)
if err != nil {
return nil, err
}
tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(resources),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp.Shutdown, nil
}
この例では次の項目を設定しています。
Exporter
Endpoint & Path
Endpoint を telemetry.vaxila-labs.com
と設定することで、データをVaxilaに送信するようになります。
Collectorを利用する場合は、Collectorのエンドポイント(localhost:4318 など) を設定してください
Headers
X-Vaxila-Token
と Accept
のヘッダーを設定することでVaxilaと通信することができます。
X-Vaxila-Token
には、Vaxilaで発行されたトークンを設定してください。
Collectorを利用する場合、ヘッダーは必要ないでしょう。
Compression
データを送信する時にデータの圧縮方法を指定することができます。指定しなかった場合は圧縮されません。
Resource
TracerProviderのResourceを設定することで、データがどこから来たかわかるようになります。
3. ミドルウェアの挿入
HTTPサーバーを建てている場合、各リクエストの情報を収集するために専用のミドルウェアを挿入する必要があります。
net/http
を使用している場合には otelhttp
を利用できます。
例えば、前節で作った initTracerProvider
関数を使用すると、以下のようにできます。
import (
...
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func main() {
ctx := context.Background()
shutdown, err := initTracerProvider(ctx)
if err != nil {
panic(err)
}
defer shutdown(ctx)
otelHandler := otelhttp.NewHandler(
http.HandlerFunc(awesomeActionHandler),
"awesome_span_name",
)
http.Handle("/awesome_path/", otelHandler)
http.ListenAndServe(":80", nil)
}
func awesomeActionHandler(w http.ResponseWriter, r *http.Request) {
...
}
このページでは net/http
への計装方法を解説していますが、GinやEchoなどを使用する場合であっても、類似したインターフェースを持っているため、基本的に net/http
と変わりません。
つまり、以下のステップで計装できます。
go get でフレームワークに対応したモジュールを取得する
また、ルーティング周りが整備されている関係上、一般的に net/http
よりも計装が簡単です。
各フレームワークに対応したモジュールは以下のページから検索できます。
4. エラーを補足
Goの言語制約上、エラーを自動で補足することはできないため、エラーが発生した場合にエラーをSpanに追加するコードを書く必要があります。
エラーの追加には Span.RecordError()
関数を使用します。 また、WithStackTrace()
を使うとスタックトレースを記録することもできます。
例えば、前節で作成したハンドラー (awesomeActionHandler
) で発生したエラーを補足するには以下のようにします。
import (
...
"go.opentelemetry.io/otel/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)
func awesomeActionHandler(w http.ResponseWriter, r *http.Request) {
err := someAction()
if err != nil {
recordError(r.Context(), err)
w.WriteHeader(500)
return
}
...
}
func recordError(ctx context.Context, err error) {
span := trace.SpanFromContext(ctx)
span.RecordError(err, trace.WithStackTrace(true))
}
5. 独自の計装の追加 (任意)
独自のSpanを追加することで、任意の範囲を計装することができます。
計装することで、変数の値や処理時間を記録することができます。
具体的には、下のように Tarcer.Start()
と Span.End()
で囲むと計装を追加できます。
const (
instrumentationName = "my-instrumentron"
instrumentationVersion = "v0.1.0"
)
var (
tracer = otel.GetTracerProvider().Tracer(
instrumentationName,
trace.WithInstrumentationVersion(instrumentationVersion),
trace.WithSchemaURL(semconv.SchemaURL),
)
)
func awesomeActionHandler(w http.ResponseWriter, r *http.Request) {
ctx, span := tracer.Start(r.Context(), "awesome_action")
defer span.End()
...
}
計装の方法は他にも用意されています。詳細はOpenTelemetryのドキュメントを参照してください。