Move error out of `ReportComponentStatus` function signature, use `ReportStatus` instead (#9175)

Fixes #9148
This commit is contained in:
Antoine Toulme 2024-01-09 09:36:41 -08:00 committed by GitHub
parent 6ed7ad1073
commit c5a2c78d61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 184 additions and 122 deletions

View File

@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: breaking
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: status
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Deprecate `ReportComponentStatus` in favor of `ReportStatus`. This new function does not return an error.
# One or more tracking issues or pull requests related to the change
issues: [9148]
# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []

View File

@ -21,8 +21,7 @@ func NewNopTelemetrySettings() component.TelemetrySettings {
MeterProvider: noopmetric.NewMeterProvider(),
MetricsLevel: configtelemetry.LevelNone,
Resource: pcommon.NewResource(),
ReportComponentStatus: func(*component.StatusEvent) error {
return nil
ReportStatus: func(*component.StatusEvent) {
},
}
}

View File

@ -37,13 +37,13 @@ type TelemetrySettings struct {
// ReportComponentStatus 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. ReportComponentStatus
// will only return errors if the API used incorrectly. The two scenarios where an error will
// be returned are:
//
// - An illegal state transition
// - Calling this method before component startup
//
// If the API is being used properly, these errors are safe to ignore.
// use this method to report status after start and before shutdown.
// Deprecated: [v0.92.0] This function will be removed in a future release.
// Use ReportStatus instead.
ReportComponentStatus func(*StatusEvent) error
// 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.
ReportStatus func(*StatusEvent)
}

View File

@ -29,17 +29,14 @@ type Map[K comparable, V component.Component] struct {
func (m *Map[K, V]) LoadOrStore(key K, create func() (V, error), telemetrySettings *component.TelemetrySettings) (*Component[V], error) {
if c, ok := m.components[key]; ok {
// If we haven't already seen this telemetry settings, this shared component represents
// another instance. Wrap ReportComponentStatus to report for all instances this shared
// 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.ReportComponentStatus
c.telemetry.ReportComponentStatus = func(ev *component.StatusEvent) error {
err := telemetrySettings.ReportComponentStatus(ev)
if prevErr := prev(ev); prevErr != nil {
err = prevErr
}
return err
prev := c.telemetry.ReportStatus
c.telemetry.ReportStatus = func(ev *component.StatusEvent) {
telemetrySettings.ReportStatus(ev)
prev(ev)
}
}
return c, nil
@ -89,9 +86,9 @@ func (c *Component[V]) Start(ctx context.Context, host component.Host) 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.ReportComponentStatus(component.NewStatusEvent(component.StatusStarting))
c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStarting))
if err = c.component.Start(ctx, host); err != nil {
_ = c.telemetry.ReportComponentStatus(component.NewPermanentErrorEvent(err))
c.telemetry.ReportStatus(component.NewPermanentErrorEvent(err))
}
})
return err
@ -105,12 +102,12 @@ 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.ReportComponentStatus(component.NewStatusEvent(component.StatusStopping))
c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopping))
err = c.component.Shutdown(ctx)
if err != nil {
_ = c.telemetry.ReportComponentStatus(component.NewPermanentErrorEvent(err))
c.telemetry.ReportStatus(component.NewPermanentErrorEvent(err))
} else {
_ = c.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusStopped))
c.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopped))
}
c.removeFunc()
})

View File

@ -109,15 +109,13 @@ func TestSharedComponent(t *testing.T) {
}
func TestSharedComponentsReportStatus(t *testing.T) {
reportedStatuses := make(map[*component.InstanceID][]component.Status)
newStatusFunc := func() func(*component.StatusEvent) error {
newStatusFunc := func() func(*component.StatusEvent) {
instanceID := &component.InstanceID{}
return func(ev *component.StatusEvent) error {
// Use an event with component.StatusNone to simulate an error.
return func(ev *component.StatusEvent) {
if ev.Status() == component.StatusNone {
return assert.AnError
return
}
reportedStatuses[instanceID] = append(reportedStatuses[instanceID], ev.Status())
return nil
}
}
@ -128,10 +126,10 @@ func TestSharedComponentsReportStatus(t *testing.T) {
// make a shared component that represents three instances
for i := 0; i < 3; i++ {
telemetrySettings = newNopTelemetrySettings()
telemetrySettings.ReportComponentStatus = newStatusFunc()
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 ReportComponentStatus for subsequently added "instances".
// wrap ReportStatus for subsequently added "instances".
if i == 0 {
comp.telemetry = telemetrySettings
}
@ -152,24 +150,18 @@ func TestSharedComponentsReportStatus(t *testing.T) {
telemetrySettings,
)
err := comp.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusStarting))
require.NoError(t, err)
comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStarting))
// ok
err = comp.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusOK))
require.NoError(t, err)
comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusOK))
// simulate an error
err = comp.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusNone))
require.ErrorIs(t, err, assert.AnError)
comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusNone))
// stopping
err = comp.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusStopping))
require.NoError(t, err)
comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusStopping))
// stopped
err = comp.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusStopped))
require.NoError(t, err)
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.
@ -227,11 +219,10 @@ func TestReportStatusOnStartShutdown(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
reportedStatuses := make(map[*component.InstanceID][]component.Status)
newStatusFunc := func() func(*component.StatusEvent) error {
newStatusFunc := func() func(*component.StatusEvent) {
instanceID := &component.InstanceID{}
return func(ev *component.StatusEvent) error {
return func(ev *component.StatusEvent) {
reportedStatuses[instanceID] = append(reportedStatuses[instanceID], ev.Status())
return nil
}
}
base := &baseComponent{}
@ -250,7 +241,7 @@ func TestReportStatusOnStartShutdown(t *testing.T) {
var err error
for i := 0; i < 3; i++ {
telemetrySettings := newNopTelemetrySettings()
telemetrySettings.ReportComponentStatus = newStatusFunc()
telemetrySettings.ReportStatus = newStatusFunc()
if i == 0 {
base.telemetry = telemetrySettings
}
@ -266,8 +257,7 @@ func TestReportStatusOnStartShutdown(t *testing.T) {
require.Equal(t, tc.startErr, err)
if tc.startErr == nil {
err = comp.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusOK))
require.NoError(t, err)
comp.telemetry.ReportStatus(component.NewStatusEvent(component.StatusOK))
err = comp.Shutdown(context.Background())
require.Equal(t, tc.shutdownErr, err)

View File

@ -64,7 +64,7 @@ type unhealthyProcessor struct {
func (p unhealthyProcessor) Start(_ context.Context, _ component.Host) error {
go func() {
_ = p.telemetry.ReportComponentStatus(component.NewStatusEvent(component.StatusRecoverableError))
p.telemetry.ReportStatus(component.NewStatusEvent(component.StatusRecoverableError))
}()
return nil
}

View File

@ -37,18 +37,18 @@ 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.ReportComponentStatus(
bes.telemetry.Status.ReportStatus(
instanceID,
component.NewStatusEvent(component.StatusStarting),
)
if err := ext.Start(ctx, components.NewHostWrapper(host, extLogger)); err != nil {
_ = bes.telemetry.Status.ReportComponentStatus(
bes.telemetry.Status.ReportStatus(
instanceID,
component.NewPermanentErrorEvent(err),
)
return err
}
_ = bes.telemetry.Status.ReportComponentOKIfStarting(instanceID)
bes.telemetry.Status.ReportOKIfStarting(instanceID)
extLogger.Info("Extension started.")
}
return nil
@ -62,19 +62,19 @@ func (bes *Extensions) Shutdown(ctx context.Context) error {
extID := bes.extensionIDs[i]
instanceID := bes.instanceIDs[extID]
ext := bes.extMap[extID]
_ = bes.telemetry.Status.ReportComponentStatus(
bes.telemetry.Status.ReportStatus(
instanceID,
component.NewStatusEvent(component.StatusStopping),
)
if err := ext.Shutdown(ctx); err != nil {
_ = bes.telemetry.Status.ReportComponentStatus(
bes.telemetry.Status.ReportStatus(
instanceID,
component.NewPermanentErrorEvent(err),
)
errs = multierr.Append(errs, err)
continue
}
_ = bes.telemetry.Status.ReportComponentStatus(
bes.telemetry.Status.ReportStatus(
instanceID,
component.NewStatusEvent(component.StatusStopped),
)

View File

@ -434,6 +434,8 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) {
var actualStatuses []*component.StatusEvent
rep := status.NewReporter(func(id *component.InstanceID, ev *component.StatusEvent) {
actualStatuses = append(actualStatuses, ev)
}, func(err error) {
require.NoError(t, err)
})
extensions.telemetry.Status = rep
rep.Ready()

View File

@ -388,20 +388,20 @@ func (g *Graph) StartAll(ctx context.Context, host component.Host) error {
}
instanceID := g.instanceIDs[node.ID()]
_ = g.telemetry.Status.ReportComponentStatus(
g.telemetry.Status.ReportStatus(
instanceID,
component.NewStatusEvent(component.StatusStarting),
)
if compErr := comp.Start(ctx, host); compErr != nil {
_ = g.telemetry.Status.ReportComponentStatus(
g.telemetry.Status.ReportStatus(
instanceID,
component.NewPermanentErrorEvent(compErr),
)
return compErr
}
_ = g.telemetry.Status.ReportComponentOKIfStarting(instanceID)
g.telemetry.Status.ReportOKIfStarting(instanceID)
}
return nil
}
@ -427,21 +427,21 @@ func (g *Graph) ShutdownAll(ctx context.Context) error {
}
instanceID := g.instanceIDs[node.ID()]
_ = g.telemetry.Status.ReportComponentStatus(
g.telemetry.Status.ReportStatus(
instanceID,
component.NewStatusEvent(component.StatusStopping),
)
if compErr := comp.Shutdown(ctx); compErr != nil {
errs = multierr.Append(errs, compErr)
_ = g.telemetry.Status.ReportComponentStatus(
g.telemetry.Status.ReportStatus(
instanceID,
component.NewPermanentErrorEvent(compErr),
)
continue
}
_ = g.telemetry.Status.ReportComponentStatus(
g.telemetry.Status.ReportStatus(
instanceID,
component.NewStatusEvent(component.StatusStopped),
)

View File

@ -2337,6 +2337,7 @@ func TestStatusReportedOnStartupShutdown(t *testing.T) {
actualStatuses := make(map[*component.InstanceID][]*component.StatusEvent)
rep := status.NewReporter(func(id *component.InstanceID, ev *component.StatusEvent) {
actualStatuses[id] = append(actualStatuses[id], ev)
}, func(err error) {
})
pg.telemetry.Status = rep

View File

@ -22,6 +22,6 @@ func NewNopTelemetrySettings() TelemetrySettings {
MeterProvider: noopmetric.NewMeterProvider(),
MetricsLevel: configtelemetry.LevelNone,
Resource: pcommon.NewResource(),
Status: status.NewReporter(func(*component.InstanceID, *component.StatusEvent) {}),
Status: status.NewReporter(func(*component.InstanceID, *component.StatusEvent) {}, func(err error) {}),
}
}

View File

@ -26,11 +26,10 @@ func TestNewNopSettings(t *testing.T) {
require.Equal(t, noopmetric.NewMeterProvider(), set.MeterProvider)
require.Equal(t, configtelemetry.LevelNone, set.MetricsLevel)
require.Equal(t, pcommon.NewResource(), set.Resource)
require.NoError(t,
set.Status.ReportComponentStatus(
&component.InstanceID{},
component.NewStatusEvent(component.StatusStarting),
),
set.Status.ReportStatus(
&component.InstanceID{},
component.NewStatusEvent(component.StatusStarting),
)
require.NoError(t, set.Status.ReportComponentOKIfStarting(&component.InstanceID{}))
set.Status.ReportOKIfStarting(&component.InstanceID{})
}

View File

@ -43,12 +43,17 @@ type TelemetrySettings struct {
// 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,
ReportComponentStatus: status.NewComponentStatusFunc(id, s.Status.ReportComponentStatus),
Logger: s.Logger,
TracerProvider: s.TracerProvider,
MeterProvider: s.MeterProvider,
MetricsLevel: s.MetricsLevel,
Resource: s.Resource,
ReportComponentStatus: func(event *component.StatusEvent) error {
statusFunc(event)
return nil
},
ReportStatus: statusFunc,
}
}

View File

@ -24,17 +24,17 @@ func TestSettings(t *testing.T) {
MeterProvider: noopmetric.NewMeterProvider(),
MetricsLevel: configtelemetry.LevelNone,
Resource: pcommon.NewResource(),
Status: status.NewReporter(func(*component.InstanceID, *component.StatusEvent) {}),
Status: status.NewReporter(
func(*component.InstanceID, *component.StatusEvent) {},
func(err error) { require.NoError(t, err) }),
}
set.Status.Ready()
require.NoError(t,
set.Status.ReportComponentStatus(
&component.InstanceID{},
component.NewStatusEvent(component.StatusStarting),
),
set.Status.ReportStatus(
&component.InstanceID{},
component.NewStatusEvent(component.StatusStarting),
)
require.NoError(t, set.Status.ReportComponentOKIfStarting(&component.InstanceID{}))
set.Status.ReportOKIfStarting(&component.InstanceID{})
compSet := set.ToComponentTelemetrySettings(&component.InstanceID{})
require.NoError(t, compSet.ReportComponentStatus(component.NewStatusEvent(component.StatusStarting)))
compSet.ReportStatus(component.NewStatusEvent(component.StatusStarting))
}

View File

@ -86,26 +86,31 @@ func newFSM(onTransition onTransitionFunc) *fsm {
// NotifyStatusFunc is the receiver of status events after successful state transitions
type NotifyStatusFunc func(*component.InstanceID, *component.StatusEvent)
// ServiceStatusFunc is the expected type of ReportComponentStatus for servicetelemetry.Settings
type ServiceStatusFunc func(*component.InstanceID, *component.StatusEvent) error
// InvalidTransitionFunc is the receiver of invalid transition errors
type InvalidTransitionFunc func(error)
// errStatusNotReady is returned when trying to report status before service start
var errStatusNotReady = errors.New("report component status is not ready until service start")
// ServiceStatusFunc is the expected type of ReportStatus for servicetelemetry.Settings
type ServiceStatusFunc func(*component.InstanceID, *component.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")
// Reporter handles component status reporting
type Reporter struct {
mu sync.Mutex
ready bool
fsmMap map[*component.InstanceID]*fsm
onStatusChange NotifyStatusFunc
mu sync.Mutex
ready bool
fsmMap map[*component.InstanceID]*fsm
onStatusChange NotifyStatusFunc
onInvalidTransition InvalidTransitionFunc
}
// NewReporter returns a reporter that will invoke the NotifyStatusFunc when a component's status
// has changed.
func NewReporter(onStatusChange NotifyStatusFunc) *Reporter {
func NewReporter(onStatusChange NotifyStatusFunc, onInvalidTransition InvalidTransitionFunc) *Reporter {
return &Reporter{
fsmMap: make(map[*component.InstanceID]*fsm),
onStatusChange: onStatusChange,
fsmMap: make(map[*component.InstanceID]*fsm),
onStatusChange: onStatusChange,
onInvalidTransition: onInvalidTransition,
}
}
@ -117,30 +122,52 @@ func (r *Reporter) Ready() {
}
// ReportComponentStatus reports status for the given InstanceID
// Deprecated: [v0.92.0] This function will be removed in a future release.
// Use ReportStatus instead.
func (r *Reporter) ReportComponentStatus(
id *component.InstanceID,
ev *component.StatusEvent,
) error {
r.ReportStatus(id, ev)
return nil
}
// ReportStatus reports status for the given InstanceID
func (r *Reporter) ReportStatus(
id *component.InstanceID,
ev *component.StatusEvent,
) {
r.mu.Lock()
defer r.mu.Unlock()
if !r.ready {
return errStatusNotReady
r.onInvalidTransition(ErrStatusNotReady)
} else {
if err := r.componentFSM(id).transition(ev); err != nil {
r.onInvalidTransition(err)
}
}
return r.componentFSM(id).transition(ev)
}
// ReportComponentOkIfStarting reports StatusOK if the component's current status is Starting
// Deprecated: [v0.92.0] This function will be removed in a future release.
// Use ReportOKIfStarting instead.
func (r *Reporter) ReportComponentOKIfStarting(id *component.InstanceID) error {
r.ReportOKIfStarting(id)
return nil
}
func (r *Reporter) ReportOKIfStarting(id *component.InstanceID) {
r.mu.Lock()
defer r.mu.Unlock()
if !r.ready {
return errStatusNotReady
r.onInvalidTransition(ErrStatusNotReady)
}
fsm := r.componentFSM(id)
if fsm.current.Status() == component.StatusStarting {
return fsm.transition(component.NewStatusEvent(component.StatusOK))
if err := fsm.transition(component.NewStatusEvent(component.StatusOK)); err != nil {
r.onInvalidTransition(err)
}
}
return nil
}
// Note: a lock must be acquired before calling this method.
@ -153,14 +180,14 @@ func (r *Reporter) componentFSM(id *component.InstanceID) *fsm {
return fsm
}
// NewComponentStatusFunc returns a function to be used as ReportComponentStatus for
// NewReportStatusFunc returns a function to be used as ReportStatus for
// component.TelemetrySettings, which differs from servicetelemetry.Settings in that
// the component version is tied to specific component instance.
func NewComponentStatusFunc(
func NewReportStatusFunc(
id *component.InstanceID,
srvStatus ServiceStatusFunc,
) func(*component.StatusEvent) error {
return func(ev *component.StatusEvent) error {
return srvStatus(id, ev)
) func(*component.StatusEvent) {
return func(ev *component.StatusEvent) {
srvStatus(id, ev)
}
}

View File

@ -208,17 +208,20 @@ func TestStatusFuncs(t *testing.T) {
id2: statuses2,
}
rep := NewReporter(statusFunc)
comp1Func := NewComponentStatusFunc(id1, rep.ReportComponentStatus)
comp2Func := NewComponentStatusFunc(id2, rep.ReportComponentStatus)
rep := NewReporter(statusFunc,
func(err error) {
require.NoError(t, err)
})
comp1Func := NewReportStatusFunc(id1, rep.ReportStatus)
comp2Func := NewReportStatusFunc(id2, rep.ReportStatus)
rep.Ready()
for _, st := range statuses1 {
require.NoError(t, comp1Func(component.NewStatusEvent(st)))
comp1Func(component.NewStatusEvent(st))
}
for _, st := range statuses2 {
require.NoError(t, comp2Func(component.NewStatusEvent(st)))
comp2Func(component.NewStatusEvent(st))
}
require.Equal(t, expectedStatuses, actualStatuses)
@ -230,7 +233,10 @@ func TestStatusFuncsConcurrent(t *testing.T) {
statusFunc := func(id *component.InstanceID, ev *component.StatusEvent) {
count++
}
rep := NewReporter(statusFunc)
rep := NewReporter(statusFunc,
func(err error) {
require.NoError(t, err)
})
rep.Ready()
wg := sync.WaitGroup{}
@ -239,11 +245,11 @@ func TestStatusFuncsConcurrent(t *testing.T) {
for _, id := range ids {
id := id
go func() {
compFn := NewComponentStatusFunc(id, rep.ReportComponentStatus)
_ = compFn(component.NewStatusEvent(component.StatusStarting))
compFn := NewReportStatusFunc(id, rep.ReportStatus)
compFn(component.NewStatusEvent(component.StatusStarting))
for i := 0; i < 1000; i++ {
_ = compFn(component.NewStatusEvent(component.StatusRecoverableError))
_ = compFn(component.NewStatusEvent(component.StatusOK))
compFn(component.NewStatusEvent(component.StatusRecoverableError))
compFn(component.NewStatusEvent(component.StatusOK))
}
wg.Done()
}()
@ -255,15 +261,19 @@ func TestStatusFuncsConcurrent(t *testing.T) {
func TestReporterReady(t *testing.T) {
statusFunc := func(*component.InstanceID, *component.StatusEvent) {}
rep := NewReporter(statusFunc)
var err error
rep := NewReporter(statusFunc,
func(e error) {
err = e
})
id := &component.InstanceID{}
err := rep.ReportComponentStatus(id, component.NewStatusEvent(component.StatusStarting))
require.ErrorIs(t, err, errStatusNotReady)
rep.ReportStatus(id, component.NewStatusEvent(component.StatusStarting))
require.ErrorIs(t, err, ErrStatusNotReady)
rep.Ready()
err = rep.ReportComponentStatus(id, component.NewStatusEvent(component.StatusStarting))
err = nil
rep.ReportStatus(id, component.NewStatusEvent(component.StatusStarting))
require.NoError(t, err)
}
@ -324,18 +334,19 @@ func TestReportComponentOKIfStarting(t *testing.T) {
func(_ *component.InstanceID, ev *component.StatusEvent) {
receivedStatuses = append(receivedStatuses, ev.Status())
},
func(err error) {
require.NoError(t, err)
},
)
rep.Ready()
id := &component.InstanceID{}
for _, status := range tc.initialStatuses {
err := rep.ReportComponentStatus(id, component.NewStatusEvent(status))
require.NoError(t, err)
rep.ReportStatus(id, component.NewStatusEvent(status))
}
err := rep.ReportComponentOKIfStarting(id)
rep.ReportOKIfStarting(id)
require.NoError(t, err)
require.Equal(t, tc.expectedStatuses, receivedStatuses)
})
}

View File

@ -5,6 +5,7 @@ package service // import "go.opentelemetry.io/collector/service"
import (
"context"
"errors"
"fmt"
"runtime"
@ -113,7 +114,12 @@ func New(ctx context.Context, set Settings, cfg Config) (*Service, error) {
MetricsLevel: cfg.Telemetry.Metrics.Level,
// Construct telemetry attributes from build info and config's resource attributes.
Resource: pcommonRes,
Status: status.NewReporter(srv.host.notifyComponentStatusChange),
Status: status.NewReporter(srv.host.notifyComponentStatusChange, func(err error) {
if errors.Is(err, status.ErrStatusNotReady) {
srv.telemetry.Logger().Warn("Invalid transition", zap.Error(err))
}
// ignore other errors as they represent invalid state transitions and are considered benign.
}),
}
if err = srv.telemetryInitializer.init(res, srv.telemetrySettings, cfg.Telemetry, set.AsyncErrorChannel); err != nil {