From f5be7351dfb64a0e9e7bc4d0f96a61d90703f46c Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Mon, 27 Oct 2025 10:59:52 -0300 Subject: [PATCH 1/7] Add OCB testing structure Signed-off-by: Arthur Silva Sens --- go.mod | 30 +++- go.sum | 117 +++++++++++- testing/ocb/.gitignore | 11 ++ testing/ocb/Makefile | 83 +++++++++ testing/ocb/README.md | 305 ++++++++++++++++++++++++++++++++ testing/ocb/builder-config.yaml | 24 +++ testing/ocb/otel-config.yaml | 49 +++++ 7 files changed, 612 insertions(+), 7 deletions(-) create mode 100644 testing/ocb/.gitignore create mode 100644 testing/ocb/Makefile create mode 100644 testing/ocb/README.md create mode 100644 testing/ocb/builder-config.yaml create mode 100644 testing/ocb/otel-config.yaml diff --git a/go.mod b/go.mod index 756f57a2..1724caea 100644 --- a/go.mod +++ b/go.mod @@ -13,12 +13,37 @@ require ( golang.org/x/time v0.13.0 ) +require ( + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/component v1.44.0 // indirect + go.opentelemetry.io/collector/consumer v1.44.0 // indirect + go.opentelemetry.io/collector/featuregate v1.44.0 // indirect + go.opentelemetry.io/collector/internal/telemetry v0.138.0 // indirect + go.opentelemetry.io/collector/pdata v1.44.0 // indirect + go.opentelemetry.io/collector/pipeline v1.44.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.13.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/log v0.14.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect + google.golang.org/grpc v1.76.0 // indirect +) + require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect @@ -26,9 +51,10 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect + go.opentelemetry.io/collector/receiver v1.44.0 golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.37.0 // indirect golang.org/x/text v0.30.0 // indirect - google.golang.org/protobuf v1.36.8 // indirect + google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/go.sum b/go.sum index 249eb299..2accd935 100644 --- a/go.sum +++ b/go.sum @@ -8,14 +8,31 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -26,6 +43,12 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= @@ -40,32 +63,116 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/collector/component v1.44.0 h1:SX5UO/gSDm+1zyvHVRFgpf8J1WP6U3y/SLUXiVEghbE= +go.opentelemetry.io/collector/component v1.44.0/go.mod h1:geKbCTNoQfu55tOPiDuxLzNZsoO9//HRRg10/8WusWk= +go.opentelemetry.io/collector/consumer v1.44.0 h1:vkKJTfQYBQNuKas0P1zv1zxJjHvmMa/n7d6GiSHT0aw= +go.opentelemetry.io/collector/consumer v1.44.0/go.mod h1:t6u5+0FBUtyZLVFhVPgFabd4Iph7rP+b9VkxaY8dqXU= +go.opentelemetry.io/collector/consumer/consumertest v0.138.0 h1:1PwWhjQ3msYhcml/YeeSegjUAVC4nlA8+LY5uKqJbHk= +go.opentelemetry.io/collector/consumer/consumertest v0.138.0/go.mod h1:2XBKvZKVcF/7ts1Y+PxTgrQiBhXAnzMfT+1VKtzoDpQ= +go.opentelemetry.io/collector/consumer/xconsumer v0.138.0 h1:peQ59TyBmt30lv4YH8gfBbTSJPuPIZW0kpFTfk45rVk= +go.opentelemetry.io/collector/consumer/xconsumer v0.138.0/go.mod h1:ivpzDlwQowx8RTOZBPa281/4NvNBvhabm7JmeAbsGIU= +go.opentelemetry.io/collector/featuregate v1.44.0 h1:/GeGhTD8f+FNWS7C4w1Dj0Ui9Jp4v2WAdlXyW1p3uG8= +go.opentelemetry.io/collector/featuregate v1.44.0/go.mod h1:d0tiRzVYrytB6LkcYgz2ESFTv7OktRPQe0QEQcPt1L4= +go.opentelemetry.io/collector/internal/telemetry v0.138.0 h1:xHHYlPh1vVvr+ip0ct288l1joc4bsEeHh0rcY3WVXJo= +go.opentelemetry.io/collector/internal/telemetry v0.138.0/go.mod h1:evqf71fdIMXdQEofbs1bVnBUzfF6zysLMLR9bEAS9Xw= +go.opentelemetry.io/collector/pdata v1.44.0 h1:q/EfWDDKrSaf4hjTIzyPeg1ZcCRg1Uj7VTFnGfNVdk8= +go.opentelemetry.io/collector/pdata v1.44.0/go.mod h1:LnsjYysFc3AwMVh6KGNlkGKJUF2ReuWxtD9Hb3lSMZk= +go.opentelemetry.io/collector/pdata/pprofile v0.138.0 h1:ElnIPJK8jVzHYSnzbIVjg/v2Yq8iVLUKf7kB00zUFlE= +go.opentelemetry.io/collector/pdata/pprofile v0.138.0/go.mod h1:M7/5+Q4LohEkEB38kHhFu3S3XCA1eGSGz5uSXvNyMlM= +go.opentelemetry.io/collector/pipeline v1.44.0 h1:EFdFBg3Wm2BlMtQbUeork5a4KFpS6haInSr+u/dk8rg= +go.opentelemetry.io/collector/pipeline v1.44.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= +go.opentelemetry.io/collector/receiver v1.44.0 h1:oPgHg7u+aqplnVTLyC3FapTsAE7BiGdTtDceE1BuTJg= +go.opentelemetry.io/collector/receiver v1.44.0/go.mod h1:NzkrGOIoWigOG54eF92ZGfJ8oSWhqGHTT0ZCGaH5NMc= +go.opentelemetry.io/contrib/bridges/otelzap v0.13.0 h1:aBKdhLVieqvwWe9A79UHI/0vgp2t/s2euY8X59pGRlw= +go.opentelemetry.io/contrib/bridges/otelzap v0.13.0/go.mod h1:SYqtxLQE7iINgh6WFuVi2AI70148B8EI35DSk0Wr8m4= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= +go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= +go.opentelemetry.io/otel/log/logtest v0.14.0 h1:BGTqNeluJDK2uIHAY8lRqxjVAYfqgcaTbVk1n3MWe5A= +go.opentelemetry.io/otel/log/logtest v0.14.0/go.mod h1:IuguGt8XVP4XA4d2oEEDMVDBBCesMg8/tSGWDjuKfoA= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/slim/otlp v1.8.0 h1:afcLwp2XOeCbGrjufT1qWyruFt+6C9g5SOuymrSPUXQ= +go.opentelemetry.io/proto/slim/otlp v1.8.0/go.mod h1:Yaa5fjYm1SMCq0hG0x/87wV1MP9H5xDuG/1+AhvBcsI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0 h1:Uc+elixz922LHx5colXGi1ORbsW8DTIGM+gg+D9V7HE= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0/go.mod h1:VyU6dTWBWv6h9w/+DYgSZAPMabWbPTFTuxp25sM8+s0= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0 h1:i8YpvWGm/Uq1koL//bnbJ/26eV3OrKWm09+rDYo7keU= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0/go.mod h1:pQ70xHY/ZVxNUBPn+qUWPl8nwai87eWdqL3M37lNi9A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/testing/ocb/.gitignore b/testing/ocb/.gitignore new file mode 100644 index 00000000..cc6d57dc --- /dev/null +++ b/testing/ocb/.gitignore @@ -0,0 +1,11 @@ +# Ignore build output +dist/ + +# Ignore logs +*.log + +# Ignore temporary config files +*-local.yaml +*-test.yaml + + diff --git a/testing/ocb/Makefile b/testing/ocb/Makefile new file mode 100644 index 00000000..1d49120f --- /dev/null +++ b/testing/ocb/Makefile @@ -0,0 +1,83 @@ +# Makefile for OCB Testing Environment +# Automates building and running the custom OpenTelemetry Collector + +.PHONY: help build run clean rebuild validate components version + +# Default target +help: + @echo "OCB Testing Environment - Available targets:" + @echo "" + @echo " build - Build the custom collector using OCB" + @echo " run - Run the built collector with test config" + @echo " clean - Remove build artifacts" + @echo " rebuild - Clean and build" + @echo " validate - Validate the collector configuration" + @echo " components - List all components in the built collector" + @echo " version - Show collector version information" + @echo " check-ocb - Verify OCB is installed" + @echo "" + +# Check if OCB is installed +check-ocb: + @which builder > /dev/null || ~/go/bin/builder version > /dev/null 2>&1 || (echo "ERROR: builder not found. Install it with: go install go.opentelemetry.io/collector/cmd/builder@latest" && exit 1) + @echo "✓ OCB is installed" + @~/go/bin/builder version || builder version + +# Build the collector +build: check-ocb + @echo "Building custom OpenTelemetry Collector..." + @echo "This may take a few minutes on first build..." + ~/go/bin/builder --config builder-config.yaml || builder --config builder-config.yaml + @echo "" + @echo "✓ Build complete! Binary: ./dist/otelcol-exporter-toolkit" + @echo "" + +# Run the collector +run: + @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ + echo "ERROR: Collector binary not found. Run 'make build' first."; \ + exit 1; \ + fi + @echo "Starting OpenTelemetry Collector..." + @echo "Press Ctrl+C to stop" + @echo "" + ./dist/otelcol-exporter-toolkit --config otel-config.yaml + +# Clean build artifacts +clean: + @echo "Cleaning build artifacts..." + rm -rf ./dist + @echo "✓ Clean complete" + +# Rebuild from scratch +rebuild: clean build + +# Validate configuration +validate: + @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ + echo "ERROR: Collector binary not found. Run 'make build' first."; \ + exit 1; \ + fi + @echo "Validating configuration..." + ./dist/otelcol-exporter-toolkit validate --config otel-config.yaml + @echo "✓ Configuration is valid" + +# List all components in the built collector +components: + @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ + echo "ERROR: Collector binary not found. Run 'make build' first."; \ + exit 1; \ + fi + @echo "Components included in this collector:" + @echo "" + ./dist/otelcol-exporter-toolkit components + +# Show version information +version: + @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ + echo "ERROR: Collector binary not found. Run 'make build' first."; \ + exit 1; \ + fi + ./dist/otelcol-exporter-toolkit --version + + diff --git a/testing/ocb/README.md b/testing/ocb/README.md new file mode 100644 index 00000000..b1347196 --- /dev/null +++ b/testing/ocb/README.md @@ -0,0 +1,305 @@ +# OCB Testing Environment + +This directory contains the OpenTelemetry Collector Builder (OCB) testing environment for developing and testing the embedded Prometheus exporter receiver functionality. + +## Overview + +The OpenTelemetry Collector Builder (OCB) is a tool that generates a custom OpenTelemetry Collector binary with specific components (receivers, processors, exporters, extensions) that you define in a manifest file. + +We use OCB to: +1. Build a custom collector that includes our exporter receivers +2. Test the integration of embedded Prometheus exporters +3. Validate that exporters work correctly in the OTel ecosystem + +## Directory Structure + +``` +testing/ocb/ +├── README.md # This file +├── builder-config.yaml # OCB manifest (what to include in the build) +├── otel-config.yaml # Collector runtime config (how to run it) +├── Makefile # Build automation +└── dist/ # Output directory (created by build) + └── otelcol-exporter-toolkit # The built collector binary +``` + +## Prerequisites + +### Install OCB (OpenTelemetry Collector Builder) + +```bash +# Option 1: Install via go install +go install go.opentelemetry.io/collector/cmd/builder@latest + +# Option 2: Download pre-built binary +# Visit: https://github.com/open-telemetry/opentelemetry-collector/releases +# Download the 'ocb' binary for your platform + +# Verify installation +ocb version +``` + +### System Requirements + +- Go 1.24 or later +- 2GB+ RAM for building +- Internet connection (for downloading dependencies) + +## Quick Start + +### 1. Build the Collector + +```bash +# From this directory (testing/ocb/) +make build + +# Or manually: +ocb --config builder-config.yaml +``` + +This will: +- Download all specified OTel components +- Generate Go code for the custom collector +- Build the binary to `./dist/otelcol-exporter-toolkit` + +**Build time**: First build takes 2-5 minutes (downloads dependencies). Subsequent builds are faster. + +### 2. Run the Collector + +```bash +# Run with the test config +make run + +# Or manually: +./dist/otelcol-exporter-toolkit --config otel-config.yaml +``` + +### 3. Verify It's Working + +Validate the configuration: +```bash +make validate +``` + +Check what's included: +```bash +make components +``` + +**Current build includes**: +- ✅ OTLP receiver (minimal receiver to meet requirements) +- ✅ Debug exporter (for testing metrics output) +- ✅ No processors (simplified for our use case) +- ✅ No extensions (simplified for our use case) +- ✅ Ready for custom Prometheus exporter receivers + +You can now run the collector: +```bash +make run +# Press Ctrl+C to stop +``` + +The collector will listen on: +- gRPC: `0.0.0.0:4317` +- HTTP: `0.0.0.0:4318` + +**Next steps**: Implement Phase 1 to add the base receiver framework for Prometheus exporters. + +## Configuration Files Explained + +### builder-config.yaml + +The **builder manifest** tells OCB what to include in the collector binary. It has sections for: + +- **`dist`**: Build output configuration (binary name, version, etc.) +- **`exporters`**: Where to send telemetry (debug, OTLP, Jaeger, etc.) +- **`processors`**: How to modify/filter telemetry (batch, filter, etc.) +- **`receivers`**: Where telemetry comes from (OTLP, Prometheus, etc.) +- **`extensions`**: Additional features (health check, pprof, etc.) + +Each component is specified as a Go module reference: +```yaml +receivers: + - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.115.0 +``` + +### otel-config.yaml + +The **runtime configuration** defines how the collector behaves when running: + +- **`extensions`**: Configure enabled extensions (ports, endpoints) +- **`receivers`**: Configure data collection (scrape intervals, endpoints) +- **`processors`**: Configure data processing (batch sizes, filters) +- **`exporters`**: Configure data export destinations +- **`service.pipelines`**: Wire together receivers → processors → exporters + +## Development Workflow + +### Adding a New Exporter Receiver + +1. **Update builder-config.yaml** to include your receiver: + ```yaml + receivers: + - gomod: github.com/prometheus/node_exporter/otlpreceiver v0.1.0 + ``` + +2. **Rebuild the collector**: + ```bash + make build + ``` + +3. **Update otel-config.yaml** to configure the receiver: + ```yaml + receivers: + prometheus/node_exporter: + scrape_interval: 30s + exporter_config: + # node_exporter specific config + ``` + +4. **Add to pipeline** in the `service.pipelines` section: + ```yaml + service: + pipelines: + metrics: + receivers: [otlp, prometheus/node_exporter] + # ... + ``` + +5. **Run and test**: + ```bash + make run + ``` + +### Iterative Development + +When developing a receiver: + +```bash +# Clean and rebuild +make clean build + +# Or for quick iteration: +make rebuild # Cleans and rebuilds in one command + +# Check logs for errors +make run 2>&1 | tee collector.log +``` + +## Troubleshooting + +### Build Fails with "cannot find module" + +**Problem**: OCB can't find your custom receiver module. + +**Solutions**: +- Ensure the Go module is published or use `replace` directives +- For local development, add to builder-config.yaml: + ```yaml + replaces: + - github.com/prometheus/exporter-toolkit => ../.. + ``` + +### Collector Fails to Start + +**Problem**: Runtime configuration error. + +**Check**: +1. Validate YAML syntax: `yamllint otel-config.yaml` +2. Ensure all receivers in pipelines are defined in receivers section +3. Check logs for specific error messages +4. Verify port conflicts (another service using the ports) + +### "Component not found" Error + +**Problem**: Component in otel-config.yaml wasn't included in builder-config.yaml. + +**Solution**: Add the component to builder-config.yaml and rebuild. + +### Performance Issues + +**Problem**: Collector uses too much memory/CPU. + +**Tune**: +- Adjust `memory_limiter` settings in otel-config.yaml +- Increase `batch` timeout to reduce processing frequency +- Lower telemetry detail level: `telemetry.metrics.level: basic` + +## Useful Commands + +```bash +# Build only +make build + +# Run the collector +make run + +# Clean build artifacts +make clean + +# Full rebuild +make rebuild + +# Show collector version/components +./dist/otelcol-exporter-toolkit --version +./dist/otelcol-exporter-toolkit components + +# Validate config (doesn't start collector) +./dist/otelcol-exporter-toolkit validate --config otel-config.yaml + +# Run with different config +./dist/otelcol-exporter-toolkit --config my-custom-config.yaml +``` + +## Debugging + +### Check What's Included + +```bash +# List all built-in components +./dist/otelcol-exporter-toolkit components + +# Should show: +# - receivers: otlp, (your custom receivers) +# - processors: batch, memory_limiter +# - exporters: debug, otlp +# - extensions: health_check, pprof, zpages +``` + +### Enable Debug Logging + +In otel-config.yaml: +```yaml +service: + telemetry: + logs: + level: debug # Change from 'info' to 'debug' +``` + +### Use Zpages for Live Debugging + +Navigate to http://localhost:55679/debug/tracez (when collector is running) to: +- See active pipeline statistics +- Monitor receiver/exporter health +- View live metrics + +## Next Steps + +1. ✅ **Phase 0 Complete**: You've set up the OCB environment +2. **Phase 1**: Implement the base receiver framework in `exporter-toolkit/otlpreceiver/` +3. **Phase 2**: Add Prometheus → OTel conversion logic +4. **Phase 3**: Define exporter integration interfaces +5. **Phase 4**: Create a dummy exporter and test in this environment + +## Resources + +- [OCB Documentation](https://github.com/open-telemetry/opentelemetry-collector/tree/main/cmd/builder) +- [OTel Collector Configuration](https://opentelemetry.io/docs/collector/configuration/) +- [Building Custom Receivers](https://opentelemetry.io/docs/collector/building/receiver/) +- [OTel Collector Architecture](https://opentelemetry.io/docs/collector/architecture/) + +--- + +Happy building! 🚀 + + diff --git a/testing/ocb/builder-config.yaml b/testing/ocb/builder-config.yaml new file mode 100644 index 00000000..bf7c5f5e --- /dev/null +++ b/testing/ocb/builder-config.yaml @@ -0,0 +1,24 @@ +# OpenTelemetry Collector Builder Configuration +# Minimal manifest for testing embedded Prometheus exporters + +dist: + name: otelcol-exporter-toolkit + description: Custom OpenTelemetry Collector with embedded Prometheus exporters + output_path: ./dist + version: 0.1.0 + module: github.com/prometheus/exporter-toolkit/testing/ocb/otelcol-exporter-toolkit + # OTel Collector version to use + otelcol_version: 0.138.0 + +exporters: + # Debug exporter for testing - prints telemetry to stdout + - gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.138.0 + +receivers: + # OTLP receiver - kept for testing and to meet minimum requirements + - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.138.0 + + # TODO: Add custom Prometheus exporter receivers here as we develop them + # Example (when ready): + # - gomod: github.com/prometheus/exporter-toolkit/examples/dummy_exporter/otlpreceiver v0.1.0 + # - gomod: github.com/prometheus/node_exporter/otlpreceiver v0.1.0 diff --git a/testing/ocb/otel-config.yaml b/testing/ocb/otel-config.yaml new file mode 100644 index 00000000..af5951c3 --- /dev/null +++ b/testing/ocb/otel-config.yaml @@ -0,0 +1,49 @@ +# OpenTelemetry Collector Runtime Configuration +# Minimal config for testing embedded Prometheus exporters + +# Receivers collect telemetry data +receivers: + # OTLP receiver - kept to meet minimum requirements + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + + # TODO: Add Prometheus exporter receivers here as we develop them + # Example (when dummy exporter is ready): + # prometheus/dummy: + # scrape_interval: 15s + # exporter_config: + # # Exporter-specific configuration goes here + # + # prometheus/node_exporter: + # scrape_interval: 30s + # exporter_config: + # collector: + # disable: + # - mdadm + +# Exporters send telemetry to backends +exporters: + # Debug exporter prints to stdout - useful for testing + debug: + verbosity: detailed + sampling_initial: 5 + sampling_thereafter: 200 + +# Service defines the telemetry pipelines +service: + pipelines: + # Metrics pipeline - where embedded exporters will send data + metrics: + receivers: [otlp] # TODO: Add custom exporter receivers here + exporters: [debug] + + # Telemetry configuration for the collector itself + telemetry: + logs: + level: info + development: true + encoding: console From 42151186bb555eef6688219ee839507e08299232 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Mon, 27 Oct 2025 11:29:06 -0300 Subject: [PATCH 2/7] Add Receiver interfaces and example implementation Signed-off-by: Arthur Silva Sens --- examples/testexporter/config.go | 30 ++++++ examples/testexporter/exporter.go | 57 ++++++++++ examples/testexporter/factory.go | 32 ++++++ examples/testexporter/go.mod | 48 +++++++++ examples/testexporter/go.sum | 153 ++++++++++++++++++++++++++ examples/testexporter/unmarshaler.go | 39 +++++++ go.mod | 12 +-- otlpreceiver/config.go | 77 +++++++++++++ otlpreceiver/doc.go | 104 ++++++++++++++++++ otlpreceiver/factory.go | 142 ++++++++++++++++++++++++ otlpreceiver/receiver.go | 156 +++++++++++++++++++++++++++ otlpreceiver/scraper.go | 109 +++++++++++++++++++ testing/ocb/builder-config.yaml | 12 ++- testing/ocb/otel-config.yaml | 20 ++-- 14 files changed, 969 insertions(+), 22 deletions(-) create mode 100644 examples/testexporter/config.go create mode 100644 examples/testexporter/exporter.go create mode 100644 examples/testexporter/factory.go create mode 100644 examples/testexporter/go.mod create mode 100644 examples/testexporter/go.sum create mode 100644 examples/testexporter/unmarshaler.go create mode 100644 otlpreceiver/config.go create mode 100644 otlpreceiver/doc.go create mode 100644 otlpreceiver/factory.go create mode 100644 otlpreceiver/receiver.go create mode 100644 otlpreceiver/scraper.go diff --git a/examples/testexporter/config.go b/examples/testexporter/config.go new file mode 100644 index 00000000..dee5e199 --- /dev/null +++ b/examples/testexporter/config.go @@ -0,0 +1,30 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testexporter + +import "fmt" + +// Config holds the configuration for the test exporter. +type Config struct { + // ExporterName is a simple test config field + ExporterName string `mapstructure:"exporter_name"` +} + +// Validate checks if the configuration is valid. +func (c *Config) Validate() error { + if c.ExporterName == "" { + return fmt.Errorf("exporter_name cannot be empty") + } + return nil +} diff --git a/examples/testexporter/exporter.go b/examples/testexporter/exporter.go new file mode 100644 index 00000000..d7260710 --- /dev/null +++ b/examples/testexporter/exporter.go @@ -0,0 +1,57 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testexporter + +import ( + "context" + "fmt" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/exporter-toolkit/otlpreceiver" +) + +// Exporter is a minimal test exporter that doesn't collect any real metrics. +type Exporter struct { + config *Config + registry *prometheus.Registry +} + +// NewExporter creates a new test exporter instance. +func NewExporter(config *Config) *Exporter { + return &Exporter{ + config: config, + registry: prometheus.NewRegistry(), + } +} + +// Initialize sets up the exporter and returns its registry. +func (e *Exporter) Initialize(ctx context.Context, cfg otlpreceiver.Config) (*prometheus.Registry, error) { + exporterCfg, ok := cfg.(*Config) + if !ok { + return nil, fmt.Errorf("invalid config type: expected *Config, got %T", cfg) + } + + e.config = exporterCfg + + fmt.Printf("Test exporter initialized with name: %s\n", e.config.ExporterName) + + // Return an empty registry - we'll test with hardcoded metrics + return e.registry, nil +} + +// Shutdown cleanly stops the exporter. +func (e *Exporter) Shutdown(ctx context.Context) error { + fmt.Printf("Test exporter shutting down: %s\n", e.config.ExporterName) + return nil +} diff --git a/examples/testexporter/factory.go b/examples/testexporter/factory.go new file mode 100644 index 00000000..4af3f6ca --- /dev/null +++ b/examples/testexporter/factory.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testexporter + +import ( + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/receiver" + + "github.com/prometheus/exporter-toolkit/otlpreceiver" +) + +// NewFactory creates a factory for the test exporter receiver. +func NewFactory() receiver.Factory { + receiverType := component.MustNewType("prometheus_testexporter") + + return otlpreceiver.NewFactory( + otlpreceiver.WithType(receiverType), + otlpreceiver.WithInitializer(NewExporter(&Config{})), + otlpreceiver.WithConfigUnmarshaler(&ConfigUnmarshaler{}), + ) +} diff --git a/examples/testexporter/go.mod b/examples/testexporter/go.mod new file mode 100644 index 00000000..361cd497 --- /dev/null +++ b/examples/testexporter/go.mod @@ -0,0 +1,48 @@ +module github.com/prometheus/exporter-toolkit/examples/testexporter + +go 1.24.0 + +require ( + github.com/prometheus/client_golang v1.23.2 + github.com/prometheus/exporter-toolkit v0.0.0 + go.opentelemetry.io/collector/component v1.44.0 + go.opentelemetry.io/collector/receiver v1.44.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.66.1 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/consumer v1.44.0 // indirect + go.opentelemetry.io/collector/featuregate v1.44.0 // indirect + go.opentelemetry.io/collector/internal/telemetry v0.138.0 // indirect + go.opentelemetry.io/collector/pdata v1.44.0 // indirect + go.opentelemetry.io/collector/pipeline v1.44.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.13.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/log v0.14.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/net v0.45.0 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect + google.golang.org/grpc v1.76.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect +) + +replace github.com/prometheus/exporter-toolkit => ../.. diff --git a/examples/testexporter/go.sum b/examples/testexporter/go.sum new file mode 100644 index 00000000..3b2143d1 --- /dev/null +++ b/examples/testexporter/go.sum @@ -0,0 +1,153 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/collector/component v1.44.0 h1:SX5UO/gSDm+1zyvHVRFgpf8J1WP6U3y/SLUXiVEghbE= +go.opentelemetry.io/collector/component v1.44.0/go.mod h1:geKbCTNoQfu55tOPiDuxLzNZsoO9//HRRg10/8WusWk= +go.opentelemetry.io/collector/consumer v1.44.0 h1:vkKJTfQYBQNuKas0P1zv1zxJjHvmMa/n7d6GiSHT0aw= +go.opentelemetry.io/collector/consumer v1.44.0/go.mod h1:t6u5+0FBUtyZLVFhVPgFabd4Iph7rP+b9VkxaY8dqXU= +go.opentelemetry.io/collector/consumer/consumertest v0.138.0 h1:1PwWhjQ3msYhcml/YeeSegjUAVC4nlA8+LY5uKqJbHk= +go.opentelemetry.io/collector/consumer/consumertest v0.138.0/go.mod h1:2XBKvZKVcF/7ts1Y+PxTgrQiBhXAnzMfT+1VKtzoDpQ= +go.opentelemetry.io/collector/consumer/xconsumer v0.138.0 h1:peQ59TyBmt30lv4YH8gfBbTSJPuPIZW0kpFTfk45rVk= +go.opentelemetry.io/collector/consumer/xconsumer v0.138.0/go.mod h1:ivpzDlwQowx8RTOZBPa281/4NvNBvhabm7JmeAbsGIU= +go.opentelemetry.io/collector/featuregate v1.44.0 h1:/GeGhTD8f+FNWS7C4w1Dj0Ui9Jp4v2WAdlXyW1p3uG8= +go.opentelemetry.io/collector/featuregate v1.44.0/go.mod h1:d0tiRzVYrytB6LkcYgz2ESFTv7OktRPQe0QEQcPt1L4= +go.opentelemetry.io/collector/internal/telemetry v0.138.0 h1:xHHYlPh1vVvr+ip0ct288l1joc4bsEeHh0rcY3WVXJo= +go.opentelemetry.io/collector/internal/telemetry v0.138.0/go.mod h1:evqf71fdIMXdQEofbs1bVnBUzfF6zysLMLR9bEAS9Xw= +go.opentelemetry.io/collector/pdata v1.44.0 h1:q/EfWDDKrSaf4hjTIzyPeg1ZcCRg1Uj7VTFnGfNVdk8= +go.opentelemetry.io/collector/pdata v1.44.0/go.mod h1:LnsjYysFc3AwMVh6KGNlkGKJUF2ReuWxtD9Hb3lSMZk= +go.opentelemetry.io/collector/pdata/pprofile v0.138.0 h1:ElnIPJK8jVzHYSnzbIVjg/v2Yq8iVLUKf7kB00zUFlE= +go.opentelemetry.io/collector/pdata/pprofile v0.138.0/go.mod h1:M7/5+Q4LohEkEB38kHhFu3S3XCA1eGSGz5uSXvNyMlM= +go.opentelemetry.io/collector/pipeline v1.44.0 h1:EFdFBg3Wm2BlMtQbUeork5a4KFpS6haInSr+u/dk8rg= +go.opentelemetry.io/collector/pipeline v1.44.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= +go.opentelemetry.io/collector/receiver v1.44.0 h1:oPgHg7u+aqplnVTLyC3FapTsAE7BiGdTtDceE1BuTJg= +go.opentelemetry.io/collector/receiver v1.44.0/go.mod h1:NzkrGOIoWigOG54eF92ZGfJ8oSWhqGHTT0ZCGaH5NMc= +go.opentelemetry.io/contrib/bridges/otelzap v0.13.0 h1:aBKdhLVieqvwWe9A79UHI/0vgp2t/s2euY8X59pGRlw= +go.opentelemetry.io/contrib/bridges/otelzap v0.13.0/go.mod h1:SYqtxLQE7iINgh6WFuVi2AI70148B8EI35DSk0Wr8m4= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= +go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= +go.opentelemetry.io/otel/log/logtest v0.14.0 h1:BGTqNeluJDK2uIHAY8lRqxjVAYfqgcaTbVk1n3MWe5A= +go.opentelemetry.io/otel/log/logtest v0.14.0/go.mod h1:IuguGt8XVP4XA4d2oEEDMVDBBCesMg8/tSGWDjuKfoA= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/slim/otlp v1.8.0 h1:afcLwp2XOeCbGrjufT1qWyruFt+6C9g5SOuymrSPUXQ= +go.opentelemetry.io/proto/slim/otlp v1.8.0/go.mod h1:Yaa5fjYm1SMCq0hG0x/87wV1MP9H5xDuG/1+AhvBcsI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0 h1:Uc+elixz922LHx5colXGi1ORbsW8DTIGM+gg+D9V7HE= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0/go.mod h1:VyU6dTWBWv6h9w/+DYgSZAPMabWbPTFTuxp25sM8+s0= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0 h1:i8YpvWGm/Uq1koL//bnbJ/26eV3OrKWm09+rDYo7keU= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0/go.mod h1:pQ70xHY/ZVxNUBPn+qUWPl8nwai87eWdqL3M37lNi9A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b h1:zPKJod4w6F1+nRGDI9ubnXYhU9NSWoFAijkHkUXeTK8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/examples/testexporter/unmarshaler.go b/examples/testexporter/unmarshaler.go new file mode 100644 index 00000000..e784af37 --- /dev/null +++ b/examples/testexporter/unmarshaler.go @@ -0,0 +1,39 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testexporter + +import ( + "fmt" + + "github.com/prometheus/exporter-toolkit/otlpreceiver" +) + +// ConfigUnmarshaler handles unmarshaling of test exporter configuration. +type ConfigUnmarshaler struct{} + +// UnmarshalExporterConfig parses the exporter-specific configuration. +func (u *ConfigUnmarshaler) UnmarshalExporterConfig(data map[string]interface{}) (otlpreceiver.Config, error) { + cfg := &Config{} + + // Simple manual unmarshaling for our minimal config + if name, ok := data["exporter_name"].(string); ok { + cfg.ExporterName = name + } + + if cfg.ExporterName == "" { + return nil, fmt.Errorf("exporter_name is required") + } + + return cfg, nil +} diff --git a/go.mod b/go.mod index 1724caea..411055b9 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,10 @@ require ( github.com/coreos/go-systemd/v22 v22.6.0 github.com/mdlayher/vsock v1.2.1 github.com/prometheus/common v0.66.1 + go.opentelemetry.io/collector/component v1.44.0 + go.opentelemetry.io/collector/consumer v1.44.0 + go.opentelemetry.io/collector/pdata v1.44.0 + go.uber.org/zap v1.27.0 go.yaml.in/yaml/v2 v2.4.3 golang.org/x/crypto v0.43.0 golang.org/x/sync v0.17.0 @@ -22,11 +26,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/collector/component v1.44.0 // indirect - go.opentelemetry.io/collector/consumer v1.44.0 // indirect go.opentelemetry.io/collector/featuregate v1.44.0 // indirect go.opentelemetry.io/collector/internal/telemetry v0.138.0 // indirect - go.opentelemetry.io/collector/pdata v1.44.0 // indirect go.opentelemetry.io/collector/pipeline v1.44.0 // indirect go.opentelemetry.io/contrib/bridges/otelzap v0.13.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect @@ -34,7 +35,6 @@ require ( go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250804133106-a7a43d27e69b // indirect google.golang.org/grpc v1.76.0 // indirect ) @@ -47,8 +47,8 @@ require ( github.com/mdlayher/socket v0.4.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect - github.com/prometheus/client_golang v1.20.4 // indirect - github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/client_golang v1.20.4 + github.com/prometheus/client_model v0.6.2 github.com/prometheus/procfs v0.15.1 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.opentelemetry.io/collector/receiver v1.44.0 diff --git a/otlpreceiver/config.go b/otlpreceiver/config.go new file mode 100644 index 00000000..1f1e46d8 --- /dev/null +++ b/otlpreceiver/config.go @@ -0,0 +1,77 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package otlpreceiver + +import ( + "errors" + "time" + + "go.opentelemetry.io/collector/component" +) + +// Config is the interface that exporter-specific configurations must implement. +// Each Prometheus exporter will provide its own Config implementation. +type Config interface { + // Validate checks if the configuration is valid. + Validate() error +} + +// ReceiverConfig holds the common configuration for all Prometheus exporter receivers. +type ReceiverConfig struct { + // ScrapeInterval defines how often to collect metrics from the exporter. + // Default: 30s + ScrapeInterval time.Duration `mapstructure:"scrape_interval"` + + // ExporterConfig holds the exporter-specific configuration. + // This will be unmarshaled by the exporter's ConfigUnmarshaler. + ExporterConfig map[string]interface{} `mapstructure:"exporter_config"` + + // exporterConfigInstance is the unmarshaled exporter-specific config. + // This is set by the factory after unmarshaling. + exporterConfigInstance Config +} + +// Validate checks if the ReceiverConfig is valid. +func (cfg *ReceiverConfig) Validate() error { + if cfg.ScrapeInterval <= 0 { + return errors.New("scrape_interval must be greater than 0") + } + + // Validate the exporter-specific config if it exists + if cfg.exporterConfigInstance != nil { + if err := cfg.exporterConfigInstance.Validate(); err != nil { + return err + } + } + + return nil +} + +// SetExporterConfig sets the unmarshaled exporter-specific configuration. +func (cfg *ReceiverConfig) SetExporterConfig(exporterCfg Config) { + cfg.exporterConfigInstance = exporterCfg +} + +// GetExporterConfig returns the unmarshaled exporter-specific configuration. +func (cfg *ReceiverConfig) GetExporterConfig() Config { + return cfg.exporterConfigInstance +} + +// createDefaultConfig returns a default ReceiverConfig with sensible defaults. +func createDefaultConfig() component.Config { + return &ReceiverConfig{ + ScrapeInterval: 30 * time.Second, + ExporterConfig: make(map[string]interface{}), + } +} diff --git a/otlpreceiver/doc.go b/otlpreceiver/doc.go new file mode 100644 index 00000000..bf9431c2 --- /dev/null +++ b/otlpreceiver/doc.go @@ -0,0 +1,104 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package otlpreceiver provides a framework for embedding Prometheus exporters +// as native OpenTelemetry Collector receivers. +// +// This package enables Prometheus exporters written in Go to run directly inside +// an OpenTelemetry Collector. +// +// # Overview +// +// The otlpreceiver package provides the core infrastructure for converting +// Prometheus exporters into OTel receivers: +// +// 1. Config system for exporter-specific configuration +// 2. Factory pattern for creating receiver instances +// 3. Lifecycle management (Start/Shutdown) +// 4. Periodic metric scraping from Prometheus registries +// 5. Conversion from Prometheus to OpenTelemetry metric format +// +// # Usage +// +// To integrate a Prometheus exporter, implement two interfaces: +// +// ExporterInitializer: Manages the exporter lifecycle +// +// type MyExporterInitializer struct { +// // exporter state +// } +// +// func (i *MyExporterInitializer) Initialize(ctx context.Context, cfg Config) (*prometheus.Registry, error) { +// // Initialize your exporter and return its registry +// } +// +// func (i *MyExporterInitializer) Shutdown(ctx context.Context) error { +// // Clean up resources +// } +// +// ConfigUnmarshaler: Handles exporter-specific configuration +// +// type MyConfigUnmarshaler struct{} +// +// func (u *MyConfigUnmarshaler) UnmarshalExporterConfig(data map[string]interface{}) (Config, error) { +// // Parse your exporter's configuration +// } +// +// Then create a receiver factory: +// +// factory := otlpreceiver.NewFactory( +// otlpreceiver.WithType("prometheus/myexporter"), +// otlpreceiver.WithInitializer(&MyExporterInitializer{}), +// otlpreceiver.WithConfigUnmarshaler(&MyConfigUnmarshaler{}), +// ) +// +// # Configuration +// +// The receiver supports common configuration options: +// +// receivers: +// prometheus/myexporter: +// scrape_interval: 30s +// exporter_config: +// # Your exporter-specific configuration here +// +// # Architecture +// +// The package follows a layered architecture: +// +// ┌─────────────────────────────────────┐ +// │ OTel Collector Pipeline │ +// └──────────────┬──────────────────────┘ +// │ ConsumeMetrics() +// ┌──────────────▼──────────────────────┐ +// │ prometheusReceiver │ +// │ - Lifecycle management │ +// │ - Scrape scheduling │ +// └──────────────┬──────────────────────┘ +// │ +// ┌──────────────▼──────────────────────┐ +// │ scraper │ +// │ - Gather from registry │ +// │ - Convert to OTel format │ +// └──────────────┬──────────────────────┘ +// │ +// ┌──────────────▼──────────────────────┐ +// │ ExporterInitializer │ +// │ (Your exporter implementation) │ +// └─────────────────────────────────────┘ +// +// # Thread Safety +// +// The receiver is designed to be thread-safe. The scraping loop runs in its own +// goroutine and coordinates gracefully with the shutdown process. +package otlpreceiver diff --git a/otlpreceiver/factory.go b/otlpreceiver/factory.go new file mode 100644 index 00000000..293f13fd --- /dev/null +++ b/otlpreceiver/factory.go @@ -0,0 +1,142 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package otlpreceiver + +import ( + "context" + "fmt" + + "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" +) + +// ExporterInitializer is the interface that Prometheus exporters must implement +// to be embedded in the OTel Collector. +type ExporterInitializer interface { + // Initialize sets up the exporter and returns a prometheus.Registry + // containing all the collectors. + Initialize(ctx context.Context, exporterConfig Config) (*prometheus.Registry, error) + + // Shutdown cleanly stops the exporter and releases resources. + Shutdown(ctx context.Context) error +} + +// ConfigUnmarshaler is the interface for unmarshaling exporter-specific configuration. +type ConfigUnmarshaler interface { + // UnmarshalExporterConfig parses the exporter-specific configuration + // from the raw map into a Config instance. + UnmarshalExporterConfig(data map[string]interface{}) (Config, error) +} + +// FactoryOption is a function that configures a Factory. +type FactoryOption func(*factoryConfig) + +// factoryConfig holds the configuration for creating a receiver factory. +type factoryConfig struct { + typeStr component.Type + initializer ExporterInitializer + configUnmarshaler ConfigUnmarshaler +} + +// WithType sets the receiver type identifier. +func WithType(typeStr component.Type) FactoryOption { + return func(cfg *factoryConfig) { + cfg.typeStr = typeStr + } +} + +// WithInitializer sets the exporter initializer. +func WithInitializer(initializer ExporterInitializer) FactoryOption { + return func(cfg *factoryConfig) { + cfg.initializer = initializer + } +} + +// WithConfigUnmarshaler sets the config unmarshaler. +func WithConfigUnmarshaler(unmarshaler ConfigUnmarshaler) FactoryOption { + return func(cfg *factoryConfig) { + cfg.configUnmarshaler = unmarshaler + } +} + +// NewFactory creates a new receiver factory for a Prometheus exporter. +// The factory uses the provided ExporterInitializer and ConfigUnmarshaler +// to manage the exporter lifecycle and configuration. +func NewFactory(opts ...FactoryOption) receiver.Factory { + cfg := &factoryConfig{} + for _, opt := range opts { + opt(cfg) + } + + if cfg.typeStr.String() == "" { + panic("receiver type must be specified") + } + if cfg.initializer == nil { + panic("exporter initializer must be specified") + } + if cfg.configUnmarshaler == nil { + panic("config unmarshaler must be specified") + } + + return receiver.NewFactory( + cfg.typeStr, + createDefaultConfig, + receiver.WithMetrics( + createMetricsReceiver(cfg.initializer, cfg.configUnmarshaler), + component.StabilityLevelAlpha, + ), + ) +} + +// createMetricsReceiver returns a function that creates a metrics receiver instance. +func createMetricsReceiver( + initializer ExporterInitializer, + unmarshaler ConfigUnmarshaler, +) receiver.CreateMetricsFunc { + return func( + ctx context.Context, + set receiver.Settings, + cfg component.Config, + consumer consumer.Metrics, + ) (receiver.Metrics, error) { + receiverCfg, ok := cfg.(*ReceiverConfig) + if !ok { + return nil, fmt.Errorf("invalid config type: %T", cfg) + } + + // Unmarshal the exporter-specific config + if len(receiverCfg.ExporterConfig) > 0 { + exporterCfg, err := unmarshaler.UnmarshalExporterConfig(receiverCfg.ExporterConfig) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal exporter config: %w", err) + } + receiverCfg.SetExporterConfig(exporterCfg) + } + + // Validate the complete configuration + if err := receiverCfg.Validate(); err != nil { + return nil, fmt.Errorf("invalid configuration: %w", err) + } + + // Create the receiver instance + return newPrometheusReceiver( + receiverCfg, + consumer, + set, + initializer, + ), nil + } +} diff --git a/otlpreceiver/receiver.go b/otlpreceiver/receiver.go new file mode 100644 index 00000000..df4ef01e --- /dev/null +++ b/otlpreceiver/receiver.go @@ -0,0 +1,156 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package otlpreceiver + +import ( + "context" + "fmt" + "time" + + "github.com/prometheus/client_golang/prometheus" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" +) + +// prometheusReceiver implements the receiver.Metrics interface for Prometheus exporters. +type prometheusReceiver struct { + config *ReceiverConfig + consumer consumer.Metrics + settings receiver.Settings + initializer ExporterInitializer + scraper *scraper + + registry *prometheus.Registry + cancel context.CancelFunc + done chan struct{} +} + +// newPrometheusReceiver creates a new Prometheus exporter receiver. +func newPrometheusReceiver( + config *ReceiverConfig, + consumer consumer.Metrics, + settings receiver.Settings, + initializer ExporterInitializer, +) *prometheusReceiver { + return &prometheusReceiver{ + config: config, + consumer: consumer, + settings: settings, + initializer: initializer, + done: make(chan struct{}), + } +} + +// Start begins the receiver's operation. +// It initializes the exporter and starts the scraping loop. +func (r *prometheusReceiver) Start(ctx context.Context, host component.Host) error { + r.settings.Logger.Info("Starting Prometheus exporter receiver") + + // Initialize the exporter + exporterConfig := r.config.GetExporterConfig() + registry, err := r.initializer.Initialize(ctx, exporterConfig) + if err != nil { + return fmt.Errorf("failed to initialize exporter: %w", err) + } + r.registry = registry + + // Create the scraper + r.scraper = newScraper( + r.registry, + r.consumer, + r.settings.Logger, + ) + + // Start the scraping loop + ctx, cancel := context.WithCancel(context.Background()) + r.cancel = cancel + + go r.scrapeLoop(ctx) + + r.settings.Logger.Info("Prometheus exporter receiver started successfully") + return nil +} + +// Shutdown stops the receiver's operation. +// It stops the scraping loop and shuts down the exporter. +func (r *prometheusReceiver) Shutdown(ctx context.Context) error { + r.settings.Logger.Info("Shutting down Prometheus exporter receiver") + + // Stop the scraping loop + if r.cancel != nil { + r.cancel() + // Wait for the scrape loop to finish or context to timeout + select { + case <-r.done: + r.settings.Logger.Debug("Scrape loop stopped") + case <-ctx.Done(): + r.settings.Logger.Warn("Context cancelled before scrape loop finished") + } + } + + // Shutdown the exporter + if r.initializer != nil { + if err := r.initializer.Shutdown(ctx); err != nil { + r.settings.Logger.Error("Failed to shutdown exporter") + return fmt.Errorf("failed to shutdown exporter: %w", err) + } + } + + r.settings.Logger.Info("Prometheus exporter receiver shut down successfully") + return nil +} + +// scrapeLoop periodically scrapes metrics from the Prometheus registry +// and sends them to the consumer. +func (r *prometheusReceiver) scrapeLoop(ctx context.Context) { + defer close(r.done) + + ticker := time.NewTicker(r.config.ScrapeInterval) + defer ticker.Stop() + + // Perform an immediate scrape on startup + if err := r.scrapeAndExport(ctx); err != nil { + r.settings.Logger.Error("Initial scrape failed") + } + + for { + select { + case <-ctx.Done(): + r.settings.Logger.Debug("Scrape loop context cancelled") + return + case <-ticker.C: + if err := r.scrapeAndExport(ctx); err != nil { + r.settings.Logger.Error("Scrape failed") + // Continue scraping even if one scrape fails + } + } + } +} + +// scrapeAndExport scrapes metrics from the registry and exports them to the consumer. +func (r *prometheusReceiver) scrapeAndExport(ctx context.Context) error { + metrics, err := r.scraper.Scrape(ctx) + if err != nil { + return fmt.Errorf("failed to scrape metrics: %w", err) + } + + if err := r.consumer.ConsumeMetrics(ctx, metrics); err != nil { + return fmt.Errorf("failed to consume metrics: %w", err) + } + + r.settings.Logger.Debug("Metrics scraped and exported") + + return nil +} diff --git a/otlpreceiver/scraper.go b/otlpreceiver/scraper.go new file mode 100644 index 00000000..30edda6e --- /dev/null +++ b/otlpreceiver/scraper.go @@ -0,0 +1,109 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package otlpreceiver + +import ( + "context" + "fmt" + "time" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.uber.org/zap" +) + +// scraper handles scraping metrics from a Prometheus registry and converting +// them to OpenTelemetry format. +type scraper struct { + registry *prometheus.Registry + consumer consumer.Metrics + logger *zap.Logger +} + +// newScraper creates a new scraper instance. +func newScraper( + registry *prometheus.Registry, + consumer consumer.Metrics, + logger *zap.Logger, +) *scraper { + return &scraper{ + registry: registry, + consumer: consumer, + logger: logger, + } +} + +// Scrape collects metrics from the Prometheus registry and converts them +// to OpenTelemetry pmetric.Metrics format. +func (s *scraper) Scrape(ctx context.Context) (pmetric.Metrics, error) { + // Gather metrics from the Prometheus registry + metricFamilies, err := s.registry.Gather() + if err != nil { + return pmetric.Metrics{}, fmt.Errorf("failed to gather metrics: %w", err) + } + + s.logger.Debug("Gathered metrics from registry") + + // Convert Prometheus metrics to OpenTelemetry format + metrics := pmetric.NewMetrics() + + // TODO: Implement conversion in Phase 2 + // For now, create a placeholder that will be replaced with actual conversion logic + if err := s.convertMetrics(metricFamilies, metrics); err != nil { + return pmetric.Metrics{}, fmt.Errorf("failed to convert metrics: %w", err) + } + + return metrics, nil +} + +// convertMetrics converts Prometheus metric families to OpenTelemetry metrics. +// This is a placeholder that will be fully implemented in Phase 2. +func (s *scraper) convertMetrics(metricFamilies []*dto.MetricFamily, dest pmetric.Metrics) error { + // Create a resource metrics entry + rm := dest.ResourceMetrics().AppendEmpty() + + // Add resource attributes + rm.Resource().Attributes().PutStr("service.name", "test-prometheus-exporter") + rm.Resource().Attributes().PutStr("exporter.type", "prometheus") + + // Create a scope metrics entry + sm := rm.ScopeMetrics().AppendEmpty() + sm.Scope().SetName("prometheus_exporter") + sm.Scope().SetVersion("1.0.0") + + // HARDCODED TEST METRIC: Add a simple gauge to verify the pipeline works + metric := sm.Metrics().AppendEmpty() + metric.SetName("test_pipeline_active") + metric.SetDescription("Hardcoded metric to test the receiver pipeline") + metric.SetUnit("1") + + gauge := metric.SetEmptyGauge() + dp := gauge.DataPoints().AppendEmpty() + dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + dp.SetDoubleValue(1.0) + dp.Attributes().PutStr("pipeline", "working") + + s.logger.Debug("Added hardcoded test metric to verify pipeline") + + // TODO: Phase 2 will implement the full conversion logic here + // Log the actual metrics from Prometheus for debugging + if len(metricFamilies) > 0 { + s.logger.Debug("Prometheus metrics available (not yet converted)") + } + + return nil +} diff --git a/testing/ocb/builder-config.yaml b/testing/ocb/builder-config.yaml index bf7c5f5e..345789cd 100644 --- a/testing/ocb/builder-config.yaml +++ b/testing/ocb/builder-config.yaml @@ -18,7 +18,15 @@ receivers: # OTLP receiver - kept for testing and to meet minimum requirements - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.138.0 - # TODO: Add custom Prometheus exporter receivers here as we develop them + # Test exporter receiver - minimal test to verify the framework + - gomod: github.com/prometheus/exporter-toolkit/examples/testexporter v0.0.0 + + # TODO: Add more Prometheus exporter receivers here as we develop them # Example (when ready): - # - gomod: github.com/prometheus/exporter-toolkit/examples/dummy_exporter/otlpreceiver v0.1.0 # - gomod: github.com/prometheus/node_exporter/otlpreceiver v0.1.0 + +replaces: + # Use local version of exporter-toolkit for development + - github.com/prometheus/exporter-toolkit => ../../.. + # Use local version of test exporter + - github.com/prometheus/exporter-toolkit/examples/testexporter => ../../../examples/testexporter diff --git a/testing/ocb/otel-config.yaml b/testing/ocb/otel-config.yaml index af5951c3..7677641f 100644 --- a/testing/ocb/otel-config.yaml +++ b/testing/ocb/otel-config.yaml @@ -11,19 +11,11 @@ receivers: http: endpoint: 0.0.0.0:4318 - # TODO: Add Prometheus exporter receivers here as we develop them - # Example (when dummy exporter is ready): - # prometheus/dummy: - # scrape_interval: 15s - # exporter_config: - # # Exporter-specific configuration goes here - # - # prometheus/node_exporter: - # scrape_interval: 30s - # exporter_config: - # collector: - # disable: - # - mdadm + # Test exporter receiver - verifies the framework works + prometheus_testexporter: + scrape_interval: 10s + exporter_config: + exporter_name: "my-test-exporter" # Exporters send telemetry to backends exporters: @@ -38,7 +30,7 @@ service: pipelines: # Metrics pipeline - where embedded exporters will send data metrics: - receivers: [otlp] # TODO: Add custom exporter receivers here + receivers: [otlp, prometheus_testexporter] exporters: [debug] # Telemetry configuration for the collector itself From cd6932c463ee6cf5401373472f6a0372833d3ac2 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Mon, 27 Oct 2025 11:42:27 -0300 Subject: [PATCH 3/7] Implement gauge convertion Signed-off-by: Arthur Silva Sens --- examples/testexporter/exporter.go | 30 ++++++++-- otlpreceiver/converter.go | 99 +++++++++++++++++++++++++++++++ otlpreceiver/scraper.go | 55 +++++++++-------- 3 files changed, 152 insertions(+), 32 deletions(-) create mode 100644 otlpreceiver/converter.go diff --git a/examples/testexporter/exporter.go b/examples/testexporter/exporter.go index d7260710..9a74ddff 100644 --- a/examples/testexporter/exporter.go +++ b/examples/testexporter/exporter.go @@ -21,10 +21,12 @@ import ( "github.com/prometheus/exporter-toolkit/otlpreceiver" ) -// Exporter is a minimal test exporter that doesn't collect any real metrics. +// Exporter is a minimal test exporter that exports a few test gauge metrics. type Exporter struct { - config *Config - registry *prometheus.Registry + config *Config + registry *prometheus.Registry + testGauge1 prometheus.Gauge + testGauge2 prometheus.Gauge } // NewExporter creates a new test exporter instance. @@ -46,7 +48,27 @@ func (e *Exporter) Initialize(ctx context.Context, cfg otlpreceiver.Config) (*pr fmt.Printf("Test exporter initialized with name: %s\n", e.config.ExporterName) - // Return an empty registry - we'll test with hardcoded metrics + // Create test gauge metrics + e.testGauge1 = prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "test_exporter_gauge_1", + Help: "First test gauge metric", + }) + + e.testGauge2 = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "test_exporter_gauge_2", + Help: "Second test gauge metric with labels", + }, []string{"label1", "label2"}).WithLabelValues("value1", "value2") + + // Register metrics + e.registry.MustRegister(e.testGauge1) + e.registry.MustRegister(e.testGauge2.(prometheus.Collector)) + + // Set initial values + e.testGauge1.Set(42.0) + e.testGauge2.Set(123.45) + + fmt.Printf("Test exporter registered %d gauge metrics\n", 2) + return e.registry, nil } diff --git a/otlpreceiver/converter.go b/otlpreceiver/converter.go new file mode 100644 index 00000000..e2e1ce2e --- /dev/null +++ b/otlpreceiver/converter.go @@ -0,0 +1,99 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package otlpreceiver + +import ( + "fmt" + "time" + + dto "github.com/prometheus/client_model/go" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" +) + +// converter handles conversion from Prometheus metric families to OpenTelemetry metrics. +type converter struct { + // Add any state needed for conversion +} + +// newConverter creates a new converter instance. +func newConverter() *converter { + return &converter{} +} + +// convertMetricFamily converts a single Prometheus MetricFamily to OpenTelemetry metrics. +// Currently only supports GAUGE metrics. +func (c *converter) convertMetricFamily(mf *dto.MetricFamily, scopeMetrics pmetric.ScopeMetrics) error { + if mf == nil || mf.Name == nil { + return fmt.Errorf("invalid metric family: nil or missing name") + } + + metricName := *mf.Name + metricType := mf.GetType() + + // Only handle Gauge metrics for now + if metricType != dto.MetricType_GAUGE { + // Skip non-gauge metrics silently + return nil + } + + // Create a new metric in the scope + metric := scopeMetrics.Metrics().AppendEmpty() + metric.SetName(metricName) + + if mf.Help != nil { + metric.SetDescription(*mf.Help) + } + + return c.convertGauge(mf, metric) +} + +// convertGauge converts Prometheus gauge metrics to OpenTelemetry gauge metrics. +func (c *converter) convertGauge(mf *dto.MetricFamily, metric pmetric.Metric) error { + gauge := metric.SetEmptyGauge() + + for _, promMetric := range mf.Metric { + if promMetric.Gauge == nil { + continue + } + + dp := gauge.DataPoints().AppendEmpty() + + // Set timestamp + if promMetric.TimestampMs != nil { + dp.SetTimestamp(pcommon.Timestamp(*promMetric.TimestampMs * 1_000_000)) + } else { + dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + } + + // Set value + if promMetric.Gauge.Value != nil { + dp.SetDoubleValue(*promMetric.Gauge.Value) + } + + // Set labels as attributes + c.setAttributes(promMetric.Label, dp.Attributes()) + } + + return nil +} + +// setAttributes converts Prometheus labels to OpenTelemetry attributes. +func (c *converter) setAttributes(labels []*dto.LabelPair, attrs pcommon.Map) { + for _, label := range labels { + if label.Name != nil && label.Value != nil { + attrs.PutStr(*label.Name, *label.Value) + } + } +} diff --git a/otlpreceiver/scraper.go b/otlpreceiver/scraper.go index 30edda6e..a1b86f80 100644 --- a/otlpreceiver/scraper.go +++ b/otlpreceiver/scraper.go @@ -16,12 +16,10 @@ package otlpreceiver import ( "context" "fmt" - "time" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "go.opentelemetry.io/collector/consumer" - "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.uber.org/zap" ) @@ -29,9 +27,10 @@ import ( // scraper handles scraping metrics from a Prometheus registry and converting // them to OpenTelemetry format. type scraper struct { - registry *prometheus.Registry - consumer consumer.Metrics - logger *zap.Logger + registry *prometheus.Registry + consumer consumer.Metrics + logger *zap.Logger + converter *converter } // newScraper creates a new scraper instance. @@ -41,9 +40,10 @@ func newScraper( logger *zap.Logger, ) *scraper { return &scraper{ - registry: registry, - consumer: consumer, - logger: logger, + registry: registry, + consumer: consumer, + logger: logger, + converter: newConverter(), } } @@ -71,13 +71,17 @@ func (s *scraper) Scrape(ctx context.Context) (pmetric.Metrics, error) { } // convertMetrics converts Prometheus metric families to OpenTelemetry metrics. -// This is a placeholder that will be fully implemented in Phase 2. func (s *scraper) convertMetrics(metricFamilies []*dto.MetricFamily, dest pmetric.Metrics) error { + if len(metricFamilies) == 0 { + s.logger.Debug("No metrics to convert") + return nil + } + // Create a resource metrics entry rm := dest.ResourceMetrics().AppendEmpty() // Add resource attributes - rm.Resource().Attributes().PutStr("service.name", "test-prometheus-exporter") + rm.Resource().Attributes().PutStr("service.name", "prometheus-exporter") rm.Resource().Attributes().PutStr("exporter.type", "prometheus") // Create a scope metrics entry @@ -85,25 +89,20 @@ func (s *scraper) convertMetrics(metricFamilies []*dto.MetricFamily, dest pmetri sm.Scope().SetName("prometheus_exporter") sm.Scope().SetVersion("1.0.0") - // HARDCODED TEST METRIC: Add a simple gauge to verify the pipeline works - metric := sm.Metrics().AppendEmpty() - metric.SetName("test_pipeline_active") - metric.SetDescription("Hardcoded metric to test the receiver pipeline") - metric.SetUnit("1") - - gauge := metric.SetEmptyGauge() - dp := gauge.DataPoints().AppendEmpty() - dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) - dp.SetDoubleValue(1.0) - dp.Attributes().PutStr("pipeline", "working") - - s.logger.Debug("Added hardcoded test metric to verify pipeline") - - // TODO: Phase 2 will implement the full conversion logic here - // Log the actual metrics from Prometheus for debugging - if len(metricFamilies) > 0 { - s.logger.Debug("Prometheus metrics available (not yet converted)") + // Convert each metric family + for _, mf := range metricFamilies { + if mf == nil { + continue + } + + err := s.converter.convertMetricFamily(mf, sm) + if err != nil { + s.logger.Debug("Failed to convert metric family") + continue + } } + s.logger.Debug("Converted Prometheus metrics to OpenTelemetry format") + return nil } From b1957502b34f29325a362324cad7f742ebcf5042 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Tue, 28 Oct 2025 18:28:20 -0300 Subject: [PATCH 4/7] Remove OCB testing configs, moved to another repo Signed-off-by: Arthur Silva Sens --- testing/ocb/.gitignore | 11 -- testing/ocb/Makefile | 83 --------- testing/ocb/README.md | 305 -------------------------------- testing/ocb/builder-config.yaml | 32 ---- testing/ocb/otel-config.yaml | 41 ----- 5 files changed, 472 deletions(-) delete mode 100644 testing/ocb/.gitignore delete mode 100644 testing/ocb/Makefile delete mode 100644 testing/ocb/README.md delete mode 100644 testing/ocb/builder-config.yaml delete mode 100644 testing/ocb/otel-config.yaml diff --git a/testing/ocb/.gitignore b/testing/ocb/.gitignore deleted file mode 100644 index cc6d57dc..00000000 --- a/testing/ocb/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Ignore build output -dist/ - -# Ignore logs -*.log - -# Ignore temporary config files -*-local.yaml -*-test.yaml - - diff --git a/testing/ocb/Makefile b/testing/ocb/Makefile deleted file mode 100644 index 1d49120f..00000000 --- a/testing/ocb/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# Makefile for OCB Testing Environment -# Automates building and running the custom OpenTelemetry Collector - -.PHONY: help build run clean rebuild validate components version - -# Default target -help: - @echo "OCB Testing Environment - Available targets:" - @echo "" - @echo " build - Build the custom collector using OCB" - @echo " run - Run the built collector with test config" - @echo " clean - Remove build artifacts" - @echo " rebuild - Clean and build" - @echo " validate - Validate the collector configuration" - @echo " components - List all components in the built collector" - @echo " version - Show collector version information" - @echo " check-ocb - Verify OCB is installed" - @echo "" - -# Check if OCB is installed -check-ocb: - @which builder > /dev/null || ~/go/bin/builder version > /dev/null 2>&1 || (echo "ERROR: builder not found. Install it with: go install go.opentelemetry.io/collector/cmd/builder@latest" && exit 1) - @echo "✓ OCB is installed" - @~/go/bin/builder version || builder version - -# Build the collector -build: check-ocb - @echo "Building custom OpenTelemetry Collector..." - @echo "This may take a few minutes on first build..." - ~/go/bin/builder --config builder-config.yaml || builder --config builder-config.yaml - @echo "" - @echo "✓ Build complete! Binary: ./dist/otelcol-exporter-toolkit" - @echo "" - -# Run the collector -run: - @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ - echo "ERROR: Collector binary not found. Run 'make build' first."; \ - exit 1; \ - fi - @echo "Starting OpenTelemetry Collector..." - @echo "Press Ctrl+C to stop" - @echo "" - ./dist/otelcol-exporter-toolkit --config otel-config.yaml - -# Clean build artifacts -clean: - @echo "Cleaning build artifacts..." - rm -rf ./dist - @echo "✓ Clean complete" - -# Rebuild from scratch -rebuild: clean build - -# Validate configuration -validate: - @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ - echo "ERROR: Collector binary not found. Run 'make build' first."; \ - exit 1; \ - fi - @echo "Validating configuration..." - ./dist/otelcol-exporter-toolkit validate --config otel-config.yaml - @echo "✓ Configuration is valid" - -# List all components in the built collector -components: - @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ - echo "ERROR: Collector binary not found. Run 'make build' first."; \ - exit 1; \ - fi - @echo "Components included in this collector:" - @echo "" - ./dist/otelcol-exporter-toolkit components - -# Show version information -version: - @if [ ! -f ./dist/otelcol-exporter-toolkit ]; then \ - echo "ERROR: Collector binary not found. Run 'make build' first."; \ - exit 1; \ - fi - ./dist/otelcol-exporter-toolkit --version - - diff --git a/testing/ocb/README.md b/testing/ocb/README.md deleted file mode 100644 index b1347196..00000000 --- a/testing/ocb/README.md +++ /dev/null @@ -1,305 +0,0 @@ -# OCB Testing Environment - -This directory contains the OpenTelemetry Collector Builder (OCB) testing environment for developing and testing the embedded Prometheus exporter receiver functionality. - -## Overview - -The OpenTelemetry Collector Builder (OCB) is a tool that generates a custom OpenTelemetry Collector binary with specific components (receivers, processors, exporters, extensions) that you define in a manifest file. - -We use OCB to: -1. Build a custom collector that includes our exporter receivers -2. Test the integration of embedded Prometheus exporters -3. Validate that exporters work correctly in the OTel ecosystem - -## Directory Structure - -``` -testing/ocb/ -├── README.md # This file -├── builder-config.yaml # OCB manifest (what to include in the build) -├── otel-config.yaml # Collector runtime config (how to run it) -├── Makefile # Build automation -└── dist/ # Output directory (created by build) - └── otelcol-exporter-toolkit # The built collector binary -``` - -## Prerequisites - -### Install OCB (OpenTelemetry Collector Builder) - -```bash -# Option 1: Install via go install -go install go.opentelemetry.io/collector/cmd/builder@latest - -# Option 2: Download pre-built binary -# Visit: https://github.com/open-telemetry/opentelemetry-collector/releases -# Download the 'ocb' binary for your platform - -# Verify installation -ocb version -``` - -### System Requirements - -- Go 1.24 or later -- 2GB+ RAM for building -- Internet connection (for downloading dependencies) - -## Quick Start - -### 1. Build the Collector - -```bash -# From this directory (testing/ocb/) -make build - -# Or manually: -ocb --config builder-config.yaml -``` - -This will: -- Download all specified OTel components -- Generate Go code for the custom collector -- Build the binary to `./dist/otelcol-exporter-toolkit` - -**Build time**: First build takes 2-5 minutes (downloads dependencies). Subsequent builds are faster. - -### 2. Run the Collector - -```bash -# Run with the test config -make run - -# Or manually: -./dist/otelcol-exporter-toolkit --config otel-config.yaml -``` - -### 3. Verify It's Working - -Validate the configuration: -```bash -make validate -``` - -Check what's included: -```bash -make components -``` - -**Current build includes**: -- ✅ OTLP receiver (minimal receiver to meet requirements) -- ✅ Debug exporter (for testing metrics output) -- ✅ No processors (simplified for our use case) -- ✅ No extensions (simplified for our use case) -- ✅ Ready for custom Prometheus exporter receivers - -You can now run the collector: -```bash -make run -# Press Ctrl+C to stop -``` - -The collector will listen on: -- gRPC: `0.0.0.0:4317` -- HTTP: `0.0.0.0:4318` - -**Next steps**: Implement Phase 1 to add the base receiver framework for Prometheus exporters. - -## Configuration Files Explained - -### builder-config.yaml - -The **builder manifest** tells OCB what to include in the collector binary. It has sections for: - -- **`dist`**: Build output configuration (binary name, version, etc.) -- **`exporters`**: Where to send telemetry (debug, OTLP, Jaeger, etc.) -- **`processors`**: How to modify/filter telemetry (batch, filter, etc.) -- **`receivers`**: Where telemetry comes from (OTLP, Prometheus, etc.) -- **`extensions`**: Additional features (health check, pprof, etc.) - -Each component is specified as a Go module reference: -```yaml -receivers: - - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.115.0 -``` - -### otel-config.yaml - -The **runtime configuration** defines how the collector behaves when running: - -- **`extensions`**: Configure enabled extensions (ports, endpoints) -- **`receivers`**: Configure data collection (scrape intervals, endpoints) -- **`processors`**: Configure data processing (batch sizes, filters) -- **`exporters`**: Configure data export destinations -- **`service.pipelines`**: Wire together receivers → processors → exporters - -## Development Workflow - -### Adding a New Exporter Receiver - -1. **Update builder-config.yaml** to include your receiver: - ```yaml - receivers: - - gomod: github.com/prometheus/node_exporter/otlpreceiver v0.1.0 - ``` - -2. **Rebuild the collector**: - ```bash - make build - ``` - -3. **Update otel-config.yaml** to configure the receiver: - ```yaml - receivers: - prometheus/node_exporter: - scrape_interval: 30s - exporter_config: - # node_exporter specific config - ``` - -4. **Add to pipeline** in the `service.pipelines` section: - ```yaml - service: - pipelines: - metrics: - receivers: [otlp, prometheus/node_exporter] - # ... - ``` - -5. **Run and test**: - ```bash - make run - ``` - -### Iterative Development - -When developing a receiver: - -```bash -# Clean and rebuild -make clean build - -# Or for quick iteration: -make rebuild # Cleans and rebuilds in one command - -# Check logs for errors -make run 2>&1 | tee collector.log -``` - -## Troubleshooting - -### Build Fails with "cannot find module" - -**Problem**: OCB can't find your custom receiver module. - -**Solutions**: -- Ensure the Go module is published or use `replace` directives -- For local development, add to builder-config.yaml: - ```yaml - replaces: - - github.com/prometheus/exporter-toolkit => ../.. - ``` - -### Collector Fails to Start - -**Problem**: Runtime configuration error. - -**Check**: -1. Validate YAML syntax: `yamllint otel-config.yaml` -2. Ensure all receivers in pipelines are defined in receivers section -3. Check logs for specific error messages -4. Verify port conflicts (another service using the ports) - -### "Component not found" Error - -**Problem**: Component in otel-config.yaml wasn't included in builder-config.yaml. - -**Solution**: Add the component to builder-config.yaml and rebuild. - -### Performance Issues - -**Problem**: Collector uses too much memory/CPU. - -**Tune**: -- Adjust `memory_limiter` settings in otel-config.yaml -- Increase `batch` timeout to reduce processing frequency -- Lower telemetry detail level: `telemetry.metrics.level: basic` - -## Useful Commands - -```bash -# Build only -make build - -# Run the collector -make run - -# Clean build artifacts -make clean - -# Full rebuild -make rebuild - -# Show collector version/components -./dist/otelcol-exporter-toolkit --version -./dist/otelcol-exporter-toolkit components - -# Validate config (doesn't start collector) -./dist/otelcol-exporter-toolkit validate --config otel-config.yaml - -# Run with different config -./dist/otelcol-exporter-toolkit --config my-custom-config.yaml -``` - -## Debugging - -### Check What's Included - -```bash -# List all built-in components -./dist/otelcol-exporter-toolkit components - -# Should show: -# - receivers: otlp, (your custom receivers) -# - processors: batch, memory_limiter -# - exporters: debug, otlp -# - extensions: health_check, pprof, zpages -``` - -### Enable Debug Logging - -In otel-config.yaml: -```yaml -service: - telemetry: - logs: - level: debug # Change from 'info' to 'debug' -``` - -### Use Zpages for Live Debugging - -Navigate to http://localhost:55679/debug/tracez (when collector is running) to: -- See active pipeline statistics -- Monitor receiver/exporter health -- View live metrics - -## Next Steps - -1. ✅ **Phase 0 Complete**: You've set up the OCB environment -2. **Phase 1**: Implement the base receiver framework in `exporter-toolkit/otlpreceiver/` -3. **Phase 2**: Add Prometheus → OTel conversion logic -4. **Phase 3**: Define exporter integration interfaces -5. **Phase 4**: Create a dummy exporter and test in this environment - -## Resources - -- [OCB Documentation](https://github.com/open-telemetry/opentelemetry-collector/tree/main/cmd/builder) -- [OTel Collector Configuration](https://opentelemetry.io/docs/collector/configuration/) -- [Building Custom Receivers](https://opentelemetry.io/docs/collector/building/receiver/) -- [OTel Collector Architecture](https://opentelemetry.io/docs/collector/architecture/) - ---- - -Happy building! 🚀 - - diff --git a/testing/ocb/builder-config.yaml b/testing/ocb/builder-config.yaml deleted file mode 100644 index 345789cd..00000000 --- a/testing/ocb/builder-config.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# OpenTelemetry Collector Builder Configuration -# Minimal manifest for testing embedded Prometheus exporters - -dist: - name: otelcol-exporter-toolkit - description: Custom OpenTelemetry Collector with embedded Prometheus exporters - output_path: ./dist - version: 0.1.0 - module: github.com/prometheus/exporter-toolkit/testing/ocb/otelcol-exporter-toolkit - # OTel Collector version to use - otelcol_version: 0.138.0 - -exporters: - # Debug exporter for testing - prints telemetry to stdout - - gomod: go.opentelemetry.io/collector/exporter/debugexporter v0.138.0 - -receivers: - # OTLP receiver - kept for testing and to meet minimum requirements - - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.138.0 - - # Test exporter receiver - minimal test to verify the framework - - gomod: github.com/prometheus/exporter-toolkit/examples/testexporter v0.0.0 - - # TODO: Add more Prometheus exporter receivers here as we develop them - # Example (when ready): - # - gomod: github.com/prometheus/node_exporter/otlpreceiver v0.1.0 - -replaces: - # Use local version of exporter-toolkit for development - - github.com/prometheus/exporter-toolkit => ../../.. - # Use local version of test exporter - - github.com/prometheus/exporter-toolkit/examples/testexporter => ../../../examples/testexporter diff --git a/testing/ocb/otel-config.yaml b/testing/ocb/otel-config.yaml deleted file mode 100644 index 7677641f..00000000 --- a/testing/ocb/otel-config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# OpenTelemetry Collector Runtime Configuration -# Minimal config for testing embedded Prometheus exporters - -# Receivers collect telemetry data -receivers: - # OTLP receiver - kept to meet minimum requirements - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - http: - endpoint: 0.0.0.0:4318 - - # Test exporter receiver - verifies the framework works - prometheus_testexporter: - scrape_interval: 10s - exporter_config: - exporter_name: "my-test-exporter" - -# Exporters send telemetry to backends -exporters: - # Debug exporter prints to stdout - useful for testing - debug: - verbosity: detailed - sampling_initial: 5 - sampling_thereafter: 200 - -# Service defines the telemetry pipelines -service: - pipelines: - # Metrics pipeline - where embedded exporters will send data - metrics: - receivers: [otlp, prometheus_testexporter] - exporters: [debug] - - # Telemetry configuration for the collector itself - telemetry: - logs: - level: info - development: true - encoding: console From 7033f27f74865a0386c62ac7f418b6bdf9788a33 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 29 Oct 2025 09:00:35 -0300 Subject: [PATCH 5/7] Allow exporters to pass default configuration Signed-off-by: Arthur Silva Sens --- otlpreceiver/config.go | 7 ++----- otlpreceiver/factory.go | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/otlpreceiver/config.go b/otlpreceiver/config.go index 1f1e46d8..601cbc66 100644 --- a/otlpreceiver/config.go +++ b/otlpreceiver/config.go @@ -16,8 +16,6 @@ package otlpreceiver import ( "errors" "time" - - "go.opentelemetry.io/collector/component" ) // Config is the interface that exporter-specific configurations must implement. @@ -69,9 +67,8 @@ func (cfg *ReceiverConfig) GetExporterConfig() Config { } // createDefaultConfig returns a default ReceiverConfig with sensible defaults. -func createDefaultConfig() component.Config { - return &ReceiverConfig{ +func createDefaultConfig() ReceiverConfig { + return ReceiverConfig{ ScrapeInterval: 30 * time.Second, - ExporterConfig: make(map[string]interface{}), } } diff --git a/otlpreceiver/factory.go b/otlpreceiver/factory.go index 293f13fd..b62e1205 100644 --- a/otlpreceiver/factory.go +++ b/otlpreceiver/factory.go @@ -27,7 +27,7 @@ import ( // to be embedded in the OTel Collector. type ExporterInitializer interface { // Initialize sets up the exporter and returns a prometheus.Registry - // containing all the collectors. + // containing all the metrics collectors. Initialize(ctx context.Context, exporterConfig Config) (*prometheus.Registry, error) // Shutdown cleanly stops the exporter and releases resources. @@ -49,6 +49,7 @@ type factoryConfig struct { typeStr component.Type initializer ExporterInitializer configUnmarshaler ConfigUnmarshaler + defaultConfig map[string]interface{} } // WithType sets the receiver type identifier. @@ -72,6 +73,12 @@ func WithConfigUnmarshaler(unmarshaler ConfigUnmarshaler) FactoryOption { } } +func WithComponentDefaults(defaults map[string]interface{}) FactoryOption { + return func(cfg *factoryConfig) { + cfg.defaultConfig = defaults + } +} + // NewFactory creates a new receiver factory for a Prometheus exporter. // The factory uses the provided ExporterInitializer and ConfigUnmarshaler // to manage the exporter lifecycle and configuration. @@ -91,9 +98,15 @@ func NewFactory(opts ...FactoryOption) receiver.Factory { panic("config unmarshaler must be specified") } + componentDefaultsFunc := func() component.Config { + config := createDefaultConfig() + config.ExporterConfig = cfg.defaultConfig + return &config + } + return receiver.NewFactory( cfg.typeStr, - createDefaultConfig, + componentDefaultsFunc, receiver.WithMetrics( createMetricsReceiver(cfg.initializer, cfg.configUnmarshaler), component.StabilityLevelAlpha, From 452bd6ccfb1fa3c9796c5982b25fc06abcfb0fed Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Thu, 30 Oct 2025 09:02:57 -0300 Subject: [PATCH 6/7] Validate exporter config based on go tags Signed-off-by: Arthur Silva Sens --- go.mod | 1 + go.sum | 2 ++ otlpreceiver/factory.go | 33 +++++++++++++++++++++------------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 411055b9..bb07e326 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/alecthomas/kingpin/v2 v2.4.0 github.com/coreos/go-systemd/v22 v22.6.0 github.com/mdlayher/vsock v1.2.1 + github.com/mitchellh/mapstructure v1.5.0 github.com/prometheus/common v0.66.1 go.opentelemetry.io/collector/component v1.44.0 go.opentelemetry.io/collector/consumer v1.44.0 diff --git a/go.sum b/go.sum index 2accd935..57ebd16c 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/otlpreceiver/factory.go b/otlpreceiver/factory.go index b62e1205..136e538e 100644 --- a/otlpreceiver/factory.go +++ b/otlpreceiver/factory.go @@ -17,6 +17,7 @@ import ( "context" "fmt" + "github.com/mitchellh/mapstructure" "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer" @@ -30,21 +31,21 @@ type ExporterInitializer interface { // containing all the metrics collectors. Initialize(ctx context.Context, exporterConfig Config) (*prometheus.Registry, error) - // Shutdown cleanly stops the exporter and releases resources. + // Shutdown is used to release resources when the receiver is shutting down. Shutdown(ctx context.Context) error } -// ConfigUnmarshaler is the interface for unmarshaling exporter-specific configuration. +// ConfigUnmarshaler is the interface used to unmarshal the exporter-specific +// configuration using mapstructure and struct tags. type ConfigUnmarshaler interface { - // UnmarshalExporterConfig parses the exporter-specific configuration - // from the raw map into a Config instance. - UnmarshalExporterConfig(data map[string]interface{}) (Config, error) + // GetConfigStruct returns a pointer to the config struct that mapstructure + // will populate. The struct should have appropriate mapstructure tags. + GetConfigStruct() Config } // FactoryOption is a function that configures a Factory. type FactoryOption func(*factoryConfig) -// factoryConfig holds the configuration for creating a receiver factory. type factoryConfig struct { typeStr component.Type initializer ExporterInitializer @@ -73,6 +74,7 @@ func WithConfigUnmarshaler(unmarshaler ConfigUnmarshaler) FactoryOption { } } +// WithComponentDefaults sets the default configuration for the component. func WithComponentDefaults(defaults map[string]interface{}) FactoryOption { return func(cfg *factoryConfig) { cfg.defaultConfig = defaults @@ -114,7 +116,6 @@ func NewFactory(opts ...FactoryOption) receiver.Factory { ) } -// createMetricsReceiver returns a function that creates a metrics receiver instance. func createMetricsReceiver( initializer ExporterInitializer, unmarshaler ConfigUnmarshaler, @@ -130,21 +131,29 @@ func createMetricsReceiver( return nil, fmt.Errorf("invalid config type: %T", cfg) } - // Unmarshal the exporter-specific config if len(receiverCfg.ExporterConfig) > 0 { - exporterCfg, err := unmarshaler.UnmarshalExporterConfig(receiverCfg.ExporterConfig) + exporterCfg := unmarshaler.GetConfigStruct() + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Result: exporterCfg, + ErrorUnused: true, // Reject unknown fields + WeaklyTypedInput: false, // Strict type checking + TagName: "mapstructure", + }) if err != nil { - return nil, fmt.Errorf("failed to unmarshal exporter config: %w", err) + return nil, fmt.Errorf("failed to create decoder: %w", err) } + + if err = decoder.Decode(receiverCfg.ExporterConfig); err != nil { + return nil, fmt.Errorf("configuration validation failed: %w", err) + } + receiverCfg.SetExporterConfig(exporterCfg) } - // Validate the complete configuration if err := receiverCfg.Validate(); err != nil { return nil, fmt.Errorf("invalid configuration: %w", err) } - // Create the receiver instance return newPrometheusReceiver( receiverCfg, consumer, From 351ffee9155f4156ec084278e634ef8e7570249f Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Thu, 30 Oct 2025 19:54:33 -0300 Subject: [PATCH 7/7] Rename interfaces, clarify docs Signed-off-by: Arthur Silva Sens --- otlpreceiver/config.go | 1 - otlpreceiver/doc.go | 33 +++++++++++++++++++++++---------- otlpreceiver/factory.go | 28 ++++++++++++++-------------- otlpreceiver/receiver.go | 32 ++++++++++++++++---------------- 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/otlpreceiver/config.go b/otlpreceiver/config.go index 601cbc66..69734bfb 100644 --- a/otlpreceiver/config.go +++ b/otlpreceiver/config.go @@ -66,7 +66,6 @@ func (cfg *ReceiverConfig) GetExporterConfig() Config { return cfg.exporterConfigInstance } -// createDefaultConfig returns a default ReceiverConfig with sensible defaults. func createDefaultConfig() ReceiverConfig { return ReceiverConfig{ ScrapeInterval: 30 * time.Second, diff --git a/otlpreceiver/doc.go b/otlpreceiver/doc.go index bf9431c2..7e339a75 100644 --- a/otlpreceiver/doc.go +++ b/otlpreceiver/doc.go @@ -22,7 +22,7 @@ // The otlpreceiver package provides the core infrastructure for converting // Prometheus exporters into OTel receivers: // -// 1. Config system for exporter-specific configuration +// 1. Config system for exporter-specific configuration with automatic validation // 2. Factory pattern for creating receiver instances // 3. Lifecycle management (Start/Shutdown) // 4. Periodic metric scraping from Prometheus registries @@ -32,26 +32,32 @@ // // To integrate a Prometheus exporter, implement two interfaces: // -// ExporterInitializer: Manages the exporter lifecycle +// ExporterLifecycleManager: Manages the exporter lifecycle // -// type MyExporterInitializer struct { +// type MyExporterLifecycleManager struct { // // exporter state // } // -// func (i *MyExporterInitializer) Initialize(ctx context.Context, cfg Config) (*prometheus.Registry, error) { -// // Initialize your exporter and return its registry +// func (i *MyExporterLifecycleManager) Start(ctx context.Context, cfg Config) (*prometheus.Registry, error) { +// // Start your exporter and return its registry // } // -// func (i *MyExporterInitializer) Shutdown(ctx context.Context) error { +// func (i *MyExporterLifecycleManager) Shutdown(ctx context.Context) error { // // Clean up resources // } // -// ConfigUnmarshaler: Handles exporter-specific configuration +// ConfigUnmarshaler: Handles exporter-specific configuration using mapstructure +// +// type MyConfig struct { +// EnableFeature bool `mapstructure:"enable_feature"` +// Timeout string `mapstructure:"timeout"` +// Items []string `mapstructure:"items"` +// } // // type MyConfigUnmarshaler struct{} // -// func (u *MyConfigUnmarshaler) UnmarshalExporterConfig(data map[string]interface{}) (Config, error) { -// // Parse your exporter's configuration +// func (u *MyConfigUnmarshaler) GetConfigStruct() Config { +// return &MyConfig{} // } // // Then create a receiver factory: @@ -70,7 +76,14 @@ // prometheus/myexporter: // scrape_interval: 30s // exporter_config: -// # Your exporter-specific configuration here +// enable_feature: true +// timeout: "30s" +// items: ["item1", "item2"] +// +// The framework automatically validates configuration using mapstructure tags: +// - Unknown fields are rejected +// - Type mismatches are caught (e.g., string where bool expected) +// - Custom validation can be added via the Config.Validate() method // // # Architecture // diff --git a/otlpreceiver/factory.go b/otlpreceiver/factory.go index 136e538e..f0f7ddda 100644 --- a/otlpreceiver/factory.go +++ b/otlpreceiver/factory.go @@ -24,12 +24,12 @@ import ( "go.opentelemetry.io/collector/receiver" ) -// ExporterInitializer is the interface that Prometheus exporters must implement +// ExporterLifecycleManager is the interface that Prometheus exporters must implement // to be embedded in the OTel Collector. -type ExporterInitializer interface { - // Initialize sets up the exporter and returns a prometheus.Registry +type ExporterLifecycleManager interface { + // Start sets up the exporter and returns a prometheus.Registry // containing all the metrics collectors. - Initialize(ctx context.Context, exporterConfig Config) (*prometheus.Registry, error) + Start(ctx context.Context, exporterConfig Config) (*prometheus.Registry, error) // Shutdown is used to release resources when the receiver is shutting down. Shutdown(ctx context.Context) error @@ -48,7 +48,7 @@ type FactoryOption func(*factoryConfig) type factoryConfig struct { typeStr component.Type - initializer ExporterInitializer + lifecycleManager ExporterLifecycleManager configUnmarshaler ConfigUnmarshaler defaultConfig map[string]interface{} } @@ -61,9 +61,9 @@ func WithType(typeStr component.Type) FactoryOption { } // WithInitializer sets the exporter initializer. -func WithInitializer(initializer ExporterInitializer) FactoryOption { +func WithLifecycleManager(lifecycleManager ExporterLifecycleManager) FactoryOption { return func(cfg *factoryConfig) { - cfg.initializer = initializer + cfg.lifecycleManager = lifecycleManager } } @@ -93,7 +93,7 @@ func NewFactory(opts ...FactoryOption) receiver.Factory { if cfg.typeStr.String() == "" { panic("receiver type must be specified") } - if cfg.initializer == nil { + if cfg.lifecycleManager == nil { panic("exporter initializer must be specified") } if cfg.configUnmarshaler == nil { @@ -101,23 +101,23 @@ func NewFactory(opts ...FactoryOption) receiver.Factory { } componentDefaultsFunc := func() component.Config { - config := createDefaultConfig() - config.ExporterConfig = cfg.defaultConfig - return &config + receiverConfig := createDefaultConfig() + receiverConfig.ExporterConfig = cfg.defaultConfig + return &receiverConfig } return receiver.NewFactory( cfg.typeStr, componentDefaultsFunc, receiver.WithMetrics( - createMetricsReceiver(cfg.initializer, cfg.configUnmarshaler), + createMetricsReceiver(cfg.lifecycleManager, cfg.configUnmarshaler), component.StabilityLevelAlpha, ), ) } func createMetricsReceiver( - initializer ExporterInitializer, + lifecycleManager ExporterLifecycleManager, unmarshaler ConfigUnmarshaler, ) receiver.CreateMetricsFunc { return func( @@ -158,7 +158,7 @@ func createMetricsReceiver( receiverCfg, consumer, set, - initializer, + lifecycleManager, ), nil } } diff --git a/otlpreceiver/receiver.go b/otlpreceiver/receiver.go index df4ef01e..6d36d176 100644 --- a/otlpreceiver/receiver.go +++ b/otlpreceiver/receiver.go @@ -26,11 +26,11 @@ import ( // prometheusReceiver implements the receiver.Metrics interface for Prometheus exporters. type prometheusReceiver struct { - config *ReceiverConfig - consumer consumer.Metrics - settings receiver.Settings - initializer ExporterInitializer - scraper *scraper + config *ReceiverConfig + consumer consumer.Metrics + settings receiver.Settings + lifecycleManager ExporterLifecycleManager + scraper *scraper registry *prometheus.Registry cancel context.CancelFunc @@ -42,14 +42,14 @@ func newPrometheusReceiver( config *ReceiverConfig, consumer consumer.Metrics, settings receiver.Settings, - initializer ExporterInitializer, + lifecycleManager ExporterLifecycleManager, ) *prometheusReceiver { return &prometheusReceiver{ - config: config, - consumer: consumer, - settings: settings, - initializer: initializer, - done: make(chan struct{}), + config: config, + consumer: consumer, + settings: settings, + lifecycleManager: lifecycleManager, + done: make(chan struct{}), } } @@ -58,11 +58,11 @@ func newPrometheusReceiver( func (r *prometheusReceiver) Start(ctx context.Context, host component.Host) error { r.settings.Logger.Info("Starting Prometheus exporter receiver") - // Initialize the exporter + // Start the exporter exporterConfig := r.config.GetExporterConfig() - registry, err := r.initializer.Initialize(ctx, exporterConfig) + registry, err := r.lifecycleManager.Start(ctx, exporterConfig) if err != nil { - return fmt.Errorf("failed to initialize exporter: %w", err) + return fmt.Errorf("failed to start exporter: %w", err) } r.registry = registry @@ -101,8 +101,8 @@ func (r *prometheusReceiver) Shutdown(ctx context.Context) error { } // Shutdown the exporter - if r.initializer != nil { - if err := r.initializer.Shutdown(ctx); err != nil { + if r.lifecycleManager != nil { + if err := r.lifecycleManager.Shutdown(ctx); err != nil { r.settings.Logger.Error("Failed to shutdown exporter") return fmt.Errorf("failed to shutdown exporter: %w", err) }