これは OpenTelemetry Advent Calendar 2024 15日目の記事です。
前置きが少しばかり長いので、目的の情報をすぐにでも知りたい方は以下の目次からスキップしてください。
前置き
今年の私のOpenTelemetryとの付き合い話として、初めてOpenTelemetryオーガニゼーションのリポジトリ(具体的にはOpenTelemetry Collector)にPull Requestを出してマージされました。差分としては些細なものですが、今後もできる範囲で貢献できればと思います。
github.com
さて、先日私の所属する開発チームで、オブザーバビリティをテーマとした開発合宿を開催しました。私のチームはOpAMPでOpenTelemetry Collectorをリモートマネジメントする参照実装を弄りながら、ECSのサイドカー構成でもリモートで設定を制御できるか試したり、実装を加えてバイナリ配布などできることを増やしたりしていました。Advent Calendarの登録の様子を見た限りOpAMPだとネタ被りしそうだったので、この話はまたの機会にします。
この合宿で、OpenTelemetry CollectorのReceiverを作ろうとしたものの、つまづいてしまったチームがありました。どのようなところに障害があったのかをヒアリングしたところ、
情報の一次ソースがわからない。あったとして辿り着けなかった
有志がまとめた日本語*1 の情報通りにやってみたものの色々なところでうまく行かない
といった声をもらいました。
この反応を踏まえて私は、OpenTelemetry Collector Receiverの作り方を、その情報が有効である期間の違いを意識しながら少しだけ丁寧に解説してみることにします。
OpenTelemetry Collector Receiverを作るための事前知識
この記事を読むにあたって、以下の情報は事前に知っているものとします:
Go言語による開発の知識
Go Modules
go generateコマンドによるコード生成
OpenTelemetryで扱うシグナルの種類(Trace/Metric/Logなど)や、それらのデータ構造
OpenTelemetry Collectorの実行や設定の方法
OpenTelemetry Collectorを構成する主要なコンポーネントの種類(Receiver/Processor/Exporter/Pipelineなど)
OpenTelemetry Collectorをカスタムビルドするツールであるocbの存在、またその利用方法
これらについて知らないまま、OpenTelemetry CollectorのComponentを自作するところにいきなりジャンプするのは大変なのではないかと個人的には考えています。
公式ドキュメントを紹介すると、情報量の多さに挫折してしまいそうなので敢えて紹介しないことにします。それぞれ日本語でわかりやすくまとめられている記事が(少なくともOTelcol Receiver自作よりは)数多くあると思いますので、検索してあたってみてください。
OpenTelemetry Collector Receiverの実装の大まかなステップ
Opentelemetry CollectorのReceiver Componentを作りたいなら、まずは公式のドキュメント(英語)があるのでこちらをあたると良いでしょう。こちらではTracesを扱うReceiverについて扱っていますが、MetricsのReceiverを作るときも開発の流れとして共通する部分があります。
opentelemetry.io
このドキュメントやライブラリのコードリーディングから得た知識をもとに、エッセンスだけ取り出してReceiver実装の構成要素をご紹介します。Receiverを実装するために必要なのは以下の3つです:
Config
: Collectorのconfigファイルを読むための構造体定義やデフォルトの設定値
{signalType}Receiver
: テレメトリを収集する実装。Trace, Metricといったシグナルの種類ごと分かれている
Factory
: CollectorがReceiverの実体を生成するための関数
これらそれぞれをOpenTelemetry Collectorが定めたインタフェースに従って実装したコードを含むGoのpackageを作っていくと、最終的にReceiverが完成します。実際にどのようなインタフェースで、どこで定義されているのかを説明します。将来これらのインタフェースの定義が変わる可能性はありますが、どこに定義があるかと言う知識は比較的長い間役に立つはずです。
Config
まずは Collectorのconfigファイルの設定内容のうち、各Componentについて書かれた設定に関するデータ型です。
go.opentelemetry.io/collector/component packageのtype Config という型に合うデータを作れば良いです。これは型上はただのanyなので何でも良いように見えるのですが、コメントには
A valid implementation MUST pass the check componenttest.CheckConfigStruct (return nil error).
と書かれており、別のpackageで定義されているCheckConfigStruct()
関数に渡したとき、エラーではなくnilが返ってくるようなデータでなければならないようです。それは具体的にどんなものかというと、端的にはmapstructureタグをつけた構造体を定義すればよいです。なぜなら、OpenTelemetry CollectorがYAMLのconfigファイルをUnmarshalするときにはgo-viper/mapstructure というライブラリが使われているからです。
もしOpenTelemetry Collectorに用意されたvalidateコマンドでConfigを検証させたい場合には、ConfigValidator interface を満たすように、ConfigにValidate()
メソッドを生やしておきましょう。
後述のFactory
を作るときにデフォルトの設定を渡す必要があり、その時のインタフェースであるCreateDefaultConfigFunc に合わせて、func() component.Config
というシグネチャでデフォルトの設定を返す関数を作る必要もあります。
{signalType}Receiver
Receiverとしての機能を成すためのメインの実装部分になります。これは、シグナルの種類ごとインタフェースが違い、複数のシグナルを受け取ることができるReceiverを実装したいならそれぞれ別に実装する必要があります。
例としてMetricsを受け取るMetricsReceiverを見てみましょう。まず満たすべきはgo.opentelemetry.io/collector/receiver packageのMetrics interface です。型名ではMetricsとなっていますが、その実態はMetricの集合というよりはMetricsReceiverと捉えた方がわかりやすいものです(package名と同じで冗長なので省略されているのだと思います)。具体的には以下のコードのようなinterfaceになっています。
type Metrics interface {
Start(ctx context.Context, host Host) error
Shutdown(ctx context.Context) error
}
現時点では受け取るのがMetricsだろうとTracesだろうと実装の対象となるinterfaceは同じです。もちろん、将来的に変わる可能性はあります。Start()
とShutdown()
しかなくてそれぞれerrorしかないのに、どうやってメトリックを生成してパイプラインに送り込むんだというところが気になりますが、少々お待ちください。
情報がReceiverにpushされるような方式のReceiver(Receiverと言う名前からするとこれが自然ですね)実装では、例えばStart()
でサーバを起動して、Shutdown()
でそのサーバをgraceful shutdownすることになるでしょう。逆に情報をReceiverがpullするような方式のReceiver(このうち、定期的にポーリングしに行くようなものをScraperと特別に呼びます)実装では、Start()
でTickerを用いたgoroutineを開始するようにした上で、Shutdown()
でそのgoroutineをキャンセルできるようにしておくことになるでしょう。とはいえ、Scraperを作る時にはこのinterfaceを満たすようなものを自分で一から実装することはあまりなく、scraperhelper を活用して、定期実行部分の実装を任せることができます。この具体的なコードは後ほどご紹介します。
Config同様にFactory
で必要な、MetricsReceiverを生成するための関数を作っておかなければなりません。これもまたシグナルの種類ごとインタフェースが異なります。
Metricsの場合ではCreateMetricsFunc と言うtypeの関数を実装する必要があります。
type CreateMetricsFunc func (context.Context, Settings, component.Config, consumer.Metrics) (Metrics, error )
返却値にMetricsReceiver(interface名はreceiver.Metrics)とerrorがあるのは自然だと思うのですが、特筆すべきはこの関数の引数です。
まず、引数にconfigがあります。この関数の内部でreceiver.Metrics interfaceを満たす構造体を生成すると思うのですが、その時にその構造体内部に受け取ったconfigを埋めておく必要があります。それに加えて、consumer.Metrics
という型の引数もまた、receiver.Metricsの実装である構造体に渡してあげる必要があります。consumer.Metricsはその名の通りReceiverが生成するメトリックの送り付け先になっており、consumer.Metricsインタフェースが提供する関数をReceiver実装で呼ぶことでメトリックの生成ができます。
Factory
最後にReceiver全体としての実体を生成するFactoryです。ここで今まで説明してきた構成要素をまとめ上げることになります。
Receiverのために作ったpackageの中に、func NewFactory() receiver.Factory
というシグネチャの関数を定義します。例えばMetricsを受け取るReceiverの場合は以下のコードのように記述します。
package hogehogereceiver
import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/receiver"
)
func NewFactory() receiver.Factory {
return receiver.NewFactory (
component.MustNewType("hogehoge" ),
createDefaultConfig,
receiver.WithMetrics(
createMetricsFunc,
component.StabilityLevelDevelopment,
),
)
}
ここまでできたら完成です。OpenTelemetry Collectorをocbでカスタムビルドする際には、このpackageのNewFactory()を呼んでReceiverをセットアップするようなコードが生成されます。
以上の流れをまとめると以下の図のようになります。
Receiver構成要素内の依存の向きと、各要素が満たすべき型
OpenTelemetry CollectorのComponentでは、そのComponentに関する様々な情報をmetadataというYAMLに書き記すことになっています。このYAMLの情報から、実際の実装に必要なコードやテストコードを生成してくれたり、ドキュメントを生成してくれたりするコマンドラインツールがあります。それこそがmdatagenです。mdatagenがないとReceiverを作れないわけではありませんが、使えるとスキーマファーストな開発ができ、ドキュメントも生成できるので便利になるはずです。
mdatagenは昔はopentelemetry-collector-contribリポジトリに含まれていたのですが、現在はopentelemetry-collectorコアのリポジトリに移管されています。こちらのREADMEに簡単なドキュメントもあります。
github.com
mdatagenに渡すYAMLの構造はhttps://github.com/open-telemetry/opentelemetry-collector/blob/main/cmd/mdatagen/metadata-schema.yaml に示されるスキーマ(のような何か)で定義されています。個人的にはこれがJSON Schemaなどのフォーマットで定義されていると嬉しいのですが、そういう世界ではないです。このファイルを見てもよくわからない場合には、opentelemetry-collector-contribリポジトリに含まれる実際のReceiverのmetadata.yamlを眺めにいくと良いでしょう。
mdatagenは現在Metrics Receiverのために重点的に開発されており、metadata.yamlに記述したMetricsやその属性の定義から定数や関数を生成してくれます。Receiver以外のコンポーネントやMetrics以外のReceiverを作る際にも部分的に利用することができます。
mdatagenはv0.115.0現在もalpha statusで、破壊的な変更が行われたり、検証されなかったバグを含むリリースが行われたりすることがあります。昔に書かれたReceiverの作り方の記事通りにやっても上手くいかない原因の一つがここにあると推測されます。
具体的な実装例
さて、ここからは以下のようなReceiverを実際に作るコードを見ながら、具体的な実装の流れを掴んでいきましょう:
sampleという名前のReceiver
configで指定した一定間隔で、メトリックを生成する(Scraper)
メトリックはsample.ultimate_answerという名前で、常に42という値を持つ
configで指定したanswerer_nameというキーの設定値を、sample.answerer.nameデータポイント属性として持つ
実装Stepごとcommitを分けたリポジトリを用意しておきましたので、合わせて参考にしてください:
github.com
この記事を参考にOTel Col DistributionやComponentを実装する場合、コード片やコマンドは(特に、Arthur1という個人名について)各自の環境に合わせて読み替えてください。
また、ここに書かれている情報は近い将来にも変更され、そのままでは動かなくなる可能性があることにご留意ください。
Step 0: 開発準備
Go 1.22.0以上を用意しましょう。
続いて、mdatagenをインストールしましょう。mdatagenはまだalpha版でビルド済みバイナリが提供されていない上に、go.modでローカルのreplaceを使っている関係からか、よくある外部のgo install・go runも利用できません(Issueはこちら 。
以下のようにOpenTelemetry Collectorのリポジトリを落としてきてインストールする必要があります。
$ git clone git@github.com:open-telemetry/opentelemetry-collector.git
$ cd opentelemetry-collector
$ git switch -d v0.115.0 # 後述のocbとバージョンを合わせておくとトラブルが少ないはず
$ (cd ./cmd/mdatagen; go install .)
$ mdatagen -v
mdatagen version (devel)
動作確認に使うCollectorをビルドするためのocbもインストールしましょう。ocbのインストール方法はドキュメント に記載されています。
以下のように(AppleシリコンmacOSの場合)して、ocbが実行可能かつパスが通っている状態であることを前提に説明します。
$ curl --proto '=https' --tlsv1.2 -fL -o ocb \
https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/cmd%2Fbuilder%2Fv0.115.0/ocb_0.115.0_darwin_arm64
mv ./ocb /usr/local/bin/ocb
chmod +x /usr/local/bin/ocb
※従来ocbはgo install経由でインストールする方法が案内されていましたが、現在こちらは非推奨となっています。
Step 1: Disto Moduleを作る
まずは作ったReceiverを配布するDistribution moduleを作っていきましょう。OpenTelemetry CollectorのComponentを公開する際には、Go Modulesではモノレポ的にサブモジュールを作ることができるのを生かして、Componentそれぞれでmoduleを作りつつ、それらを1リポジトリにまとめて提供することが多いです。
$ mkdir opentelemetry-collector-components-tutorial
$ go mod init github.com/Arthur1/opentelemetry-collector-components-tutorial
Step 2: Sample Receiver moduleを作る
次に作成するSampleReceiverを作ります。慣例として以下のようにディレクトリを切って、その中でまたmoduleを作りましょう。
$ mkdir receiver/samplereceiver
$ cd receiver/samplereceiver
$ go mod init github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver
その後、生成されるgo.modファイルのgoディレクティブを以下のように書き換えておきましょう。OpenTelemetry CollectorはGoチームがサポートするGoのバージョンをサポートする ことになっており、それと合わせるためです。
module github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver
- go 1.23.4
+ go 1.22.0
以下のようなファイルをmetadata.yamlとして用意しましょう:
type : sample
status :
class : receiver
stability :
development : [ metrics]
codeowners :
active : [ Arthur1]
attributes :
sample.answerer.name :
type : string
description : Answerer Name
metrics :
sample.ultimate_answer :
enabled : true
description : Ultimate Answer
unit : "1"
gauge :
value_type : double
attributes : [ sample.answerer.name]
コンポーネントの名前や安定性、属性やメトリックの定義といった内容がメタデータとして記述されているのがわかります。
また、このファイルからコード生成する下準備として、go:generate
ディレクティブを含んだgenerate.goファイルを以下のように作っておきます。
package samplereceiver
Step 4: mdatagenでコード・ドキュメント生成
実際にmdatagenを使ってコード生成していきましょう。
go generate ./...
コマンドを実行すると、同ディレクトリ直下にテストファイルやドキュメントファイルができるほか、internal/metadata packageにメタデータから生成されたコードが置かれます。
なお、go generateコマンドのデフォルトの動作ではmdatagenが異常終了したとしてもその出力を確認することができません。go generate -v ./...
というようにverboseオプションをつけると、標準エラー出力の内容が出力されます。
生成されたGoのコードでは様々なpackageのimportがされているので、合わせてgo mod tidy
を実行しておきましょう。ここまで終わると以下のように、generated_component_test.goファイルのNewFactory()
がundefinedというコンパイルエラーだけ表示される状態になるはずです。
Step 5: Configの実装
さて、NewFactoryを作るにはConfigとMetricsReceiverが必要という話は前にした通りです。まずはConfigを作っていきましょう。
config.goファイルを作成し以下のように書き込みます:
package samplereceiver
import (
"github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver/internal/metadata"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/receiver/scraperhelper"
)
type config struct {
scraperhelper.ControllerConfig `mapstructure:",squash"`
metadata.MetricsBuilderConfig `mapstructure:",squash"`
AnswerName string `mapstructure:"answerer_name"`
}
func createDefaultConfig() component.Config {
cc := scraperhelper.NewDefaultControllerConfig()
mbc := metadata.DefaultMetricsBuilderConfig()
return &config{
ControllerConfig: cc,
MetricsBuilderConfig: mbc,
AnswerName: "" ,
}
}
var _ component.CreateDefaultConfigFunc = createDefaultConfig
answerer_nameというconfigキーを受け取ってメトリック生成時に属性として使いたいのでconfigにAnswerNameがあることは分かりやすいでしょう。これとは別に2つ構造体を埋め込んでいます。
scraperhelper.ControllerConfig はscraperhelperがが作るReceiverに渡すScrapeに関する設定を定義しています。Scraperとしては例えば定期実行する間隔をconfigで設定したいところですが、これはCollectorの設定のcollection_interval
というキーに書けば良いということが、ControllerConfig structのタグからわかります。
sample :
answer_name : Arthur1
collection_interval : 10s
metadata.MetricsBuilderConfigは、mdatagenでコード生成した中に含まれるもので、メトリックごとenabledを制御できるようなconfigの構造を埋め込んでくれます。
今回は生成するメトリックがデフォルトでenableになるようにmetadata.yamlを書いているので、そこまで気にする必要はありません。仮にそうしなかった場合には以下のようなconfigをユーザが書くことによって、メトリックを個別に有効化することができます。
sample :
answer_name : Arthur1
metrics :
sample.ultimate_answer :
enabled : true
config structを定義するだけでなく、のちにFactoryを実装するときに備えてcomponent.CreateDefaultConfigFunc
も作っています。
Step 6: Scraperの実装
次のステップはMetricsReceiverの実装のはずですが、今回はscraperhelperを活用するので、そのための下準備としてScraperを実装します。Scraperとして1回のジョブで実行する内容を定義しておき、scraperhelperでwrapすると定期実行させるReceiverの出来上がりです。
scraper.goファイルを作って以下のように書き込みます:
package samplereceiver
import (
"context"
"time"
"github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver/internal/metadata"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/receiver"
)
type sampleScraper struct {
cfg *config
mb *metadata.MetricsBuilder
}
func (s *sampleScraper) scrape(context.Context) (pmetric.Metrics, error ) {
ts := pcommon.NewTimestampFromTime(time.Now())
s.mb.RecordSampleUltimateAnswerDataPoint(ts, 42 , s.cfg.AnswerName)
return s.mb.Emit(), nil
}
func newSampleScraper(cfg *config, settings receiver.Settings) *sampleScraper {
mb := metadata.NewMetricsBuilder(cfg.MetricsBuilderConfig, settings)
return &sampleScraper{
cfg: cfg,
mb: mb,
}
}
ここで注目したいのは func (s *sampleScraper) scrape(context.Context) (pmetric.Metrics, error)
の実装です。
s.mb.RecordSampleUltimateAnswerDataPoint(ts, 42, s.cfg.AnswerName)
では、mdatagenで生成したMetricsBuilderを呼び出してメトリックを生成しています。メトリックの時刻、値、付与する属性を型付きの引数で指定できるため、metadata.yamlで定義したメトリックスキーマと一致する形式のメトリックが安全に作れるようになっています。
この関数を呼び出しただけではバッファに残っている状態です。最終的にs.mb.Emit()
を呼ぶことによってpmetric.Metrics
形式(Metricsのパイプラインデータ形式)に変換されます。
Step 7: MetricsReceiverの実装
Step 6で作ったScraperをMetricsReceiverに変換していきましょう。
receiver.goを作成して以下のように書きます:
package samplereceiver
import (
"context"
"github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver/internal/metadata"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/receiver"
"go.opentelemetry.io/collector/receiver/scraperhelper"
"go.opentelemetry.io/collector/scraper"
)
func createMetricsReceiver(
_ context.Context,
settings receiver.Settings,
rawCfg component.Config,
consumer consumer.Metrics,
) (receiver.Metrics, error ) {
cfg := rawCfg.(*config)
s := newSampleScraper(cfg, settings)
ms, err := scraper.NewMetrics(s.scrape)
if err != nil {
return nil , err
}
return scraperhelper.NewScraperControllerReceiver(
&cfg.ControllerConfig,
settings,
consumer,
scraperhelper.AddScraper(metadata.Type, ms),
)
}
var _ receiver.CreateMetricsFunc = createMetricsReceiver
receiver.CreateMetricsFunc
の型に合わせて関数を作って、scraperとの橋渡しをします。また、ここではconfigの型変換(component.Config→自前で定義したconfig型)を行っています。型安全ではないのでerrも引数にとってチェックしておく方が望ましいかもしれません。
この辺りは多くのScraper形式のReceiverが全く同じようなコードを書いているので、scraperhelperを使うときにはこんな感じかと受け取っておくぐらいでひとまず良いと思います。
Step 8: NewFactoryの実装
ようやく実装のゴールが見えてきました。最後にOpenTelemetry CollectorがReceiverを実体化させるために呼ぶ最初の関数であるNewFactoryを実装します。
factory.goファイルを作成し以下のように書きます:
package samplereceiver
import (
"github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver/internal/metadata"
"go.opentelemetry.io/collector/receiver"
)
func NewFactory() receiver.Factory {
return receiver.NewFactory(
metadata.Type,
createDefaultConfig,
receiver.WithMetrics(
createMetricsReceiver,
metadata.MetricsStability,
),
)
}
シグナルごとの安定性やComponentの名前に、mdatagenで生成した定数を使っているのがポイントです。
ここまで完成したらコンパイルとテストが通るようになるはずです。samplereceiver packageでgo testを実行し、テストが通ることを確認しましょう。
Step 9: 動作確認のためにビルド
これまで長らくreceiver.samplereceiverディレクトリの中で作業していましたが、2つ上のディレクトリであるDistoルートモジュールに戻ってください。
今回作ったsamplereceiverと、デバッグ用のテキスト出力ができるdebugexporterを含むOpenTelemetry Collectorをビルドしてみましょう。
otelcol-builder.yamlを用意します:
dist:
module: github.com/Arthur1/opentelemetry-collector-components-tutorial
description: Custom OpenTelemetry Collector for Testing Sample Components
output_path: .
version: 0.1.0
receivers:
- gomod: github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver v0.1.0
exporters:
- gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.115.0
replaces:
- github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver => ./receiver/samplereceiver
ポイントはreplacesを使っているところです。これはgo.modのreplace directive に相当する設定です。GitHubに上げて外部のユーザーに使ってもらうためのモジュール名をつけているけれども、手元ではローカルのものを参照したいのでこのような記述をしています。
設定ファイルができたらocbでビルドを実行します。いくつかのGoのコードが生成された後に、opentelemetry-collector-components-tutorialという実行ファイルも生成されるはずです。
$ ocb --config otelcol-builder.yaml
$ file ./opentelemetry-collector-components-tutorial
./opentelemetry-collector-components-tutorial: Mach-O 64-bit executable arm64
Step 10: 動作確認
生成されたOpenTelemetry Collectorのバイナリを早速実行したいところですが、Collectorを動かすための設定ファイルが必要です。
otelcol-config.yamlを以下のように用意します:
receivers :
sample :
answerer_name : Arthur1
collection_interval : 10s
exporters :
debug :
verbosity : detailed
service :
pipelines :
metrics :
receivers :
- sample
exporters :
- debug
あとは生成されたバイナリにオプションを渡して実行します。
./opentelemetry-collector-components-tutorial --config otelcol-config.yaml
以下のような出力が10秒おきに増えていたら成功です!
2024-12-16T06:55:11.725+0900 info Metrics {"kind": "exporter", "data_type": "metrics", "name": "debug", "resource metrics": 1, "metrics": 1, "data points": 1}
2024-12-16T06:55:11.726+0900 info ResourceMetrics #0
Resource SchemaURL:
ScopeMetrics #0
ScopeMetrics SchemaURL:
InstrumentationScope github.com/Arthur1/opentelemetry-collector-components-tutorial/receiver/samplereceiver 0.1.0
Metric #0
Descriptor:
-> Name: sample.ultimate_answer
-> Description: Ultimate Answer
-> Unit: 1
-> DataType: Gauge
NumberDataPoints #0
Data point attributes:
-> sample.answerer.name: Str(Arthur)
StartTimestamp: 2024-12-15 21:55:10.7219 +0000 UTC
Timestamp: 2024-12-15 21:55:11.72505 +0000 UTC
Value: 42.000000
{"kind": "exporter", "data_type": "metrics", "name": "debug"}
まとめ
今なお活発にアップデートが続くOpenTelemetry Collectorの世界でComponentを自作する方法を、Collector自作につまづいてしまった人の声を参考にまとめ直してみました。それでも私は理解している側ではあるので、致命的なところで説明不足な部分がまだまだあるでしょう。随時改善していければと思いますので、SNSや記事のコメントでフィードバックをいただけますと幸いです。良かったよ、だけでもとても嬉しいです。