diff --git a/Makefile b/Makefile index a89ad8e23f4..277d60ae1ef 100644 --- a/Makefile +++ b/Makefile @@ -258,6 +258,7 @@ check-contrib: @$(MAKE) -C $(CONTRIB_PATH) for-all CMD="$(GOCMD) mod edit \ -replace go.opentelemetry.io/collector=$(CURDIR) \ -replace go.opentelemetry.io/collector/component=$(CURDIR)/component \ + -replace go.opentelemetry.io/collector/component/componentstatus=$(CURDIR)/component/componentstatus \ -replace go.opentelemetry.io/collector/config/configauth=$(CURDIR)/config/configauth \ -replace go.opentelemetry.io/collector/config/configcompression=$(CURDIR)/config/configcompression \ -replace go.opentelemetry.io/collector/config/configgrpc=$(CURDIR)/config/configgrpc \ @@ -319,6 +320,7 @@ restore-contrib: @$(MAKE) -C $(CONTRIB_PATH) for-all CMD="$(GOCMD) mod edit \ -dropreplace go.opentelemetry.io/collector \ -dropreplace go.opentelemetry.io/collector/component \ + -dropreplace go.opentelemetry.io/collector/component/componentstatus \ -dropreplace go.opentelemetry.io/collector/config/configauth \ -dropreplace go.opentelemetry.io/collector/config/configcompression \ -dropreplace go.opentelemetry.io/collector/config/configgrpc \ diff --git a/cmd/builder/internal/builder/main_test.go b/cmd/builder/internal/builder/main_test.go index 44e1b37513e..7b3718dd665 100644 --- a/cmd/builder/internal/builder/main_test.go +++ b/cmd/builder/internal/builder/main_test.go @@ -41,6 +41,7 @@ var ( replaceModules = []string{ "", "/component", + "/component/componentstatus", "/config/configauth", "/config/configcompression", "/config/configgrpc", diff --git a/cmd/builder/test/core.builder.yaml b/cmd/builder/test/core.builder.yaml index 1b2ba855916..9a6d98844fd 100644 --- a/cmd/builder/test/core.builder.yaml +++ b/cmd/builder/test/core.builder.yaml @@ -20,6 +20,7 @@ exporters: replaces: - go.opentelemetry.io/collector => ${WORKSPACE_DIR} - go.opentelemetry.io/collector/component => ${WORKSPACE_DIR}/component + - go.opentelemetry.io/collector/component/componentstatus => ${WORKSPACE_DIR}/component/componentstatus - go.opentelemetry.io/collector/config/configauth => ${WORKSPACE_DIR}/config/configauth - go.opentelemetry.io/collector/config/configcompression => ${WORKSPACE_DIR}/config/configcompression - go.opentelemetry.io/collector/config/configgrpc => ${WORKSPACE_DIR}/config/configgrpc diff --git a/cmd/mdatagen/go.mod b/cmd/mdatagen/go.mod index f52462b4e55..816ce20f3cf 100644 --- a/cmd/mdatagen/go.mod +++ b/cmd/mdatagen/go.mod @@ -100,3 +100,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/cmd/otelcorecol/builder-config.yaml b/cmd/otelcorecol/builder-config.yaml index e63f908086e..66a97af5f2e 100644 --- a/cmd/otelcorecol/builder-config.yaml +++ b/cmd/otelcorecol/builder-config.yaml @@ -44,6 +44,7 @@ replaces: - go.opentelemetry.io/collector/internal/globalgates => ../../internal/globalgates - go.opentelemetry.io/collector/otelcol => ../../otelcol - go.opentelemetry.io/collector/component => ../../component + - go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus - go.opentelemetry.io/collector/config/configauth => ../../config/configauth - go.opentelemetry.io/collector/config/configcompression => ../../config/configcompression - go.opentelemetry.io/collector/config/configgrpc => ../../config/configgrpc diff --git a/cmd/otelcorecol/go.mod b/cmd/otelcorecol/go.mod index 9eec0fe1d67..77042832ff1 100644 --- a/cmd/otelcorecol/go.mod +++ b/cmd/otelcorecol/go.mod @@ -81,6 +81,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector v0.105.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configauth v0.105.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.12.0 // indirect go.opentelemetry.io/collector/config/configgrpc v0.105.0 // indirect @@ -145,6 +146,8 @@ replace go.opentelemetry.io/collector/otelcol => ../../otelcol replace go.opentelemetry.io/collector/component => ../../component +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus + replace go.opentelemetry.io/collector/config/configauth => ../../config/configauth replace go.opentelemetry.io/collector/config/configcompression => ../../config/configcompression diff --git a/component/componentstatus/Makefile b/component/componentstatus/Makefile new file mode 100644 index 00000000000..ded7a36092d --- /dev/null +++ b/component/componentstatus/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/component/componentstatus/go.mod b/component/componentstatus/go.mod new file mode 100644 index 00000000000..e0e89445b45 --- /dev/null +++ b/component/componentstatus/go.mod @@ -0,0 +1,36 @@ +module go.opentelemetry.io/collector/component/componentstatus + +go 1.21.0 + +toolchain go1.21.12 + +require ( + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/component v0.105.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect + go.opentelemetry.io/collector/pdata v1.12.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.opentelemetry.io/collector/pdata => ../../pdata + +replace go.opentelemetry.io/collector/config/configtelemetry => ../../config/configtelemetry + +replace go.opentelemetry.io/collector/component => ../ diff --git a/component/componentstatus/go.sum b/component/componentstatus/go.sum new file mode 100644 index 00000000000..147b5cbbcda --- /dev/null +++ b/component/componentstatus/go.sum @@ -0,0 +1,78 @@ +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.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +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= +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.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +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= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/component/status.go b/component/componentstatus/status.go similarity index 92% rename from component/status.go rename to component/componentstatus/status.go index 60894a217ca..54fb1fe3744 100644 --- a/component/status.go +++ b/component/componentstatus/status.go @@ -1,12 +1,20 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package component // import "go.opentelemetry.io/collector/component" +package componentstatus // import "go.opentelemetry.io/collector/component/componentstatus" import ( "time" ) +type StatusReporter interface { + // ReportStatus allows a component to report runtime changes in status. The service + // will automatically report status for a component during startup and shutdown. Components can + // use this method to report status after start and before shutdown. For more details about + // component status reporting see: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-status.md + ReportStatus(*StatusEvent) +} + type Status int32 // Enumeration of possible component statuses diff --git a/component/status_test.go b/component/componentstatus/status_test.go similarity index 87% rename from component/status_test.go rename to component/componentstatus/status_test.go index 13755d078a5..0b1e014f4ad 100644 --- a/component/status_test.go +++ b/component/componentstatus/status_test.go @@ -1,6 +1,6 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package component +package componentstatus import ( "fmt" @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/component" ) func TestNewStatusEvent(t *testing.T) { @@ -52,12 +54,12 @@ func TestStatusEventsWithError(t *testing.T) { func TestAggregateStatus(t *testing.T) { for _, tc := range []struct { name string - statusMap map[*InstanceID]*StatusEvent + statusMap map[*component.InstanceID]*StatusEvent expectedStatus Status }{ { name: "aggregate status with fatal is FatalError", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusFatalError), @@ -67,7 +69,7 @@ func TestAggregateStatus(t *testing.T) { }, { name: "aggregate status with permanent is PermanentError", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusPermanentError), @@ -77,7 +79,7 @@ func TestAggregateStatus(t *testing.T) { }, { name: "aggregate status with stopping is Stopping", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusRecoverableError), @@ -87,7 +89,7 @@ func TestAggregateStatus(t *testing.T) { }, { name: "aggregate status with stopped and non-stopped is Stopping", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusRecoverableError), @@ -97,7 +99,7 @@ func TestAggregateStatus(t *testing.T) { }, { name: "aggregate status with all stopped is Stopped", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStopped), {}: NewStatusEvent(StatusStopped), {}: NewStatusEvent(StatusStopped), @@ -106,7 +108,7 @@ func TestAggregateStatus(t *testing.T) { }, { name: "aggregate status with recoverable is RecoverableError", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusRecoverableError), @@ -115,7 +117,7 @@ func TestAggregateStatus(t *testing.T) { }, { name: "aggregate status with starting is Starting", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), }, @@ -123,7 +125,7 @@ func TestAggregateStatus(t *testing.T) { }, { name: "aggregate status with all ok is OK", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusOK), @@ -189,12 +191,12 @@ func TestAggregateStatusEvent(t *testing.T) { for _, tc := range []struct { name string - statusMap map[*InstanceID]*StatusEvent + statusMap map[*component.InstanceID]*StatusEvent expectedStatus *StatusEvent }{ { name: "FatalError - existing event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: latest(NewFatalErrorEvent(assert.AnError)), @@ -208,7 +210,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "FatalError - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewFatalErrorEvent(assert.AnError), @@ -222,7 +224,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "PermanentError - existing event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: latest(NewPermanentErrorEvent(assert.AnError)), @@ -236,7 +238,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "PermanentError - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewPermanentErrorEvent(assert.AnError), @@ -250,7 +252,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "Stopping - existing event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusRecoverableError), @@ -263,7 +265,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "Stopping - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: NewStatusEvent(StatusRecoverableError), @@ -276,7 +278,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "Stopped - existing event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStopped), {}: latest(NewStatusEvent(StatusStopped)), {}: NewStatusEvent(StatusStopped), @@ -288,7 +290,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "RecoverableError - existing event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: NewStatusEvent(StatusOK), {}: latest(NewRecoverableErrorEvent(assert.AnError)), @@ -301,7 +303,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "Starting - synthetic event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusStarting), {}: latest(NewStatusEvent(StatusOK)), }, @@ -312,7 +314,7 @@ func TestAggregateStatusEvent(t *testing.T) { }, { name: "OK - existing event", - statusMap: map[*InstanceID]*StatusEvent{ + statusMap: map[*component.InstanceID]*StatusEvent{ {}: NewStatusEvent(StatusOK), {}: latest(NewStatusEvent(StatusOK)), {}: NewStatusEvent(StatusOK), diff --git a/component/componenttest/nop_telemetry.go b/component/componenttest/nop_telemetry.go index 171e9daa47e..0324f65c980 100644 --- a/component/componenttest/nop_telemetry.go +++ b/component/componenttest/nop_telemetry.go @@ -21,7 +21,5 @@ func NewNopTelemetrySettings() component.TelemetrySettings { MeterProvider: noopmetric.NewMeterProvider(), MetricsLevel: configtelemetry.LevelNone, Resource: pcommon.NewResource(), - ReportStatus: func(*component.StatusEvent) { - }, } } diff --git a/component/telemetry.go b/component/telemetry.go index 6f8f0ec84d3..dce1e62928d 100644 --- a/component/telemetry.go +++ b/component/telemetry.go @@ -34,10 +34,4 @@ type TelemetrySettings struct { // Resource contains the resource attributes for the collector's telemetry. Resource pcommon.Resource - - // ReportStatus allows a component to report runtime changes in status. The service - // will automatically report status for a component during startup and shutdown. Components can - // use this method to report status after start and before shutdown. For more details about - // component status reporting see: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-status.md - ReportStatus func(*StatusEvent) } diff --git a/config/configauth/go.mod b/config/configauth/go.mod index e03d9096b8c..6d6b19fb195 100644 --- a/config/configauth/go.mod +++ b/config/configauth/go.mod @@ -21,6 +21,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/confmap v0.105.0 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect @@ -46,6 +47,8 @@ replace go.opentelemetry.io/collector/confmap => ../../confmap replace go.opentelemetry.io/collector/component => ../../component +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus + replace go.opentelemetry.io/collector/config/configtelemetry => ../configtelemetry replace go.opentelemetry.io/collector/extension => ../../extension diff --git a/config/configgrpc/go.mod b/config/configgrpc/go.mod index 99db60086ae..df6a8a91afa 100644 --- a/config/configgrpc/go.mod +++ b/config/configgrpc/go.mod @@ -52,6 +52,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/confmap v0.105.0 // indirect go.opentelemetry.io/collector/extension v0.105.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.105.0 // indirect @@ -109,3 +110,5 @@ replace go.opentelemetry.io/collector/consumer => ../../consumer replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/config/confighttp/go.mod b/config/confighttp/go.mod index ca5d8ac8b63..be2837d595d 100644 --- a/config/confighttp/go.mod +++ b/config/confighttp/go.mod @@ -47,6 +47,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/confmap v0.105.0 // indirect go.opentelemetry.io/collector/extension v0.105.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.105.0 // indirect @@ -102,3 +103,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/config/internal/go.mod b/config/internal/go.mod index 757d6fff8ff..a085ed7f85f 100644 --- a/config/internal/go.mod +++ b/config/internal/go.mod @@ -41,3 +41,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/connector/forwardconnector/go.mod b/connector/forwardconnector/go.mod index 70286d54f11..4436d3c9669 100644 --- a/connector/forwardconnector/go.mod +++ b/connector/forwardconnector/go.mod @@ -90,3 +90,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/connector/go.mod b/connector/go.mod index eedaf4307b5..8e36c81d6ee 100644 --- a/connector/go.mod +++ b/connector/go.mod @@ -73,3 +73,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../internal/global replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/exporter/debugexporter/go.mod b/exporter/debugexporter/go.mod index 165281a2318..5908af807b1 100644 --- a/exporter/debugexporter/go.mod +++ b/exporter/debugexporter/go.mod @@ -41,6 +41,7 @@ require ( github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect go.opentelemetry.io/collector v0.105.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configretry v1.12.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.105.0 // indirect go.opentelemetry.io/collector/consumer/consumertest v0.105.0 // indirect @@ -96,3 +97,5 @@ replace go.opentelemetry.io/collector/config/configretry => ../../config/configr replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/go.mod b/exporter/go.mod index 8858ef764e6..d1b577ba0f3 100644 --- a/exporter/go.mod +++ b/exporter/go.mod @@ -51,6 +51,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/confmap v0.105.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.105.0 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect @@ -95,3 +96,5 @@ replace go.opentelemetry.io/collector/config/configtelemetry => ../config/config replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/exporter/loggingexporter/go.mod b/exporter/loggingexporter/go.mod index 9b7444e4a0d..d9f8fa52857 100644 --- a/exporter/loggingexporter/go.mod +++ b/exporter/loggingexporter/go.mod @@ -40,6 +40,7 @@ require ( github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect go.opentelemetry.io/collector v0.105.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configretry v1.12.0 // indirect go.opentelemetry.io/collector/consumer v0.105.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.105.0 // indirect @@ -101,3 +102,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/nopexporter/go.mod b/exporter/nopexporter/go.mod index a8d37c00208..1035eb7831e 100644 --- a/exporter/nopexporter/go.mod +++ b/exporter/nopexporter/go.mod @@ -91,3 +91,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/otlpexporter/go.mod b/exporter/otlpexporter/go.mod index 191e4bb061d..9efb47e5b07 100644 --- a/exporter/otlpexporter/go.mod +++ b/exporter/otlpexporter/go.mod @@ -54,6 +54,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/confignet v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/config/internal v0.105.0 // indirect @@ -144,3 +145,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/exporter/otlphttpexporter/go.mod b/exporter/otlphttpexporter/go.mod index e6604d26407..1a10f534287 100644 --- a/exporter/otlphttpexporter/go.mod +++ b/exporter/otlphttpexporter/go.mod @@ -53,6 +53,7 @@ require ( github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rs/cors v1.11.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configauth v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/config/internal v0.105.0 // indirect @@ -141,3 +142,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/auth/go.mod b/extension/auth/go.mod index 036b8577921..76816d9b486 100644 --- a/extension/auth/go.mod +++ b/extension/auth/go.mod @@ -31,6 +31,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/confmap v0.105.0 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect @@ -54,6 +55,8 @@ require ( replace go.opentelemetry.io/collector/component => ../../component +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus + replace go.opentelemetry.io/collector/confmap => ../../confmap replace go.opentelemetry.io/collector/extension => ../ diff --git a/extension/ballastextension/go.mod b/extension/ballastextension/go.mod index 054f7cd7c10..fdc2d52dcef 100644 --- a/extension/ballastextension/go.mod +++ b/extension/ballastextension/go.mod @@ -41,6 +41,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.105.0 // indirect @@ -91,3 +92,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/extension.go b/extension/extension.go index aaa235d5ae0..023666845fe 100644 --- a/extension/extension.go +++ b/extension/extension.go @@ -8,6 +8,7 @@ import ( "fmt" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/confmap" ) @@ -59,7 +60,7 @@ type StatusWatcher interface { // Extensions that implement this interface must be ready that the ComponentStatusChanged // may be called before, after or concurrently with calls to Component.Start() and Component.Shutdown(). // The function may be called concurrently with itself. - ComponentStatusChanged(source *component.InstanceID, event *component.StatusEvent) + ComponentStatusChanged(source *component.InstanceID, event *componentstatus.StatusEvent) } // Settings is passed to Factory.Create(...) function. diff --git a/extension/extensiontest/statuswatcher_extension.go b/extension/extensiontest/statuswatcher_extension.go index a4d78f9033e..d85721a7d3b 100644 --- a/extension/extensiontest/statuswatcher_extension.go +++ b/extension/extensiontest/statuswatcher_extension.go @@ -7,6 +7,7 @@ import ( "context" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/extension" ) @@ -21,7 +22,7 @@ func NewStatusWatcherExtensionCreateSettings() extension.Settings { // NewStatusWatcherExtensionFactory returns a component.ExtensionFactory to construct a status watcher extension. func NewStatusWatcherExtensionFactory( - onStatusChanged func(source *component.InstanceID, event *component.StatusEvent), + onStatusChanged func(source *component.InstanceID, event *componentstatus.StatusEvent), ) extension.Factory { return extension.NewFactory( component.MustNewType("statuswatcher"), @@ -39,9 +40,9 @@ func NewStatusWatcherExtensionFactory( type statusWatcherExtension struct { component.StartFunc component.ShutdownFunc - onStatusChanged func(source *component.InstanceID, event *component.StatusEvent) + onStatusChanged func(source *component.InstanceID, event *componentstatus.StatusEvent) } -func (e statusWatcherExtension) ComponentStatusChanged(source *component.InstanceID, event *component.StatusEvent) { +func (e statusWatcherExtension) ComponentStatusChanged(source *component.InstanceID, event *componentstatus.StatusEvent) { e.onStatusChanged(source, event) } diff --git a/extension/extensiontest/statuswatcher_extension_test.go b/extension/extensiontest/statuswatcher_extension_test.go index 14f9859e354..ae4ab488a83 100644 --- a/extension/extensiontest/statuswatcher_extension_test.go +++ b/extension/extensiontest/statuswatcher_extension_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/extension" ) @@ -18,7 +19,7 @@ import ( func TestStatusWatcherExtension(t *testing.T) { statusChanged := false factory := NewStatusWatcherExtensionFactory( - func(*component.InstanceID, *component.StatusEvent) { + func(*component.InstanceID, *componentstatus.StatusEvent) { statusChanged = true }, ) @@ -32,7 +33,7 @@ func TestStatusWatcherExtension(t *testing.T) { assert.NoError(t, ext.Start(context.Background(), componenttest.NewNopHost())) assert.False(t, statusChanged) - ext.(extension.StatusWatcher).ComponentStatusChanged(&component.InstanceID{}, &component.StatusEvent{}) + ext.(extension.StatusWatcher).ComponentStatusChanged(&component.InstanceID{}, &componentstatus.StatusEvent{}) assert.True(t, statusChanged) assert.NoError(t, ext.Shutdown(context.Background())) diff --git a/extension/go.mod b/extension/go.mod index 3c7db6c786e..640d701ad67 100644 --- a/extension/go.mod +++ b/extension/go.mod @@ -6,6 +6,7 @@ require ( github.com/google/uuid v1.6.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/component/componentstatus v0.105.0 go.opentelemetry.io/collector/confmap v0.105.0 go.uber.org/goleak v1.3.0 ) @@ -53,6 +54,8 @@ require ( replace go.opentelemetry.io/collector/component => ../component +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus + replace go.opentelemetry.io/collector/confmap => ../confmap replace go.opentelemetry.io/collector/pdata => ../pdata diff --git a/extension/memorylimiterextension/go.mod b/extension/memorylimiterextension/go.mod index 09baf6c125f..716b20385c7 100644 --- a/extension/memorylimiterextension/go.mod +++ b/extension/memorylimiterextension/go.mod @@ -40,6 +40,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.105.0 // indirect @@ -85,3 +86,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/zpagesextension/go.mod b/extension/zpagesextension/go.mod index 08d18b0c05e..e0abca06e94 100644 --- a/extension/zpagesextension/go.mod +++ b/extension/zpagesextension/go.mod @@ -6,6 +6,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.105.0 go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/component/componentstatus v0.105.0 go.opentelemetry.io/collector/config/configauth v0.105.0 go.opentelemetry.io/collector/config/confighttp v0.105.0 go.opentelemetry.io/collector/confmap v0.105.0 @@ -126,3 +127,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/extension/zpagesextension/zpagesextension.go b/extension/zpagesextension/zpagesextension.go index 16bb6c6635d..4827728a6d5 100644 --- a/extension/zpagesextension/zpagesextension.go +++ b/extension/zpagesextension/zpagesextension.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) const ( @@ -83,7 +84,10 @@ func (zpe *zpagesExtension) Start(ctx context.Context, host component.Host) erro defer close(zpe.stopCh) if errHTTP := zpe.server.Serve(ln); errHTTP != nil && !errors.Is(errHTTP, http.ErrServerClosed) { - zpe.telemetry.ReportStatus(component.NewFatalErrorEvent(errHTTP)) + statusReporter, ok := host.(componentstatus.StatusReporter) + if ok { + statusReporter.ReportStatus(componentstatus.NewFatalErrorEvent(errHTTP)) + } } }() diff --git a/go.mod b/go.mod index 00fd2e54769..e8e3dcbed20 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/shirou/gopsutil/v4 v4.24.6 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/component/componentstatus v0.105.0 go.opentelemetry.io/collector/confmap v0.105.0 go.opentelemetry.io/collector/consumer v0.105.0 go.opentelemetry.io/collector/consumer/consumertest v0.105.0 @@ -91,6 +92,8 @@ require ( replace go.opentelemetry.io/collector/component => ./component +replace go.opentelemetry.io/collector/component/componentstatus => ./component/componentstatus + replace go.opentelemetry.io/collector/confmap => ./confmap replace go.opentelemetry.io/collector/config/configtelemetry => ./config/configtelemetry diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod index b16094c5fc1..73ce80fff69 100644 --- a/internal/e2e/go.mod +++ b/internal/e2e/go.mod @@ -56,6 +56,7 @@ require ( github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rs/cors v1.11.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configauth v0.105.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.12.0 // indirect go.opentelemetry.io/collector/config/confignet v0.105.0 // indirect @@ -154,3 +155,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../globalgates replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/internal/sharedcomponent/sharedcomponent.go b/internal/sharedcomponent/sharedcomponent.go index 1a3e65878c2..5b209570cd8 100644 --- a/internal/sharedcomponent/sharedcomponent.go +++ b/internal/sharedcomponent/sharedcomponent.go @@ -8,9 +8,11 @@ package sharedcomponent // import "go.opentelemetry.io/collector/internal/shared import ( "context" + "slices" "sync" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) func NewMap[K comparable, V component.Component]() *Map[K, V] { @@ -31,17 +33,6 @@ func (m *Map[K, V]) LoadOrStore(key K, create func() (V, error), telemetrySettin m.lock.Lock() defer m.lock.Unlock() if c, ok := m.components[key]; ok { - // If we haven't already seen this telemetry settings, this shared component represents - // another instance. Wrap ReportStatus to report for all instances this shared - // component represents. - if _, ok := c.seenSettings[telemetrySettings]; !ok { - c.seenSettings[telemetrySettings] = struct{}{} - prev := c.telemetry.ReportStatus - c.telemetry.ReportStatus = func(ev *component.StatusEvent) { - telemetrySettings.ReportStatus(ev) - prev(ev) - } - } return c, nil } comp, err := create() @@ -57,9 +48,6 @@ func (m *Map[K, V]) LoadOrStore(key K, create func() (V, error), telemetrySettin delete(m.components, key) }, telemetry: telemetrySettings, - seenSettings: map[*component.TelemetrySettings]struct{}{ - telemetrySettings: {}, - }, } m.components[key] = newComp return newComp, nil @@ -74,8 +62,9 @@ type Component[V component.Component] struct { stopOnce sync.Once removeFunc func() - telemetry *component.TelemetrySettings - seenSettings map[*component.TelemetrySettings]struct{} + telemetry *component.TelemetrySettings + + hostWrapper *hostWrapper } // Unwrap returns the original component. @@ -85,20 +74,72 @@ func (c *Component[V]) Unwrap() V { // Start starts the underlying component if it never started before. func (c *Component[V]) Start(ctx context.Context, host component.Host) error { + if c.hostWrapper == nil { + c.hostWrapper = &hostWrapper{ + host: host, + sources: make([]componentstatus.StatusReporter, 0), + previousEvents: make([]*componentstatus.StatusEvent, 0), + } + } + + statusReporter, isStatusReporter := host.(componentstatus.StatusReporter) + if isStatusReporter { + c.hostWrapper.addSource(statusReporter) + } + var err error c.startOnce.Do(func() { // It's important that status for a shared component is reported through its // telemetry settings to keep status in sync and avoid race conditions. This logic duplicates // and takes priority over the automated status reporting that happens in graph, making the // status reporting in graph a no-op. - c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStarting)) - if err = c.component.Start(ctx, host); err != nil { - c.telemetry.ReportStatus(component.NewPermanentErrorEvent(err)) + if c.hostWrapper != nil { + c.hostWrapper.ReportStatus(componentstatus.NewStatusEvent(componentstatus.StatusStarting)) + } + if err = c.component.Start(ctx, c.hostWrapper); err != nil { + if c.hostWrapper != nil { + c.hostWrapper.ReportStatus(componentstatus.NewPermanentErrorEvent(err)) + } } }) + return err } +var _ component.Host = (*hostWrapper)(nil) +var _ componentstatus.StatusReporter = (*hostWrapper)(nil) + +type hostWrapper struct { + host component.Host + sources []componentstatus.StatusReporter + previousEvents []*componentstatus.StatusEvent +} + +func (h *hostWrapper) GetFactory(kind component.Kind, componentType component.Type) component.Factory { + return h.host.GetFactory(kind, componentType) +} + +func (h *hostWrapper) GetExtensions() map[component.ID]component.Component { + return h.host.GetExtensions() +} + +func (h *hostWrapper) ReportStatus(e *componentstatus.StatusEvent) { + if !slices.Contains(h.previousEvents, e) { + h.previousEvents = append(h.previousEvents, e) + } + + for _, s := range h.sources { + s.ReportStatus(e) + } +} + +func (h *hostWrapper) addSource(s componentstatus.StatusReporter) { + for _, e := range h.previousEvents { + s.ReportStatus(e) + } + h.sources = append(h.sources, s) +} + // Shutdown shuts down the underlying component. func (c *Component[V]) Shutdown(ctx context.Context) error { var err error @@ -107,12 +148,16 @@ func (c *Component[V]) Shutdown(ctx context.Context) error { // telemetry settings to keep status in sync and avoid race conditions. This logic duplicates // and takes priority over the automated status reporting that happens in graph, making the // status reporting in graph a no-op. - c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopping)) + if c.hostWrapper != nil { + c.hostWrapper.ReportStatus(componentstatus.NewStatusEvent(componentstatus.StatusStopping)) + } err = c.component.Shutdown(ctx) - if err != nil { - c.telemetry.ReportStatus(component.NewPermanentErrorEvent(err)) - } else { - c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopped)) + if c.hostWrapper != nil { + if err != nil { + c.hostWrapper.ReportStatus(componentstatus.NewPermanentErrorEvent(err)) + } else { + c.hostWrapper.ReportStatus(componentstatus.NewStatusEvent(componentstatus.StatusStopped)) + } } c.removeFunc() }) diff --git a/internal/sharedcomponent/sharedcomponent_test.go b/internal/sharedcomponent/sharedcomponent_test.go index 77cda5124db..09bd6df4f47 100644 --- a/internal/sharedcomponent/sharedcomponent_test.go +++ b/internal/sharedcomponent/sharedcomponent_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" ) @@ -107,123 +108,53 @@ func TestSharedComponent(t *testing.T) { assert.NoError(t, got.Shutdown(context.Background())) assert.Equal(t, 1, calledStop) } -func TestSharedComponentsReportStatus(t *testing.T) { - reportedStatuses := make(map[*component.InstanceID][]component.Status) - newStatusFunc := func() func(*component.StatusEvent) { - instanceID := &component.InstanceID{} - return func(ev *component.StatusEvent) { - if ev.Status() == component.StatusNone { - return - } - reportedStatuses[instanceID] = append(reportedStatuses[instanceID], ev.Status()) - } - } - - comp := &baseComponent{} - comps := NewMap[component.ID, *baseComponent]() - var telemetrySettings *component.TelemetrySettings - - // make a shared component that represents three instances - for i := 0; i < 3; i++ { - telemetrySettings = newNopTelemetrySettings() - telemetrySettings.ReportStatus = newStatusFunc() - // The initial settings for the shared component need to match the ones passed to the first - // invocation of LoadOrStore so that underlying telemetry settings reference can be used to - // wrap ReportStatus for subsequently added "instances". - if i == 0 { - comp.telemetry = telemetrySettings - } - got, err := comps.LoadOrStore( - id, - func() (*baseComponent, error) { return comp, nil }, - telemetrySettings, - ) - require.NoError(t, err) - assert.Len(t, comps.components, 1) - assert.Same(t, comp, got.Unwrap()) - } - - // make sure we don't try to represent a fourth instance if we reuse a telemetrySettings - _, _ = comps.LoadOrStore( - id, - func() (*baseComponent, error) { return comp, nil }, - telemetrySettings, - ) - - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStarting)) - - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusOK)) - - // simulate an error - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusNone)) - - // stopping - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopping)) - - // stopped - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopped)) - - // The shared component represents 3 component instances. Reporting status for the shared - // component should report status for each of the instances it represents. - expectedStatuses := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, - } - - require.Equal(t, 3, len(reportedStatuses)) - - for _, actualStatuses := range reportedStatuses { - require.Equal(t, expectedStatuses, actualStatuses) - } -} func TestReportStatusOnStartShutdown(t *testing.T) { for _, tc := range []struct { - name string - startErr error - shutdownErr error - expectedStatuses []component.Status + name string + startErr error + shutdownErr error + expectedStatuses []componentstatus.Status + expectedNumReporterInstances int }{ { name: "successful start/stop", startErr: nil, shutdownErr: nil, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, + expectedNumReporterInstances: 3, }, { name: "start error", startErr: assert.AnError, shutdownErr: nil, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusPermanentError, }, + expectedNumReporterInstances: 1, }, { name: "shutdown error", shutdownErr: assert.AnError, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusPermanentError, }, + expectedNumReporterInstances: 3, }, } { t.Run(tc.name, func(t *testing.T) { - reportedStatuses := make(map[*component.InstanceID][]component.Status) - newStatusFunc := func() func(*component.StatusEvent) { - instanceID := &component.InstanceID{} - return func(ev *component.StatusEvent) { - reportedStatuses[instanceID] = append(reportedStatuses[instanceID], ev.Status()) - } + reportedStatuses := make(map[*component.InstanceID][]componentstatus.Status) + newStatusFunc := func(id *component.InstanceID, ev *componentstatus.StatusEvent) { + reportedStatuses[id] = append(reportedStatuses[id], ev.Status()) } base := &baseComponent{} if tc.startErr != nil { @@ -241,7 +172,6 @@ func TestReportStatusOnStartShutdown(t *testing.T) { var err error for i := 0; i < 3; i++ { telemetrySettings := newNopTelemetrySettings() - telemetrySettings.ReportStatus = newStatusFunc() if i == 0 { base.telemetry = telemetrySettings } @@ -253,17 +183,24 @@ func TestReportStatusOnStartShutdown(t *testing.T) { require.NoError(t, err) } - err = comp.Start(context.Background(), componenttest.NewNopHost()) + baseHost := componenttest.NewNopHost() + for i := 0; i < 3; i++ { + err = comp.Start(context.Background(), &testHost{Host: baseHost, InstanceID: &component.InstanceID{}, newStatusFunc: newStatusFunc}) + if err != nil { + break + } + } + require.Equal(t, tc.startErr, err) if tc.startErr == nil { - comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusOK)) + comp.hostWrapper.ReportStatus(componentstatus.NewStatusEvent(componentstatus.StatusOK)) err = comp.Shutdown(context.Background()) require.Equal(t, tc.shutdownErr, err) } - require.Equal(t, 3, len(reportedStatuses)) + require.Equal(t, tc.expectedNumReporterInstances, len(reportedStatuses)) for _, actualStatuses := range reportedStatuses { require.Equal(t, tc.expectedStatuses, actualStatuses) @@ -277,3 +214,16 @@ func newNopTelemetrySettings() *component.TelemetrySettings { set := componenttest.NewNopTelemetrySettings() return &set } + +var _ component.Host = (*testHost)(nil) +var _ componentstatus.StatusReporter = (*testHost)(nil) + +type testHost struct { + component.Host + *component.InstanceID + newStatusFunc func(id *component.InstanceID, ev *componentstatus.StatusEvent) +} + +func (h *testHost) ReportStatus(e *componentstatus.StatusEvent) { + h.newStatusFunc(h.InstanceID, e) +} diff --git a/otelcol/collector_test.go b/otelcol/collector_test.go index a2dc4083f5f..f538de55321 100644 --- a/otelcol/collector_test.go +++ b/otelcol/collector_test.go @@ -20,6 +20,7 @@ import ( "gopkg.in/yaml.v3" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension/extensiontest" "go.opentelemetry.io/collector/processor/processortest" @@ -145,9 +146,9 @@ func TestComponentStatusWatcher(t *testing.T) { factories.Processors[unhealthyProcessorFactory.Type()] = unhealthyProcessorFactory // Keep track of all status changes in a map. - changedComponents := map[*component.InstanceID][]component.Status{} + changedComponents := map[*component.InstanceID][]componentstatus.Status{} var mux sync.Mutex - onStatusChanged := func(source *component.InstanceID, event *component.StatusEvent) { + onStatusChanged := func(source *component.InstanceID, event *componentstatus.StatusEvent) { if source.ID.Type() != unhealthyProcessorFactory.Type() { return } @@ -174,17 +175,17 @@ func TestComponentStatusWatcher(t *testing.T) { // An unhealthy processor asynchronously reports a recoverable error. Depending on the Go // Scheduler the statuses reported at startup will be one of the two valid sequnces below. - startupStatuses1 := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, + startupStatuses1 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, } - startupStatuses2 := []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, + startupStatuses2 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, } // the modulus of the actual statuses will match the modulus of the startup statuses - startupStatuses := func(actualStatuses []component.Status) []component.Status { + startupStatuses := func(actualStatuses []componentstatus.Status) []componentstatus.Status { if len(actualStatuses)%2 == 1 { return startupStatuses1 } @@ -216,8 +217,8 @@ func TestComponentStatusWatcher(t *testing.T) { // Check for additional statuses after Shutdown. for _, v := range changedComponents { - expectedStatuses := append([]component.Status{}, startupStatuses(v)...) - expectedStatuses = append(expectedStatuses, component.StatusStopping, component.StatusStopped) + expectedStatuses := append([]componentstatus.Status{}, startupStatuses(v)...) + expectedStatuses = append(expectedStatuses, componentstatus.StatusStopping, componentstatus.StatusStopped) assert.Equal(t, expectedStatuses, v) } diff --git a/otelcol/go.mod b/otelcol/go.mod index 0699ca0b342..ef033b219c4 100644 --- a/otelcol/go.mod +++ b/otelcol/go.mod @@ -6,6 +6,7 @@ require ( github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/component/componentstatus v0.105.0 go.opentelemetry.io/collector/config/configtelemetry v0.105.0 go.opentelemetry.io/collector/confmap v0.105.0 go.opentelemetry.io/collector/connector v0.105.0 @@ -154,3 +155,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../internal/global replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/otelcol/otelcoltest/go.mod b/otelcol/otelcoltest/go.mod index 49b19a92393..70874e37c3e 100644 --- a/otelcol/otelcoltest/go.mod +++ b/otelcol/otelcoltest/go.mod @@ -61,6 +61,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector v0.105.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/consumer v0.105.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.105.0 // indirect @@ -172,3 +173,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/processor/batchprocessor/go.mod b/processor/batchprocessor/go.mod index 32b1d86db1b..748a3b1aa63 100644 --- a/processor/batchprocessor/go.mod +++ b/processor/batchprocessor/go.mod @@ -45,6 +45,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.105.0 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.105.0 // indirect @@ -91,3 +92,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/processor/go.mod b/processor/go.mod index f0af3fb6f1b..24410d43fad 100644 --- a/processor/go.mod +++ b/processor/go.mod @@ -7,6 +7,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.105.0 go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/component/componentstatus v0.105.0 go.opentelemetry.io/collector/config/configtelemetry v0.105.0 go.opentelemetry.io/collector/consumer v0.105.0 go.opentelemetry.io/collector/consumer/consumertest v0.105.0 @@ -73,3 +74,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../internal/global replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/processor/memorylimiterprocessor/go.mod b/processor/memorylimiterprocessor/go.mod index 17860bcb612..106e76bf80a 100644 --- a/processor/memorylimiterprocessor/go.mod +++ b/processor/memorylimiterprocessor/go.mod @@ -45,6 +45,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.105.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.105.0 // indirect go.opentelemetry.io/collector/consumer/consumerprofiles v0.105.0 // indirect go.opentelemetry.io/collector/featuregate v1.12.0 // indirect @@ -98,3 +99,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/processor/processortest/unhealthy_processor.go b/processor/processortest/unhealthy_processor.go index c537ab53498..b3dd467ea3d 100644 --- a/processor/processortest/unhealthy_processor.go +++ b/processor/processortest/unhealthy_processor.go @@ -7,6 +7,7 @@ import ( "context" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/consumer/consumertest" @@ -62,9 +63,12 @@ type unhealthyProcessor struct { telemetry component.TelemetrySettings } -func (p unhealthyProcessor) Start(context.Context, component.Host) error { +func (p unhealthyProcessor) Start(_ context.Context, host component.Host) error { go func() { - p.telemetry.ReportStatus(component.NewStatusEvent(component.StatusRecoverableError)) + statusReporter, isStatusReporter := host.(componentstatus.StatusReporter) + if isStatusReporter { + statusReporter.ReportStatus(componentstatus.NewStatusEvent(componentstatus.StatusRecoverableError)) + } }() return nil } diff --git a/receiver/go.mod b/receiver/go.mod index abe1f937512..db1b85bf994 100644 --- a/receiver/go.mod +++ b/receiver/go.mod @@ -74,3 +74,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../internal/global replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/receiver/nopreceiver/go.mod b/receiver/nopreceiver/go.mod index dc2f668029c..b2c8bfd3865 100644 --- a/receiver/nopreceiver/go.mod +++ b/receiver/nopreceiver/go.mod @@ -84,3 +84,5 @@ replace go.opentelemetry.io/collector/pdata/pprofile => ../../pdata/pprofile replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/receiver/otlpreceiver/go.mod b/receiver/otlpreceiver/go.mod index 86e85179259..e8c8e1ca49b 100644 --- a/receiver/otlpreceiver/go.mod +++ b/receiver/otlpreceiver/go.mod @@ -8,6 +8,7 @@ require ( github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector v0.105.0 go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/component/componentstatus v0.105.0 go.opentelemetry.io/collector/config/configgrpc v0.105.0 go.opentelemetry.io/collector/config/confighttp v0.105.0 go.opentelemetry.io/collector/config/confignet v0.105.0 @@ -144,3 +145,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../../internal/glo replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus diff --git a/receiver/otlpreceiver/otlp.go b/receiver/otlpreceiver/otlp.go index 95d782b880e..ac59b2981a8 100644 --- a/receiver/otlpreceiver/otlp.go +++ b/receiver/otlpreceiver/otlp.go @@ -14,6 +14,7 @@ import ( "google.golang.org/grpc" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/pdata/plog/plogotlp" @@ -110,7 +111,10 @@ func (r *otlpReceiver) startGRPCServer(host component.Host) error { defer r.shutdownWG.Done() if errGrpc := r.serverGRPC.Serve(gln); errGrpc != nil && !errors.Is(errGrpc, grpc.ErrServerStopped) { - r.settings.ReportStatus(component.NewFatalErrorEvent(errGrpc)) + statusReporter, ok := host.(componentstatus.StatusReporter) + if ok { + statusReporter.ReportStatus(componentstatus.NewFatalErrorEvent(errGrpc)) + } } }() return nil @@ -160,7 +164,10 @@ func (r *otlpReceiver) startHTTPServer(ctx context.Context, host component.Host) defer r.shutdownWG.Done() if errHTTP := r.serverHTTP.Serve(hln); errHTTP != nil && !errors.Is(errHTTP, http.ErrServerClosed) { - r.settings.ReportStatus(component.NewFatalErrorEvent(errHTTP)) + statusReporter, ok := host.(componentstatus.StatusReporter) + if ok { + statusReporter.ReportStatus(componentstatus.NewFatalErrorEvent(errHTTP)) + } } }() return nil diff --git a/service/extensions/extensions.go b/service/extensions/extensions.go index 1ef73b08f1e..5265b414e97 100644 --- a/service/extensions/extensions.go +++ b/service/extensions/extensions.go @@ -13,10 +13,11 @@ import ( "go.uber.org/zap" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension" "go.opentelemetry.io/collector/service/internal/components" - "go.opentelemetry.io/collector/service/internal/servicetelemetry" + "go.opentelemetry.io/collector/service/internal/status" "go.opentelemetry.io/collector/service/internal/zpages" ) @@ -24,10 +25,12 @@ const zExtensionName = "zextensionname" // Extensions is a map of extensions created from extension configs. type Extensions struct { - telemetry servicetelemetry.TelemetrySettings + telemetry component.TelemetrySettings extMap map[component.ID]extension.Extension instanceIDs map[component.ID]*component.InstanceID extensionIDs []component.ID // start order (and reverse stop order) + + reporter status.Reporter } // Start starts all extensions. @@ -38,20 +41,29 @@ func (bes *Extensions) Start(ctx context.Context, host component.Host) error { extLogger.Info("Extension is starting...") instanceID := bes.instanceIDs[extID] ext := bes.extMap[extID] - bes.telemetry.Status.ReportStatus( - instanceID, - component.NewStatusEvent(component.StatusStarting), - ) - if err := ext.Start(ctx, host); err != nil { - bes.telemetry.Status.ReportStatus( + + if bes.reporter != nil { + bes.reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(err), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), ) + } + if err := ext.Start(ctx, host); err != nil { + if bes.reporter != nil { + bes.reporter.ReportStatus( + instanceID, + componentstatus.NewPermanentErrorEvent(err), + ) + } // We log with zap.AddStacktrace(zap.DPanicLevel) to avoid adding the stack trace to the error log extLogger.WithOptions(zap.AddStacktrace(zap.DPanicLevel)).Error("Failed to start extension", zap.Error(err)) return err } - bes.telemetry.Status.ReportOKIfStarting(instanceID) + + if bes.reporter != nil { + bes.reporter.ReportOKIfStarting(instanceID) + } + extLogger.Info("Extension started.") } return nil @@ -65,22 +77,30 @@ func (bes *Extensions) Shutdown(ctx context.Context) error { extID := bes.extensionIDs[i] instanceID := bes.instanceIDs[extID] ext := bes.extMap[extID] - bes.telemetry.Status.ReportStatus( - instanceID, - component.NewStatusEvent(component.StatusStopping), - ) - if err := ext.Shutdown(ctx); err != nil { - bes.telemetry.Status.ReportStatus( + + if bes.reporter != nil { + bes.reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(err), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), ) + } + + if err := ext.Shutdown(ctx); err != nil { + if bes.reporter != nil { + bes.reporter.ReportStatus( + instanceID, + componentstatus.NewPermanentErrorEvent(err), + ) + } errs = multierr.Append(errs, err) continue } - bes.telemetry.Status.ReportStatus( - instanceID, - component.NewStatusEvent(component.StatusStopped), - ) + if bes.reporter != nil { + bes.reporter.ReportStatus( + instanceID, + componentstatus.NewStatusEvent(componentstatus.StatusStopped), + ) + } } return errs @@ -121,7 +141,7 @@ func (bes *Extensions) NotifyConfig(ctx context.Context, conf *confmap.Conf) err return errs } -func (bes *Extensions) NotifyComponentStatusChange(source *component.InstanceID, event *component.StatusEvent) { +func (bes *Extensions) NotifyComponentStatusChange(source *component.InstanceID, event *componentstatus.StatusEvent) { for _, extID := range bes.extensionIDs { ext := bes.extMap[extID] if sw, ok := ext.(extension.StatusWatcher); ok { @@ -166,21 +186,34 @@ func (bes *Extensions) HandleZPages(w http.ResponseWriter, r *http.Request) { // Settings holds configuration for building Extensions. type Settings struct { - Telemetry servicetelemetry.TelemetrySettings + Telemetry component.TelemetrySettings BuildInfo component.BuildInfo // Extensions builder for extensions. Extensions *extension.Builder } +type Option func(*Extensions) + +func WithReporter(reporter status.Reporter) Option { + return func(e *Extensions) { + e.reporter = reporter + } +} + // New creates a new Extensions from Config. -func New(ctx context.Context, set Settings, cfg Config) (*Extensions, error) { +func New(ctx context.Context, set Settings, cfg Config, options ...Option) (*Extensions, error) { exts := &Extensions{ telemetry: set.Telemetry, extMap: make(map[component.ID]extension.Extension), instanceIDs: make(map[component.ID]*component.InstanceID), extensionIDs: make([]component.ID, 0, len(cfg)), } + + for _, opt := range options { + opt(exts) + } + for _, extID := range cfg { instanceID := &component.InstanceID{ ID: extID, @@ -188,7 +221,7 @@ func New(ctx context.Context, set Settings, cfg Config) (*Extensions, error) { } extSet := extension.Settings{ ID: extID, - TelemetrySettings: set.Telemetry.ToComponentTelemetrySettings(instanceID), + TelemetrySettings: set.Telemetry, BuildInfo: set.BuildInfo, } extSet.TelemetrySettings.Logger = components.ExtensionLogger(set.Telemetry.Logger, extID) diff --git a/service/extensions/extensions_test.go b/service/extensions/extensions_test.go index d519132d3f3..2d4dd0b6d45 100644 --- a/service/extensions/extensions_test.go +++ b/service/extensions/extensions_test.go @@ -12,12 +12,13 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/extension" "go.opentelemetry.io/collector/extension/extensiontest" - "go.opentelemetry.io/collector/service/internal/servicetelemetry" "go.opentelemetry.io/collector/service/internal/status" + "go.opentelemetry.io/collector/service/internal/testcomponents" ) func TestBuildExtensions(t *testing.T) { @@ -83,7 +84,7 @@ func TestBuildExtensions(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := New(context.Background(), Settings{ - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: testcomponents.NewNopTelemetrySettings(), BuildInfo: component.NewDefaultBuildInfo(), Extensions: extension.NewBuilder(tt.extensionsConfigs, tt.factories), }, tt.config) @@ -175,7 +176,7 @@ func (tc testOrderCase) testOrdering(t *testing.T) { } exts, err := New(context.Background(), Settings{ - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: testcomponents.NewNopTelemetrySettings(), BuildInfo: component.NewDefaultBuildInfo(), Extensions: extension.NewBuilder( extCfgs, @@ -284,7 +285,7 @@ func TestNotifyConfig(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { extensions, err := New(context.Background(), Settings{ - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: testcomponents.NewNopTelemetrySettings(), BuildInfo: component.NewDefaultBuildInfo(), Extensions: extension.NewBuilder(tt.extensionsConfigs, tt.factories), }, tt.serviceExtensions) @@ -361,7 +362,7 @@ func newCreateErrorExtensionFactory() extension.Factory { func TestStatusReportedOnStartupShutdown(t *testing.T) { // compare two slices of status events ignoring timestamp - assertEqualStatuses := func(t *testing.T, evts1, evts2 []*component.StatusEvent) { + assertEqualStatuses := func(t *testing.T, evts1, evts2 []*componentstatus.StatusEvent) { assert.Equal(t, len(evts1), len(evts2)) for i := 0; i < len(evts1); i++ { ev1 := evts1[i] @@ -373,37 +374,37 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { for _, tc := range []struct { name string - expectedStatuses []*component.StatusEvent + expectedStatuses []*componentstatus.StatusEvent startErr error shutdownErr error }{ { name: "successful startup/shutdown", - expectedStatuses: []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + expectedStatuses: []*componentstatus.StatusEvent{ + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewStatusEvent(componentstatus.StatusStopped), }, startErr: nil, shutdownErr: nil, }, { name: "start error", - expectedStatuses: []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewPermanentErrorEvent(assert.AnError), + expectedStatuses: []*componentstatus.StatusEvent{ + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, startErr: assert.AnError, shutdownErr: nil, }, { name: "shutdown error", - expectedStatuses: []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewPermanentErrorEvent(assert.AnError), + expectedStatuses: []*componentstatus.StatusEvent{ + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, startErr: nil, shutdownErr: assert.AnError, @@ -420,25 +421,27 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { factories := map[component.Type]extension.Factory{ statusType: factory, } + + var actualStatuses []*componentstatus.StatusEvent + rep := status.NewReporter(func(_ *component.InstanceID, ev *componentstatus.StatusEvent) { + actualStatuses = append(actualStatuses, ev) + }, func(err error) { + require.NoError(t, err) + }) + extensions, err := New( context.Background(), Settings{ - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: testcomponents.NewNopTelemetrySettings(), BuildInfo: component.NewDefaultBuildInfo(), Extensions: extension.NewBuilder(extensionsConfigs, factories), }, []component.ID{compID}, + WithReporter(rep), ) assert.NoError(t, err) - var actualStatuses []*component.StatusEvent - rep := status.NewReporter(func(_ *component.InstanceID, ev *component.StatusEvent) { - actualStatuses = append(actualStatuses, ev) - }, func(err error) { - require.NoError(t, err) - }) - extensions.telemetry.Status = rep rep.Ready() assert.Equal(t, tc.startErr, extensions.Start(context.Background(), componenttest.NewNopHost())) diff --git a/service/go.mod b/service/go.mod index 1ff29ae6e66..b2c3ecfc10e 100644 --- a/service/go.mod +++ b/service/go.mod @@ -12,6 +12,7 @@ require ( go.opencensus.io v0.24.0 go.opentelemetry.io/collector v0.105.0 go.opentelemetry.io/collector/component v0.105.0 + go.opentelemetry.io/collector/component/componentstatus v0.105.0 go.opentelemetry.io/collector/config/confighttp v0.105.0 go.opentelemetry.io/collector/config/configtelemetry v0.105.0 go.opentelemetry.io/collector/confmap v0.105.0 @@ -162,3 +163,5 @@ replace go.opentelemetry.io/collector/internal/globalgates => ../internal/global replace go.opentelemetry.io/collector/consumer/consumerprofiles => ../consumer/consumerprofiles replace go.opentelemetry.io/collector/consumer/consumertest => ../consumer/consumertest + +replace go.opentelemetry.io/collector/component/componentstatus => ../component/componentstatus diff --git a/service/host.go b/service/host.go deleted file mode 100644 index ce8dc530d40..00000000000 --- a/service/host.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package service // import "go.opentelemetry.io/collector/service" - -import ( - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/connector" - "go.opentelemetry.io/collector/exporter" - "go.opentelemetry.io/collector/extension" - "go.opentelemetry.io/collector/processor" - "go.opentelemetry.io/collector/receiver" - "go.opentelemetry.io/collector/service/extensions" - "go.opentelemetry.io/collector/service/internal/graph" -) - -// TODO: remove as part of https://github.com/open-telemetry/opentelemetry-collector/issues/7370 for service 1.0 -type getExporters interface { - GetExporters() map[component.DataType]map[component.ID]component.Component -} - -var _ getExporters = (*serviceHost)(nil) -var _ component.Host = (*serviceHost)(nil) - -type serviceHost struct { - asyncErrorChannel chan error - receivers *receiver.Builder - processors *processor.Builder - exporters *exporter.Builder - connectors *connector.Builder - extensions *extension.Builder - - buildInfo component.BuildInfo - - pipelines *graph.Graph - serviceExtensions *extensions.Extensions -} - -func (host *serviceHost) GetFactory(kind component.Kind, componentType component.Type) component.Factory { - switch kind { - case component.KindReceiver: - return host.receivers.Factory(componentType) - case component.KindProcessor: - return host.processors.Factory(componentType) - case component.KindExporter: - return host.exporters.Factory(componentType) - case component.KindConnector: - return host.connectors.Factory(componentType) - case component.KindExtension: - return host.extensions.Factory(componentType) - } - return nil -} - -func (host *serviceHost) GetExtensions() map[component.ID]component.Component { - return host.serviceExtensions.GetExtensions() -} - -// Deprecated: [0.79.0] This function will be removed in the future. -// Several components in the contrib repository use this function so it cannot be removed -// before those cases are removed. In most cases, use of this function can be replaced by a -// connector. See https://github.com/open-telemetry/opentelemetry-collector/issues/7370 and -// https://github.com/open-telemetry/opentelemetry-collector/pull/7390#issuecomment-1483710184 -// for additional information. -func (host *serviceHost) GetExporters() map[component.DataType]map[component.ID]component.Component { - return host.pipelines.GetExporters() -} - -func (host *serviceHost) notifyComponentStatusChange(source *component.InstanceID, event *component.StatusEvent) { - host.serviceExtensions.NotifyComponentStatusChange(source, event) - if event.Status() == component.StatusFatalError { - host.asyncErrorChannel <- event.Err() - } -} diff --git a/service/internal/graph/graph.go b/service/internal/graph/graph.go index a25a04167cd..b3c7dd447c4 100644 --- a/service/internal/graph/graph.go +++ b/service/internal/graph/graph.go @@ -16,7 +16,11 @@ import ( "context" "errors" "fmt" + "net/http" + "path" + "runtime" "strings" + "time" "go.uber.org/multierr" "go.uber.org/zap" @@ -25,21 +29,25 @@ import ( "gonum.org/v1/gonum/graph/topo" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/connector" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/extension" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/internal/fanoutconsumer" "go.opentelemetry.io/collector/processor" "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/service/extensions" "go.opentelemetry.io/collector/service/internal/capabilityconsumer" - "go.opentelemetry.io/collector/service/internal/servicetelemetry" "go.opentelemetry.io/collector/service/internal/status" + "go.opentelemetry.io/collector/service/internal/zpages" "go.opentelemetry.io/collector/service/pipelines" ) // Settings holds configuration for building builtPipelines. type Settings struct { - Telemetry servicetelemetry.TelemetrySettings + Telemetry component.TelemetrySettings BuildInfo component.BuildInfo ReceiverBuilder *receiver.Builder @@ -61,7 +69,7 @@ type Graph struct { // Keep track of status source per node instanceIDs map[int64]*component.InstanceID - telemetry servicetelemetry.TelemetrySettings + telemetry component.TelemetrySettings } // Build builds a full pipeline graph. @@ -298,22 +306,16 @@ func (g *Graph) buildComponents(ctx context.Context, set Settings) error { for i := len(nodes) - 1; i >= 0; i-- { node := nodes[i] - // skipped for capabilitiesNodes and fanoutNodes as they are not assigned componentIDs. - var telemetrySettings component.TelemetrySettings - if instanceID, ok := g.instanceIDs[node.ID()]; ok { - telemetrySettings = set.Telemetry.ToComponentTelemetrySettings(instanceID) - } - switch n := node.(type) { case *receiverNode: - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ReceiverBuilder, g.nextConsumers(n.ID())) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ReceiverBuilder, g.nextConsumers(n.ID())) case *processorNode: // nextConsumers is guaranteed to be length 1. Either it is the next processor or it is the fanout node for the exporters. - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ProcessorBuilder, g.nextConsumers(n.ID())[0]) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ProcessorBuilder, g.nextConsumers(n.ID())[0]) case *exporterNode: - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ExporterBuilder) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ExporterBuilder) case *connectorNode: - err = n.buildComponent(ctx, telemetrySettings, set.BuildInfo, set.ConnectorBuilder, g.nextConsumers(n.ID())) + err = n.buildComponent(ctx, set.Telemetry, set.BuildInfo, set.ConnectorBuilder, g.nextConsumers(n.ID())) case *capabilitiesNode: capability := consumer.Capabilities{ // The fanOutNode represents the aggregate capabilities of the exporters in the pipeline. @@ -396,7 +398,7 @@ type pipelineNodes struct { exporters map[int64]graph.Node } -func (g *Graph) StartAll(ctx context.Context, host component.Host, reporter status.Reporter) error { +func (g *Graph) StartAll(ctx context.Context, host *Host) error { nodes, err := topo.Sort(g.componentGraph) if err != nil { return err @@ -415,15 +417,15 @@ func (g *Graph) StartAll(ctx context.Context, host component.Host, reporter stat } instanceID := g.instanceIDs[node.ID()] - reporter.ReportStatus( + host.Reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), ) - if compErr := comp.Start(ctx, host); compErr != nil { - reporter.ReportStatus( + if compErr := comp.Start(ctx, &HostWrapper{Host: host, InstanceID: instanceID}); compErr != nil { + host.Reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(compErr), + componentstatus.NewPermanentErrorEvent(compErr), ) // We log with zap.AddStacktrace(zap.DPanicLevel) to avoid adding the stack trace to the error log g.telemetry.Logger.WithOptions(zap.AddStacktrace(zap.DPanicLevel)). @@ -435,7 +437,7 @@ func (g *Graph) StartAll(ctx context.Context, host component.Host, reporter stat return compErr } - reporter.ReportOKIfStarting(instanceID) + host.Reporter.ReportOKIfStarting(instanceID) } return nil } @@ -463,21 +465,21 @@ func (g *Graph) ShutdownAll(ctx context.Context, reporter status.Reporter) error instanceID := g.instanceIDs[node.ID()] reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStopping), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), ) if compErr := comp.Shutdown(ctx); compErr != nil { errs = multierr.Append(errs, compErr) reporter.ReportStatus( instanceID, - component.NewPermanentErrorEvent(compErr), + componentstatus.NewPermanentErrorEvent(compErr), ) continue } reporter.ReportStatus( instanceID, - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewStatusEvent(componentstatus.StatusStopped), ) } return errs @@ -578,3 +580,161 @@ func connectorStability(f connector.Factory, expType, recType component.Type) co } return component.StabilityLevelUndefined } + +// TODO: remove as part of https://github.com/open-telemetry/opentelemetry-collector/issues/7370 for service 1.0 +type getExporters interface { + GetExporters() map[component.DataType]map[component.ID]component.Component +} + +var _ getExporters = (*Host)(nil) +var _ component.Host = (*Host)(nil) + +type Host struct { + AsyncErrorChannel chan error + Receivers *receiver.Builder + Processors *processor.Builder + Exporters *exporter.Builder + Connectors *connector.Builder + Extensions *extension.Builder + + BuildInfo component.BuildInfo + + Pipelines *Graph + ServiceExtensions *extensions.Extensions + + Reporter status.Reporter +} + +func (h *Host) GetFactory(kind component.Kind, componentType component.Type) component.Factory { + switch kind { + case component.KindReceiver: + return h.Receivers.Factory(componentType) + case component.KindProcessor: + return h.Processors.Factory(componentType) + case component.KindExporter: + return h.Exporters.Factory(componentType) + case component.KindConnector: + return h.Connectors.Factory(componentType) + case component.KindExtension: + return h.Extensions.Factory(componentType) + } + return nil +} + +func (h *Host) GetExtensions() map[component.ID]component.Component { + return h.ServiceExtensions.GetExtensions() +} + +// Deprecated: [0.79.0] This function will be removed in the future. +// Several components in the contrib repository use this function so it cannot be removed +// before those cases are removed. In most cases, use of this function can be replaced by a +// connector. See https://github.com/open-telemetry/opentelemetry-collector/issues/7370 and +// https://github.com/open-telemetry/opentelemetry-collector/pull/7390#issuecomment-1483710184 +// for additional information. +func (h *Host) GetExporters() map[component.DataType]map[component.ID]component.Component { + return h.Pipelines.GetExporters() +} + +func (h *Host) NotifyComponentStatusChange(source *component.InstanceID, event *componentstatus.StatusEvent) { + h.ServiceExtensions.NotifyComponentStatusChange(source, event) + if event.Status() == componentstatus.StatusFatalError { + h.AsyncErrorChannel <- event.Err() + } +} + +var _ getExporters = (*HostWrapper)(nil) +var _ component.Host = (*HostWrapper)(nil) +var _ componentstatus.StatusReporter = (*HostWrapper)(nil) + +type HostWrapper struct { + *Host + InstanceID *component.InstanceID +} + +func (host *HostWrapper) ReportStatus(event *componentstatus.StatusEvent) { + host.Reporter.ReportStatus(host.InstanceID, event) +} + +const ( + // Paths + zServicePath = "servicez" + zPipelinePath = "pipelinez" + zExtensionPath = "extensionz" + zFeaturePath = "featurez" +) + +var ( + // InfoVar is a singleton instance of the Info struct. + runtimeInfoVar [][2]string +) + +func init() { + runtimeInfoVar = [][2]string{ + {"StartTimestamp", time.Now().String()}, + {"Go", runtime.Version()}, + {"OS", runtime.GOOS}, + {"Arch", runtime.GOARCH}, + // Add other valuable runtime information here. + } +} + +func handleFeaturezRequest(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Feature Gates"}) + zpages.WriteHTMLFeaturesTable(w, getFeaturesTableData()) + zpages.WriteHTMLPageFooter(w) +} + +func getFeaturesTableData() zpages.FeatureGateTableData { + data := zpages.FeatureGateTableData{} + featuregate.GlobalRegistry().VisitAll(func(gate *featuregate.Gate) { + data.Rows = append(data.Rows, zpages.FeatureGateTableRowData{ + ID: gate.ID(), + Enabled: gate.IsEnabled(), + Description: gate.Description(), + Stage: gate.Stage().String(), + FromVersion: gate.FromVersion(), + ToVersion: gate.ToVersion(), + ReferenceURL: gate.ReferenceURL(), + }) + }) + return data +} + +func getBuildInfoProperties(buildInfo component.BuildInfo) [][2]string { + return [][2]string{ + {"Command", buildInfo.Command}, + {"Description", buildInfo.Description}, + {"Version", buildInfo.Version}, + } +} + +func (h *Host) RegisterZPages(mux *http.ServeMux, pathPrefix string) { + mux.HandleFunc(path.Join(pathPrefix, zServicePath), h.zPagesRequest) + mux.HandleFunc(path.Join(pathPrefix, zPipelinePath), h.Pipelines.HandleZPages) + mux.HandleFunc(path.Join(pathPrefix, zExtensionPath), h.ServiceExtensions.HandleZPages) + mux.HandleFunc(path.Join(pathPrefix, zFeaturePath), handleFeaturezRequest) +} + +func (h *Host) zPagesRequest(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Service " + h.BuildInfo.Command}) + zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Build Info", Properties: getBuildInfoProperties(h.BuildInfo)}) + zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Runtime Info", Properties: runtimeInfoVar}) + zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ + Name: "Pipelines", + ComponentEndpoint: zPipelinePath, + Link: true, + }) + zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ + Name: "Extensions", + ComponentEndpoint: zExtensionPath, + Link: true, + }) + zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ + Name: "Features", + ComponentEndpoint: zFeaturePath, + Link: true, + }) + zpages.WriteHTMLPageFooter(w) +} diff --git a/service/internal/graph/graph_test.go b/service/internal/graph/graph_test.go index 6c1ffc0aab7..5452edf1fbb 100644 --- a/service/internal/graph/graph_test.go +++ b/service/internal/graph/graph_test.go @@ -16,6 +16,7 @@ import ( "gonum.org/v1/gonum/graph/simple" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/connector" "go.opentelemetry.io/collector/connector/connectortest" @@ -28,7 +29,6 @@ import ( "go.opentelemetry.io/collector/processor/processortest" "go.opentelemetry.io/collector/receiver" "go.opentelemetry.io/collector/receiver/receivertest" - "go.opentelemetry.io/collector/service/internal/servicetelemetry" "go.opentelemetry.io/collector/service/internal/status" "go.opentelemetry.io/collector/service/internal/status/statustest" "go.opentelemetry.io/collector/service/internal/testcomponents" @@ -145,7 +145,7 @@ func TestGraphStartStop(t *testing.T) { } pg := &Graph{componentGraph: simple.NewDirectedGraph()} - pg.telemetry = servicetelemetry.NewNopTelemetrySettings() + pg.telemetry = componenttest.NewNopTelemetrySettings() pg.instanceIDs = make(map[int64]*component.InstanceID) for _, edge := range tt.edges { @@ -155,7 +155,7 @@ func TestGraphStartStop(t *testing.T) { pg.componentGraph.SetEdge(simple.Edge{F: f, T: t}) } - require.NoError(t, pg.StartAll(ctx, componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + require.NoError(t, pg.StartAll(ctx, &Host{Reporter: status.NewReporter(func(*component.InstanceID, *componentstatus.StatusEvent) {}, func(error) {})})) for _, edge := range tt.edges { assert.Greater(t, ctx.order[edge[0]], ctx.order[edge[1]]) } @@ -189,7 +189,7 @@ func TestGraphStartStopCycle(t *testing.T) { pg.componentGraph.SetEdge(simple.Edge{F: c1, T: e1}) pg.componentGraph.SetEdge(simple.Edge{F: c1, T: p1}) // loop back - err := pg.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter()) + err := pg.StartAll(context.Background(), &Host{}) assert.Error(t, err) assert.Contains(t, err.Error(), `topo: no topological ordering: cyclic components`) @@ -200,7 +200,7 @@ func TestGraphStartStopCycle(t *testing.T) { func TestGraphStartStopComponentError(t *testing.T) { pg := &Graph{componentGraph: simple.NewDirectedGraph()} - pg.telemetry = servicetelemetry.NewNopTelemetrySettings() + pg.telemetry = componenttest.NewNopTelemetrySettings() r1 := &testNode{ id: component.MustNewIDWithName("r", "1"), startErr: errors.New("foo"), @@ -217,7 +217,7 @@ func TestGraphStartStopComponentError(t *testing.T) { F: r1, T: e1, }) - assert.EqualError(t, pg.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter()), "foo") + assert.EqualError(t, pg.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*component.InstanceID, *componentstatus.StatusEvent) {}, func(error) {})}), "foo") assert.EqualError(t, pg.ShutdownAll(context.Background(), statustest.NewNopStatusReporter()), "bar") } @@ -719,7 +719,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { t.Run(test.name, func(t *testing.T) { // Build the pipeline set := Settings{ - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: componenttest.NewNopTelemetrySettings(), BuildInfo: component.NewDefaultBuildInfo(), ReceiverBuilder: receiver.NewBuilder( map[component.ID]component.Config{ @@ -769,7 +769,7 @@ func TestConnectorPipelinesGraph(t *testing.T) { assert.Equal(t, len(test.pipelineConfigs), len(pg.pipelines)) - assert.NoError(t, pg.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.NoError(t, pg.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*component.InstanceID, *componentstatus.StatusEvent) {}, func(error) {})})) mutatingPipelines := make(map[component.ID]bool, len(test.pipelineConfigs)) @@ -1006,7 +1006,7 @@ func TestConnectorRouter(t *testing.T) { ctx := context.Background() set := Settings{ - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: componenttest.NewNopTelemetrySettings(), BuildInfo: component.NewDefaultBuildInfo(), ReceiverBuilder: receiver.NewBuilder( map[component.ID]component.Config{ @@ -2050,7 +2050,7 @@ func TestGraphBuildErrors(t *testing.T) { t.Run(test.name, func(t *testing.T) { set := Settings{ BuildInfo: component.NewDefaultBuildInfo(), - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: componenttest.NewNopTelemetrySettings(), ReceiverBuilder: receiver.NewBuilder( test.receiverCfgs, map[component.Type]receiver.Factory{ @@ -2097,7 +2097,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { nopConnectorFactory := connectortest.NewNopFactory() set := Settings{ - Telemetry: servicetelemetry.NewNopTelemetrySettings(), + Telemetry: componenttest.NewNopTelemetrySettings(), BuildInfo: component.NewDefaultBuildInfo(), ReceiverBuilder: receiver.NewBuilder( map[component.ID]component.Config{ @@ -2149,7 +2149,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*component.InstanceID, *componentstatus.StatusEvent) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) @@ -2163,7 +2163,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*component.InstanceID, *componentstatus.StatusEvent) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) @@ -2177,7 +2177,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*component.InstanceID, *componentstatus.StatusEvent) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) @@ -2197,7 +2197,7 @@ func TestGraphFailToStartAndShutdown(t *testing.T) { } pipelines, err := Build(context.Background(), set) assert.NoError(t, err) - assert.Error(t, pipelines.StartAll(context.Background(), componenttest.NewNopHost(), statustest.NewNopStatusReporter())) + assert.Error(t, pipelines.StartAll(context.Background(), &Host{Reporter: status.NewReporter(func(*component.InstanceID, *componentstatus.StatusEvent) {}, func(error) {})})) assert.Error(t, pipelines.ShutdownAll(context.Background(), statustest.NewNopStatusReporter())) }) } @@ -2224,7 +2224,7 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { } // compare two maps of status events ignoring timestamp - assertEqualStatuses := func(t *testing.T, evMap1, evMap2 map[*component.InstanceID][]*component.StatusEvent) { + assertEqualStatuses := func(t *testing.T, evMap1, evMap2 map[*component.InstanceID][]*componentstatus.StatusEvent) { assert.Equal(t, len(evMap1), len(evMap2)) for id, evts1 := range evMap1 { evts2 := evMap2[id] @@ -2242,35 +2242,35 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { for _, tc := range []struct { name string edge [2]*testNode - expectedStatuses map[*component.InstanceID][]*component.StatusEvent + expectedStatuses map[*component.InstanceID][]*componentstatus.StatusEvent startupErr error shutdownErr error }{ { name: "successful startup/shutdown", edge: [2]*testNode{rNoErr, eNoErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*component.InstanceID][]*componentstatus.StatusEvent{ instanceIDs[rNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewStatusEvent(componentstatus.StatusStopped), }, instanceIDs[eNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewStatusEvent(componentstatus.StatusStopped), }, }, }, { name: "early startup error", edge: [2]*testNode{rNoErr, eStErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*component.InstanceID][]*componentstatus.StatusEvent{ instanceIDs[eStErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, }, startupErr: assert.AnError, @@ -2278,16 +2278,16 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { { name: "late startup error", edge: [2]*testNode{rStErr, eNoErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*component.InstanceID][]*componentstatus.StatusEvent{ instanceIDs[rStErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, instanceIDs[eNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewStatusEvent(componentstatus.StatusStopped), }, }, startupErr: assert.AnError, @@ -2295,18 +2295,18 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { { name: "early shutdown error", edge: [2]*testNode{rSdErr, eNoErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*component.InstanceID][]*componentstatus.StatusEvent{ instanceIDs[rSdErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, instanceIDs[eNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewStatusEvent(componentstatus.StatusStopped), }, }, shutdownErr: assert.AnError, @@ -2314,18 +2314,18 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { { name: "late shutdown error", edge: [2]*testNode{rNoErr, eSdErr}, - expectedStatuses: map[*component.InstanceID][]*component.StatusEvent{ + expectedStatuses: map[*component.InstanceID][]*componentstatus.StatusEvent{ instanceIDs[rNoErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewStatusEvent(component.StatusStopped), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewStatusEvent(componentstatus.StatusStopped), }, instanceIDs[eSdErr]: { - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusStopping), - component.NewPermanentErrorEvent(assert.AnError), + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusStopping), + componentstatus.NewPermanentErrorEvent(assert.AnError), }, }, shutdownErr: assert.AnError, @@ -2333,15 +2333,14 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { pg := &Graph{componentGraph: simple.NewDirectedGraph()} - pg.telemetry = servicetelemetry.NewNopTelemetrySettings() + pg.telemetry = componenttest.NewNopTelemetrySettings() - actualStatuses := make(map[*component.InstanceID][]*component.StatusEvent) - rep := status.NewReporter(func(id *component.InstanceID, ev *component.StatusEvent) { + actualStatuses := make(map[*component.InstanceID][]*componentstatus.StatusEvent) + rep := status.NewReporter(func(id *component.InstanceID, ev *componentstatus.StatusEvent) { actualStatuses[id] = append(actualStatuses[id], ev) }, func(error) { }) - pg.telemetry.Status = rep rep.Ready() e0, e1 := tc.edge[0], tc.edge[1] @@ -2351,7 +2350,7 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) { } pg.componentGraph.SetEdge(simple.Edge{F: e0, T: e1}) - assert.Equal(t, tc.startupErr, pg.StartAll(context.Background(), componenttest.NewNopHost(), rep)) + assert.Equal(t, tc.startupErr, pg.StartAll(context.Background(), &Host{Reporter: rep})) assert.Equal(t, tc.shutdownErr, pg.ShutdownAll(context.Background(), rep)) assertEqualStatuses(t, tc.expectedStatuses, actualStatuses) }) diff --git a/service/internal/proctelemetry/process_telemetry.go b/service/internal/proctelemetry/process_telemetry.go index e7a0cc1454a..0fe58d5d694 100644 --- a/service/internal/proctelemetry/process_telemetry.go +++ b/service/internal/proctelemetry/process_telemetry.go @@ -15,7 +15,6 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/service/internal/metadata" - "go.opentelemetry.io/collector/service/internal/servicetelemetry" ) // processMetrics is a struct that contains views related to process metrics (cpu, mem, etc) @@ -53,7 +52,7 @@ func WithHostProc(hostProc string) RegisterOption { // RegisterProcessMetrics creates a new set of processMetrics (mem, cpu) that can be used to measure // basic information about this process. -func RegisterProcessMetrics(cfg servicetelemetry.TelemetrySettings, opts ...RegisterOption) error { +func RegisterProcessMetrics(cfg component.TelemetrySettings, opts ...RegisterOption) error { set := registerOption{} for _, opt := range opts { opt.apply(&set) @@ -74,7 +73,7 @@ func RegisterProcessMetrics(cfg servicetelemetry.TelemetrySettings, opts ...Regi return err } - _, err = metadata.NewTelemetryBuilder(cfg.ToComponentTelemetrySettings(&component.InstanceID{}), + _, err = metadata.NewTelemetryBuilder(cfg, metadata.WithProcessUptimeCallback(pm.updateProcessUptime), metadata.WithProcessRuntimeHeapAllocBytesCallback(pm.updateAllocMem), metadata.WithProcessRuntimeTotalAllocBytesCallback(pm.updateTotalAllocMem), diff --git a/service/internal/proctelemetry/process_telemetry_test.go b/service/internal/proctelemetry/process_telemetry_test.go index fb750fcf664..3c4fb629529 100644 --- a/service/internal/proctelemetry/process_telemetry_test.go +++ b/service/internal/proctelemetry/process_telemetry_test.go @@ -17,15 +17,19 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" otelprom "go.opentelemetry.io/otel/exporters/prometheus" + noopmetric "go.opentelemetry.io/otel/metric/noop" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" + nooptrace "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configtelemetry" - "go.opentelemetry.io/collector/service/internal/servicetelemetry" + "go.opentelemetry.io/collector/pdata/pcommon" ) type testTelemetry struct { - servicetelemetry.TelemetrySettings + component.TelemetrySettings promHandler http.Handler meterProvider *sdkmetric.MeterProvider } @@ -41,7 +45,13 @@ var expectedMetrics = []string{ func setupTelemetry(t *testing.T) testTelemetry { settings := testTelemetry{ - TelemetrySettings: servicetelemetry.NewNopTelemetrySettings(), + TelemetrySettings: component.TelemetrySettings{ + Logger: zap.NewNop(), + TracerProvider: nooptrace.NewTracerProvider(), + MeterProvider: noopmetric.NewMeterProvider(), + MetricsLevel: configtelemetry.LevelNone, + Resource: pcommon.NewResource(), + }, } settings.TelemetrySettings.MetricsLevel = configtelemetry.LevelNormal diff --git a/service/internal/servicetelemetry/nop_telemetry_settings_test.go b/service/internal/servicetelemetry/nop_telemetry_settings_test.go deleted file mode 100644 index 8c4a401e0f0..00000000000 --- a/service/internal/servicetelemetry/nop_telemetry_settings_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package servicetelemetry - -import ( - "testing" - - "github.com/stretchr/testify/require" - noopmetric "go.opentelemetry.io/otel/metric/noop" - nooptrace "go.opentelemetry.io/otel/trace/noop" - "go.uber.org/zap" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configtelemetry" - "go.opentelemetry.io/collector/pdata/pcommon" -) - -func TestNewNopSettings(t *testing.T) { - set := NewNopTelemetrySettings() - set.Status.Ready() - require.NotNil(t, set) - require.IsType(t, TelemetrySettings{}, set) - require.Equal(t, zap.NewNop(), set.Logger) - require.Equal(t, nooptrace.NewTracerProvider(), set.TracerProvider) - require.Equal(t, noopmetric.NewMeterProvider(), set.MeterProvider) - require.Equal(t, configtelemetry.LevelNone, set.MetricsLevel) - require.Equal(t, pcommon.NewResource(), set.Resource) - set.Status.ReportStatus( - &component.InstanceID{}, - component.NewStatusEvent(component.StatusStarting), - ) - set.Status.ReportOKIfStarting(&component.InstanceID{}) - -} diff --git a/service/internal/servicetelemetry/package_test.go b/service/internal/servicetelemetry/package_test.go deleted file mode 100644 index 0c9a1bc67a6..00000000000 --- a/service/internal/servicetelemetry/package_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package servicetelemetry - -import ( - "testing" - - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/service/internal/servicetelemetry/telemetry_settings.go b/service/internal/servicetelemetry/telemetry_settings.go deleted file mode 100644 index 55da9bcf0c2..00000000000 --- a/service/internal/servicetelemetry/telemetry_settings.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package servicetelemetry // import "go.opentelemetry.io/collector/service/internal/servicetelemetry" - -import ( - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/trace" - "go.uber.org/zap" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configtelemetry" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/service/internal/status" -) - -// TelemetrySettings mirrors component.TelemetrySettings except for the mechanism for reporting -// status. Service-level status reporting has additional methods which can report status for -// components by their InstanceID whereas the component versions are tied to a specific component. -type TelemetrySettings struct { - // Logger that the factory can use during creation and can pass to the created - // component to be used later as well. - Logger *zap.Logger - - // TracerProvider that the factory can pass to other instrumented third-party libraries. - TracerProvider trace.TracerProvider - - // MeterProvider that the factory can pass to other instrumented third-party libraries. - MeterProvider metric.MeterProvider - - // MetricsLevel controls the level of detail for metrics emitted by the collector. - // Experimental: *NOTE* this field is experimental and may be changed or removed. - MetricsLevel configtelemetry.Level - - // Resource contains the resource attributes for the collector's telemetry. - Resource pcommon.Resource - - // Status contains a Reporter that allows the service to report status on behalf of a - // component. - Status status.Reporter -} - -// ToComponentTelemetrySettings returns a TelemetrySettings for a specific component derived from -// this service level Settings object. -func (s TelemetrySettings) ToComponentTelemetrySettings(id *component.InstanceID) component.TelemetrySettings { - statusFunc := status.NewReportStatusFunc(id, s.Status.ReportStatus) - return component.TelemetrySettings{ - Logger: s.Logger, - TracerProvider: s.TracerProvider, - MeterProvider: s.MeterProvider, - MetricsLevel: s.MetricsLevel, - Resource: s.Resource, - ReportStatus: statusFunc, - } -} diff --git a/service/internal/servicetelemetry/telemetry_settings_test.go b/service/internal/servicetelemetry/telemetry_settings_test.go deleted file mode 100644 index 5aad2c6c2b6..00000000000 --- a/service/internal/servicetelemetry/telemetry_settings_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package servicetelemetry - -import ( - "testing" - - "github.com/stretchr/testify/require" - noopmetric "go.opentelemetry.io/otel/metric/noop" - nooptrace "go.opentelemetry.io/otel/trace/noop" - "go.uber.org/zap" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configtelemetry" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/service/internal/status" -) - -func TestSettings(t *testing.T) { - set := TelemetrySettings{ - Logger: zap.NewNop(), - TracerProvider: nooptrace.NewTracerProvider(), - MeterProvider: noopmetric.NewMeterProvider(), - MetricsLevel: configtelemetry.LevelNone, - Resource: pcommon.NewResource(), - Status: status.NewReporter( - func(*component.InstanceID, *component.StatusEvent) {}, - func(err error) { require.NoError(t, err) }), - } - set.Status.Ready() - set.Status.ReportStatus( - &component.InstanceID{}, - component.NewStatusEvent(component.StatusStarting), - ) - set.Status.ReportOKIfStarting(&component.InstanceID{}) - - compSet := set.ToComponentTelemetrySettings(&component.InstanceID{}) - compSet.ReportStatus(component.NewStatusEvent(component.StatusStarting)) -} diff --git a/service/internal/status/status.go b/service/internal/status/status.go index fecae051d13..501f4750aa7 100644 --- a/service/internal/status/status.go +++ b/service/internal/status/status.go @@ -9,25 +9,26 @@ import ( "sync" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) // onTransitionFunc receives a component.StatusEvent on a successful state transition -type onTransitionFunc func(*component.StatusEvent) +type onTransitionFunc func(*componentstatus.StatusEvent) // errInvalidStateTransition is returned for invalid state transitions var errInvalidStateTransition = errors.New("invalid state transition") // fsm is a finite state machine that models transitions for component status type fsm struct { - current *component.StatusEvent - transitions map[component.Status]map[component.Status]struct{} + current *componentstatus.StatusEvent + transitions map[componentstatus.Status]map[componentstatus.Status]struct{} onTransition onTransitionFunc } // transition will attempt to execute a state transition. If it's successful, it calls the // onTransitionFunc with a StatusEvent representing the new state. Returns an error if the arguments // result in an invalid status, or if the state transition is not valid. -func (m *fsm) transition(ev *component.StatusEvent) error { +func (m *fsm) transition(ev *componentstatus.StatusEvent) error { if _, ok := m.transitions[m.current.Status()][ev.Status()]; !ok { return fmt.Errorf( "cannot transition from %s to %s: %w", @@ -45,52 +46,52 @@ func (m *fsm) transition(ev *component.StatusEvent) error { // The initial state is set to component.StatusNone. func newFSM(onTransition onTransitionFunc) *fsm { return &fsm{ - current: component.NewStatusEvent(component.StatusNone), + current: componentstatus.NewStatusEvent(componentstatus.StatusNone), onTransition: onTransition, - transitions: map[component.Status]map[component.Status]struct{}{ - component.StatusNone: { - component.StatusStarting: {}, + transitions: map[componentstatus.Status]map[componentstatus.Status]struct{}{ + componentstatus.StatusNone: { + componentstatus.StatusStarting: {}, }, - component.StatusStarting: { - component.StatusOK: {}, - component.StatusRecoverableError: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: {}, + componentstatus.StatusStarting: { + componentstatus.StatusOK: {}, + componentstatus.StatusRecoverableError: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: {}, }, - component.StatusOK: { - component.StatusRecoverableError: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: {}, + componentstatus.StatusOK: { + componentstatus.StatusRecoverableError: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: {}, }, - component.StatusRecoverableError: { - component.StatusOK: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: {}, + componentstatus.StatusRecoverableError: { + componentstatus.StatusOK: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: {}, }, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopping: { - component.StatusRecoverableError: {}, - component.StatusPermanentError: {}, - component.StatusFatalError: {}, - component.StatusStopped: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopping: { + componentstatus.StatusRecoverableError: {}, + componentstatus.StatusPermanentError: {}, + componentstatus.StatusFatalError: {}, + componentstatus.StatusStopped: {}, }, - component.StatusStopped: {}, + componentstatus.StatusStopped: {}, }, } } // NotifyStatusFunc is the receiver of status events after successful state transitions -type NotifyStatusFunc func(*component.InstanceID, *component.StatusEvent) +type NotifyStatusFunc func(*component.InstanceID, *componentstatus.StatusEvent) // InvalidTransitionFunc is the receiver of invalid transition errors type InvalidTransitionFunc func(error) // ServiceStatusFunc is the expected type of ReportStatus for servicetelemetry.Settings -type ServiceStatusFunc func(*component.InstanceID, *component.StatusEvent) +type ServiceStatusFunc func(*component.InstanceID, *componentstatus.StatusEvent) // ErrStatusNotReady is returned when trying to report status before service start var ErrStatusNotReady = errors.New("report component status is not ready until service start") @@ -98,7 +99,7 @@ var ErrStatusNotReady = errors.New("report component status is not ready until s // Reporter handles component status reporting type Reporter interface { Ready() - ReportStatus(id *component.InstanceID, ev *component.StatusEvent) + ReportStatus(id *component.InstanceID, ev *componentstatus.StatusEvent) ReportOKIfStarting(id *component.InstanceID) } @@ -130,7 +131,7 @@ func (r *reporter) Ready() { // ReportStatus reports status for the given InstanceID func (r *reporter) ReportStatus( id *component.InstanceID, - ev *component.StatusEvent, + ev *componentstatus.StatusEvent, ) { r.mu.Lock() defer r.mu.Unlock() @@ -150,8 +151,8 @@ func (r *reporter) ReportOKIfStarting(id *component.InstanceID) { r.onInvalidTransition(ErrStatusNotReady) } fsm := r.componentFSM(id) - if fsm.current.Status() == component.StatusStarting { - if err := fsm.transition(component.NewStatusEvent(component.StatusOK)); err != nil { + if fsm.current.Status() == componentstatus.StatusStarting { + if err := fsm.transition(componentstatus.NewStatusEvent(componentstatus.StatusOK)); err != nil { r.onInvalidTransition(err) } } @@ -161,7 +162,7 @@ func (r *reporter) ReportOKIfStarting(id *component.InstanceID) { func (r *reporter) componentFSM(id *component.InstanceID) *fsm { fsm, ok := r.fsmMap[id] if !ok { - fsm = newFSM(func(ev *component.StatusEvent) { r.onStatusChange(id, ev) }) + fsm = newFSM(func(ev *componentstatus.StatusEvent) { r.onStatusChange(id, ev) }) r.fsmMap[id] = fsm } return fsm @@ -173,8 +174,8 @@ func (r *reporter) componentFSM(id *component.InstanceID) *fsm { func NewReportStatusFunc( id *component.InstanceID, srvStatus ServiceStatusFunc, -) func(*component.StatusEvent) { - return func(ev *component.StatusEvent) { +) func(*componentstatus.StatusEvent) { + return func(ev *componentstatus.StatusEvent) { srvStatus(id, ev) } } diff --git a/service/internal/status/status_test.go b/service/internal/status/status_test.go index c31c649e496..d510003f58c 100644 --- a/service/internal/status/status_test.go +++ b/service/internal/status/status_test.go @@ -11,128 +11,129 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" ) func TestStatusFSM(t *testing.T) { for _, tc := range []struct { name string - reportedStatuses []component.Status - expectedStatuses []component.Status + reportedStatuses []componentstatus.Status + expectedStatuses []componentstatus.Status expectedErrorCount int }{ { name: "successful startup and shutdown", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, }, { name: "component recovered", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, }, { name: "repeated events are errors", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, - component.StatusRecoverableError, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, + componentstatus.StatusRecoverableError, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, expectedErrorCount: 2, }, { name: "PermanentError is terminal", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusPermanentError, - component.StatusOK, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusPermanentError, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusPermanentError, }, expectedErrorCount: 1, }, { name: "FatalError is terminal", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusFatalError, - component.StatusOK, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusFatalError, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusFatalError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusFatalError, }, expectedErrorCount: 1, }, { name: "Stopped is terminal", - reportedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, - component.StatusOK, + reportedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, }, expectedErrorCount: 1, }, } { t.Run(tc.name, func(t *testing.T) { - var receivedStatuses []component.Status + var receivedStatuses []componentstatus.Status fsm := newFSM( - func(ev *component.StatusEvent) { + func(ev *componentstatus.StatusEvent) { receivedStatuses = append(receivedStatuses, ev.Status()) }, ) errorCount := 0 for _, status := range tc.reportedStatuses { - if err := fsm.transition(component.NewStatusEvent(status)); err != nil { + if err := fsm.transition(componentstatus.NewStatusEvent(status)); err != nil { errorCount++ require.ErrorIs(t, err, errInvalidStateTransition) } @@ -145,33 +146,33 @@ func TestStatusFSM(t *testing.T) { } func TestValidSeqsToStopped(t *testing.T) { - events := []*component.StatusEvent{ - component.NewStatusEvent(component.StatusStarting), - component.NewStatusEvent(component.StatusOK), - component.NewStatusEvent(component.StatusRecoverableError), - component.NewStatusEvent(component.StatusPermanentError), - component.NewStatusEvent(component.StatusFatalError), + events := []*componentstatus.StatusEvent{ + componentstatus.NewStatusEvent(componentstatus.StatusStarting), + componentstatus.NewStatusEvent(componentstatus.StatusOK), + componentstatus.NewStatusEvent(componentstatus.StatusRecoverableError), + componentstatus.NewStatusEvent(componentstatus.StatusPermanentError), + componentstatus.NewStatusEvent(componentstatus.StatusFatalError), } for _, ev := range events { - name := fmt.Sprintf("transition from: %s to: %s invalid", ev.Status(), component.StatusStopped) + name := fmt.Sprintf("transition from: %s to: %s invalid", ev.Status(), componentstatus.StatusStopped) t.Run(name, func(t *testing.T) { - fsm := newFSM(func(*component.StatusEvent) {}) - if ev.Status() != component.StatusStarting { - require.NoError(t, fsm.transition(component.NewStatusEvent(component.StatusStarting))) + fsm := newFSM(func(*componentstatus.StatusEvent) {}) + if ev.Status() != componentstatus.StatusStarting { + require.NoError(t, fsm.transition(componentstatus.NewStatusEvent(componentstatus.StatusStarting))) } require.NoError(t, fsm.transition(ev)) // skipping to stopped is not allowed - err := fsm.transition(component.NewStatusEvent(component.StatusStopped)) + err := fsm.transition(componentstatus.NewStatusEvent(componentstatus.StatusStopped)) require.ErrorIs(t, err, errInvalidStateTransition) // stopping -> stopped is allowed for non-fatal, non-permanent errors - err = fsm.transition(component.NewStatusEvent(component.StatusStopping)) - if ev.Status() == component.StatusPermanentError || ev.Status() == component.StatusFatalError { + err = fsm.transition(componentstatus.NewStatusEvent(componentstatus.StatusStopping)) + if ev.Status() == componentstatus.StatusPermanentError || ev.Status() == componentstatus.StatusFatalError { require.ErrorIs(t, err, errInvalidStateTransition) } else { require.NoError(t, err) - require.NoError(t, fsm.transition(component.NewStatusEvent(component.StatusStopped))) + require.NoError(t, fsm.transition(componentstatus.NewStatusEvent(componentstatus.StatusStopped))) } }) } @@ -182,28 +183,28 @@ func TestStatusFuncs(t *testing.T) { id1 := &component.InstanceID{} id2 := &component.InstanceID{} - actualStatuses := make(map[*component.InstanceID][]component.Status) - statusFunc := func(id *component.InstanceID, ev *component.StatusEvent) { + actualStatuses := make(map[*component.InstanceID][]componentstatus.Status) + statusFunc := func(id *component.InstanceID, ev *componentstatus.StatusEvent) { actualStatuses[id] = append(actualStatuses[id], ev.Status()) } - statuses1 := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + statuses1 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, } - statuses2 := []component.Status{ - component.StatusStarting, - component.StatusOK, - component.StatusRecoverableError, - component.StatusOK, - component.StatusStopping, - component.StatusStopped, + statuses2 := []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, + componentstatus.StatusRecoverableError, + componentstatus.StatusOK, + componentstatus.StatusStopping, + componentstatus.StatusStopped, } - expectedStatuses := map[*component.InstanceID][]component.Status{ + expectedStatuses := map[*component.InstanceID][]componentstatus.Status{ id1: statuses1, id2: statuses2, } @@ -217,11 +218,11 @@ func TestStatusFuncs(t *testing.T) { rep.Ready() for _, st := range statuses1 { - comp1Func(component.NewStatusEvent(st)) + comp1Func(componentstatus.NewStatusEvent(st)) } for _, st := range statuses2 { - comp2Func(component.NewStatusEvent(st)) + comp2Func(componentstatus.NewStatusEvent(st)) } require.Equal(t, expectedStatuses, actualStatuses) @@ -230,7 +231,7 @@ func TestStatusFuncs(t *testing.T) { func TestStatusFuncsConcurrent(t *testing.T) { ids := []*component.InstanceID{{}, {}, {}, {}} count := 0 - statusFunc := func(*component.InstanceID, *component.StatusEvent) { + statusFunc := func(*component.InstanceID, *componentstatus.StatusEvent) { count++ } rep := NewReporter(statusFunc, @@ -246,10 +247,10 @@ func TestStatusFuncsConcurrent(t *testing.T) { id := id go func() { compFn := NewReportStatusFunc(id, rep.ReportStatus) - compFn(component.NewStatusEvent(component.StatusStarting)) + compFn(componentstatus.NewStatusEvent(componentstatus.StatusStarting)) for i := 0; i < 1000; i++ { - compFn(component.NewStatusEvent(component.StatusRecoverableError)) - compFn(component.NewStatusEvent(component.StatusOK)) + compFn(componentstatus.NewStatusEvent(componentstatus.StatusRecoverableError)) + compFn(componentstatus.NewStatusEvent(componentstatus.StatusOK)) } wg.Done() }() @@ -260,7 +261,7 @@ func TestStatusFuncsConcurrent(t *testing.T) { } func TestReporterReady(t *testing.T) { - statusFunc := func(*component.InstanceID, *component.StatusEvent) {} + statusFunc := func(*component.InstanceID, *componentstatus.StatusEvent) {} var err error rep := NewReporter(statusFunc, func(e error) { @@ -268,70 +269,70 @@ func TestReporterReady(t *testing.T) { }) id := &component.InstanceID{} - rep.ReportStatus(id, component.NewStatusEvent(component.StatusStarting)) + rep.ReportStatus(id, componentstatus.NewStatusEvent(componentstatus.StatusStarting)) require.ErrorIs(t, err, ErrStatusNotReady) rep.Ready() err = nil - rep.ReportStatus(id, component.NewStatusEvent(component.StatusStarting)) + rep.ReportStatus(id, componentstatus.NewStatusEvent(componentstatus.StatusStarting)) require.NoError(t, err) } func TestReportComponentOKIfStarting(t *testing.T) { for _, tc := range []struct { name string - initialStatuses []component.Status - expectedStatuses []component.Status + initialStatuses []componentstatus.Status + expectedStatuses []componentstatus.Status }{ { name: "matching condition: StatusStarting", - initialStatuses: []component.Status{ - component.StatusStarting, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, }, }, { name: "non-matching condition StatusOK", - initialStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusOK, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusOK, }, }, { name: "non-matching condition RecoverableError", - initialStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusRecoverableError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusRecoverableError, }, }, { name: "non-matching condition PermanentError", - initialStatuses: []component.Status{ - component.StatusStarting, - component.StatusPermanentError, + initialStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusPermanentError, }, - expectedStatuses: []component.Status{ - component.StatusStarting, - component.StatusPermanentError, + expectedStatuses: []componentstatus.Status{ + componentstatus.StatusStarting, + componentstatus.StatusPermanentError, }, }, } { t.Run(tc.name, func(t *testing.T) { - var receivedStatuses []component.Status + var receivedStatuses []componentstatus.Status rep := NewReporter( - func(_ *component.InstanceID, ev *component.StatusEvent) { + func(_ *component.InstanceID, ev *componentstatus.StatusEvent) { receivedStatuses = append(receivedStatuses, ev.Status()) }, func(err error) { @@ -342,7 +343,7 @@ func TestReportComponentOKIfStarting(t *testing.T) { id := &component.InstanceID{} for _, status := range tc.initialStatuses { - rep.ReportStatus(id, component.NewStatusEvent(status)) + rep.ReportStatus(id, componentstatus.NewStatusEvent(status)) } rep.ReportOKIfStarting(id) diff --git a/service/internal/status/statustest/statustest.go b/service/internal/status/statustest/statustest.go index 3ba40a16298..34c41d94959 100644 --- a/service/internal/status/statustest/statustest.go +++ b/service/internal/status/statustest/statustest.go @@ -5,6 +5,7 @@ package statustest // import "go.opentelemetry.io/collector/service/internal/sta import ( "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/service/internal/status" ) @@ -16,6 +17,6 @@ type nopStatusReporter struct{} func (r *nopStatusReporter) Ready() {} -func (r *nopStatusReporter) ReportStatus(*component.InstanceID, *component.StatusEvent) {} +func (r *nopStatusReporter) ReportStatus(*component.InstanceID, *componentstatus.StatusEvent) {} func (r *nopStatusReporter) ReportOKIfStarting(*component.InstanceID) {} diff --git a/service/internal/servicetelemetry/nop_telemetry_settings.go b/service/internal/testcomponents/telemetry_settings.go similarity index 65% rename from service/internal/servicetelemetry/nop_telemetry_settings.go rename to service/internal/testcomponents/telemetry_settings.go index 116554a51f8..b4c0428c85b 100644 --- a/service/internal/servicetelemetry/nop_telemetry_settings.go +++ b/service/internal/testcomponents/telemetry_settings.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package servicetelemetry // import "go.opentelemetry.io/collector/service/internal/servicetelemetry" +package testcomponents // import "go.opentelemetry.io/collector/service/internal/testcomponents" import ( noopmetric "go.opentelemetry.io/otel/metric/noop" @@ -11,17 +11,15 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configtelemetry" "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/service/internal/status" ) // NewNopTelemetrySettings returns a new nop settings for Create* functions. -func NewNopTelemetrySettings() TelemetrySettings { - return TelemetrySettings{ +func NewNopTelemetrySettings() component.TelemetrySettings { + return component.TelemetrySettings{ Logger: zap.NewNop(), TracerProvider: nooptrace.NewTracerProvider(), MeterProvider: noopmetric.NewMeterProvider(), MetricsLevel: configtelemetry.LevelNone, Resource: pcommon.NewResource(), - Status: status.NewReporter(func(*component.InstanceID, *component.StatusEvent) {}, func(error) {}), } } diff --git a/service/service.go b/service/service.go index 8b57bd12cad..333d6137033 100644 --- a/service/service.go +++ b/service/service.go @@ -32,7 +32,6 @@ import ( "go.opentelemetry.io/collector/service/internal/graph" "go.opentelemetry.io/collector/service/internal/proctelemetry" "go.opentelemetry.io/collector/service/internal/resource" - "go.opentelemetry.io/collector/service/internal/servicetelemetry" "go.opentelemetry.io/collector/service/internal/status" "go.opentelemetry.io/collector/service/telemetry" ) @@ -70,8 +69,8 @@ type Settings struct { // Service represents the implementation of a component.Host. type Service struct { buildInfo component.BuildInfo - telemetrySettings servicetelemetry.TelemetrySettings - host *serviceHost + telemetrySettings component.TelemetrySettings + host *graph.Host collectorConf *confmap.Conf } @@ -81,14 +80,14 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) { extendedConfig := obsreportconfig.UseOtelWithSDKConfigurationForInternalTelemetryFeatureGate.IsEnabled() srv := &Service{ buildInfo: set.BuildInfo, - host: &serviceHost{ - receivers: set.Receivers, - processors: set.Processors, - exporters: set.Exporters, - connectors: set.Connectors, - extensions: set.Extensions, - buildInfo: set.BuildInfo, - asyncErrorChannel: set.AsyncErrorChannel, + host: &graph.Host{ + Receivers: set.Receivers, + Processors: set.Processors, + Exporters: set.Exporters, + Connectors: set.Connectors, + Extensions: set.Extensions, + BuildInfo: set.BuildInfo, + AsyncErrorChannel: set.AsyncErrorChannel, }, collectorConf: set.CollectorConf, } @@ -108,6 +107,13 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) { return nil, fmt.Errorf("failed to create logger: %w", err) } + srv.host.Reporter = status.NewReporter(srv.host.NotifyComponentStatusChange, func(err error) { + if errors.Is(err, status.ErrStatusNotReady) { + logger.Warn("Invalid transition", zap.Error(err)) + } + // ignore other errors as they represent invalid state transitions and are considered benign. + }) + tracerProvider, err := telFactory.CreateTracerProvider(ctx, telset, &cfg.Telemetry) if err != nil { return nil, fmt.Errorf("failed to create tracer provider: %w", err) @@ -131,19 +137,13 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) { } logsAboutMeterProvider(logger, cfg.Telemetry.Metrics, mp, extendedConfig) - srv.telemetrySettings = servicetelemetry.TelemetrySettings{ + srv.telemetrySettings = component.TelemetrySettings{ Logger: logger, MeterProvider: mp, TracerProvider: tracerProvider, MetricsLevel: cfg.Telemetry.Metrics.Level, // Construct telemetry attributes from build info and config's resource attributes. Resource: pcommonRes, - Status: status.NewReporter(srv.host.notifyComponentStatusChange, func(err error) { - if errors.Is(err, status.ErrStatusNotReady) { - logger.Warn("Invalid transition", zap.Error(err)) - } - // ignore other errors as they represent invalid state transitions and are considered benign. - }), } if err = srv.initGraph(ctx, set, cfg); err != nil { @@ -201,23 +201,23 @@ func (srv *Service) Start(ctx context.Context) error { ) // enable status reporting - srv.telemetrySettings.Status.Ready() + srv.host.Reporter.Ready() - if err := srv.host.serviceExtensions.Start(ctx, srv.host); err != nil { + if err := srv.host.ServiceExtensions.Start(ctx, srv.host); err != nil { return fmt.Errorf("failed to start extensions: %w", err) } if srv.collectorConf != nil { - if err := srv.host.serviceExtensions.NotifyConfig(ctx, srv.collectorConf); err != nil { + if err := srv.host.ServiceExtensions.NotifyConfig(ctx, srv.collectorConf); err != nil { return err } } - if err := srv.host.pipelines.StartAll(ctx, srv.host, srv.telemetrySettings.Status); err != nil { + if err := srv.host.Pipelines.StartAll(ctx, srv.host); err != nil { return fmt.Errorf("cannot start pipelines: %w", err) } - if err := srv.host.serviceExtensions.NotifyPipelineReady(); err != nil { + if err := srv.host.ServiceExtensions.NotifyPipelineReady(); err != nil { return err } @@ -260,15 +260,15 @@ func (srv *Service) Shutdown(ctx context.Context) error { // Begin shutdown sequence. srv.telemetrySettings.Logger.Info("Starting shutdown...") - if err := srv.host.serviceExtensions.NotifyPipelineNotReady(); err != nil { + if err := srv.host.ServiceExtensions.NotifyPipelineNotReady(); err != nil { errs = multierr.Append(errs, fmt.Errorf("failed to notify that pipeline is not ready: %w", err)) } - if err := srv.host.pipelines.ShutdownAll(ctx, srv.telemetrySettings.Status); err != nil { + if err := srv.host.Pipelines.ShutdownAll(ctx, srv.host.Reporter); err != nil { errs = multierr.Append(errs, fmt.Errorf("failed to shutdown pipelines: %w", err)) } - if err := srv.host.serviceExtensions.Shutdown(ctx); err != nil { + if err := srv.host.ServiceExtensions.Shutdown(ctx); err != nil { errs = multierr.Append(errs, fmt.Errorf("failed to shutdown extensions: %w", err)) } @@ -285,9 +285,9 @@ func (srv *Service) initExtensions(ctx context.Context, cfg extensions.Config) e extensionsSettings := extensions.Settings{ Telemetry: srv.telemetrySettings, BuildInfo: srv.buildInfo, - Extensions: srv.host.extensions, + Extensions: srv.host.Extensions, } - if srv.host.serviceExtensions, err = extensions.New(ctx, extensionsSettings, cfg); err != nil { + if srv.host.ServiceExtensions, err = extensions.New(ctx, extensionsSettings, cfg, extensions.WithReporter(srv.host.Reporter)); err != nil { return fmt.Errorf("failed to build extensions: %w", err) } return nil @@ -296,7 +296,7 @@ func (srv *Service) initExtensions(ctx context.Context, cfg extensions.Config) e // Creates the pipeline graph. func (srv *Service) initGraph(ctx context.Context, set Settings, cfg Config) error { var err error - if srv.host.pipelines, err = graph.Build(ctx, graph.Settings{ + if srv.host.Pipelines, err = graph.Build(ctx, graph.Settings{ Telemetry: srv.telemetrySettings, BuildInfo: srv.buildInfo, ReceiverBuilder: set.Receivers, diff --git a/service/service_test.go b/service/service_test.go index b0a952724be..7021e364627 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -21,6 +21,7 @@ import ( "go.uber.org/zap/zapcore" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/config/configtelemetry" "go.opentelemetry.io/collector/confmap" @@ -227,6 +228,7 @@ func TestServiceGetExporters(t *testing.T) { assert.NoError(t, srv.Shutdown(context.Background())) }) + // nolint SA1019 expMap := srv.host.GetExporters() assert.Len(t, expMap, 3) assert.Len(t, expMap[component.DataTypeTraces], 1) @@ -437,11 +439,11 @@ func TestServiceFatalError(t *testing.T) { }) go func() { - ev := component.NewFatalErrorEvent(assert.AnError) - srv.host.notifyComponentStatusChange(&component.InstanceID{}, ev) + ev := componentstatus.NewFatalErrorEvent(assert.AnError) + srv.host.NotifyComponentStatusChange(&component.InstanceID{}, ev) }() - err = <-srv.host.asyncErrorChannel + err = <-srv.host.AsyncErrorChannel require.ErrorIs(t, err, assert.AnError) } diff --git a/service/zpages.go b/service/zpages.go deleted file mode 100644 index c7f7b494ad3..00000000000 --- a/service/zpages.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package service // import "go.opentelemetry.io/collector/service" - -import ( - "net/http" - "path" - "runtime" - "time" - - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/featuregate" - "go.opentelemetry.io/collector/service/internal/zpages" -) - -const ( - // Paths - zServicePath = "servicez" - zPipelinePath = "pipelinez" - zExtensionPath = "extensionz" - zFeaturePath = "featurez" -) - -var ( - // InfoVar is a singleton instance of the Info struct. - runtimeInfoVar [][2]string -) - -func init() { - runtimeInfoVar = [][2]string{ - {"StartTimestamp", time.Now().String()}, - {"Go", runtime.Version()}, - {"OS", runtime.GOOS}, - {"Arch", runtime.GOARCH}, - // Add other valuable runtime information here. - } -} - -func (host *serviceHost) RegisterZPages(mux *http.ServeMux, pathPrefix string) { - mux.HandleFunc(path.Join(pathPrefix, zServicePath), host.zPagesRequest) - mux.HandleFunc(path.Join(pathPrefix, zPipelinePath), host.pipelines.HandleZPages) - mux.HandleFunc(path.Join(pathPrefix, zExtensionPath), host.serviceExtensions.HandleZPages) - mux.HandleFunc(path.Join(pathPrefix, zFeaturePath), handleFeaturezRequest) -} - -func (host *serviceHost) zPagesRequest(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Service " + host.buildInfo.Command}) - zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Build Info", Properties: getBuildInfoProperties(host.buildInfo)}) - zpages.WriteHTMLPropertiesTable(w, zpages.PropertiesTableData{Name: "Runtime Info", Properties: runtimeInfoVar}) - zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ - Name: "Pipelines", - ComponentEndpoint: zPipelinePath, - Link: true, - }) - zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ - Name: "Extensions", - ComponentEndpoint: zExtensionPath, - Link: true, - }) - zpages.WriteHTMLComponentHeader(w, zpages.ComponentHeaderData{ - Name: "Features", - ComponentEndpoint: zFeaturePath, - Link: true, - }) - zpages.WriteHTMLPageFooter(w) -} - -func handleFeaturezRequest(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - zpages.WriteHTMLPageHeader(w, zpages.HeaderData{Title: "Feature Gates"}) - zpages.WriteHTMLFeaturesTable(w, getFeaturesTableData()) - zpages.WriteHTMLPageFooter(w) -} - -func getFeaturesTableData() zpages.FeatureGateTableData { - data := zpages.FeatureGateTableData{} - featuregate.GlobalRegistry().VisitAll(func(gate *featuregate.Gate) { - data.Rows = append(data.Rows, zpages.FeatureGateTableRowData{ - ID: gate.ID(), - Enabled: gate.IsEnabled(), - Description: gate.Description(), - Stage: gate.Stage().String(), - FromVersion: gate.FromVersion(), - ToVersion: gate.ToVersion(), - ReferenceURL: gate.ReferenceURL(), - }) - }) - return data -} - -func getBuildInfoProperties(buildInfo component.BuildInfo) [][2]string { - return [][2]string{ - {"Command", buildInfo.Command}, - {"Description", buildInfo.Description}, - {"Version", buildInfo.Version}, - } -} diff --git a/versions.yaml b/versions.yaml index 7ee33e71b7b..727802ada1a 100644 --- a/versions.yaml +++ b/versions.yaml @@ -20,6 +20,7 @@ module-sets: - go.opentelemetry.io/collector/cmd/mdatagen - go.opentelemetry.io/collector/component - go.opentelemetry.io/collector/component/componentprofiles + - go.opentelemetry.io/collector/component/componentstatus - go.opentelemetry.io/collector/confmap - go.opentelemetry.io/collector/confmap/converter/expandconverter - go.opentelemetry.io/collector/confmap/provider/envprovider