Skip to content

OpenTelemetryの計装していたらplugの解決策が見つかった事例

plug では、plugはなぜ文字列をキーとして扱うのかで書いたように、文字列で置き換え対象の関数を指定するインターフェイスになっている。これは仕方がないものだと思っていたので何も考えていなかった。

ところで、OpenTelemetryで計装するとき、パッケージ単位で MeterTracer を用意する必要があるけれど、引数にパッケージのインポートパスを渡す必要があり、これが地味に面倒くさい。なので以下のような実装をしていた。

type t struct{}
var tracer = otel.Tracer(reflect.TypeOf(t{}).PkgPath())

これでインポートパスを書く必要がなくなり、多少便利にはなっているのだが、パッケージごとにこれを書き続けるのもまた面倒くさいと感じるので、なんとかしたいなと以下のような方法を考えた。

// コードは適当
func Auto() *otel.Tracer {
p := getParentFunc() // コールスタックから1つ前の関数ポインタを取得
f := runtime.FuncForPC(uintptr(p))
return otel.Tracer(f.Name())
}

ここで f.Name() は以下のようなスタイルで名前を返す。

  • time.Now
  • os/user.Current
  • net/http.(*Client).Do
  • math/rand/v2.N[...]

ここまで思いついて、plug でも同じようにすれば文字列での関数名を省略できるんじゃないかと連想した。具体的には以下のように。

v := reflect.ValueOf(fn)
p := v.UnsafePointer()
f := runtime.FuncForPC(uintptr(p))
plug.Set(f.Name(), func() {})

型パラメータのある関数では少し奇妙な文字列になっているが、plug の場合は設定した側と呼び出された時の判定で比較できればいいので、これでよい。