Files
de8a6ce125 [chore] [scraperhelper] Thread context and error through scrapeFunc (#15152)
#### Description

Change the scraper controller's scrapeFunc signature to accept a context
and return an error. This is preparation for scraper controller
extensions (#12449).

NewController wraps the user-supplied scrapeFunc to apply Timeout to the
incoming context when non-zero, so scrapeMetrics/scrapeLogs/
scrapeProfiles no longer build their own context, and WithScrapeContext
is removed.

Scraper and consumer errors are now returned. The timer path discards
the returned error, preserving existing behavior.


#### Link to tracking issue

Relates to
https://github.com/open-telemetry/opentelemetry-collector/issues/12449

#### Testing

Unit tests updated

#### Documentation

N/A

---------

Co-authored-by: Florian Lehner <[email protected]>
Co-authored-by: Alex Boten <[email protected]>
2026-04-23 20:58:37 +00:00

143 lines
3.5 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// package controller provides functionality used in scraperhelper and xscraperhelper.
package controller // import "go.opentelemetry.io/collector/scraper/scraperhelper/internal/controller"
import (
"context"
"sync"
"time"
"go.uber.org/multierr"
"go.uber.org/zap"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/receiver"
"go.opentelemetry.io/collector/receiver/receiverhelper"
"go.opentelemetry.io/collector/scraper"
)
type Controller[T component.Component] struct {
collectionInterval time.Duration
initialDelay time.Duration
Timeout time.Duration
Scrapers []T
scrapeFunc func(context.Context, *Controller[T]) error
tickerCh <-chan time.Time
done chan struct{}
wg sync.WaitGroup
Obsrecv *receiverhelper.ObsReport
}
func NewController[T component.Component](
cfg *ControllerConfig,
rSet receiver.Settings,
scrapers []T,
scrapeFunc func(context.Context, *Controller[T]) error,
tickerCh <-chan time.Time,
) (*Controller[T], error) {
obsrecv, err := receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{
ReceiverID: rSet.ID,
Transport: "",
ReceiverCreateSettings: rSet,
})
if err != nil {
return nil, err
}
cs := &Controller[T]{
collectionInterval: cfg.CollectionInterval,
initialDelay: cfg.InitialDelay,
Timeout: cfg.Timeout,
Scrapers: scrapers,
scrapeFunc: scrapeFunc,
done: make(chan struct{}),
tickerCh: tickerCh,
Obsrecv: obsrecv,
}
if cfg.Timeout > 0 {
cs.scrapeFunc = func(ctx context.Context, c *Controller[T]) error {
ctx, cancel := context.WithTimeout(ctx, cfg.Timeout)
defer cancel()
return scrapeFunc(ctx, c)
}
}
return cs, nil
}
// Start the receiver, invoked during service start.
func (sc *Controller[T]) Start(ctx context.Context, host component.Host) error {
for _, scrp := range sc.Scrapers {
if err := scrp.Start(ctx, host); err != nil {
return err
}
}
sc.startScraping()
return nil
}
// Shutdown the receiver, invoked during service shutdown.
func (sc *Controller[T]) Shutdown(ctx context.Context) error {
// Signal the goroutine to stop.
close(sc.done)
sc.wg.Wait()
var errs error
for _, scrp := range sc.Scrapers {
errs = multierr.Append(errs, scrp.Shutdown(ctx))
}
return errs
}
// startScraping initiates a ticker that calls Scrape based on the configured
// collection interval.
func (sc *Controller[T]) startScraping() {
sc.wg.Go(func() {
if sc.initialDelay > 0 {
select {
case <-time.After(sc.initialDelay):
case <-sc.done:
return
}
}
if sc.tickerCh == nil {
ticker := time.NewTicker(sc.collectionInterval)
defer ticker.Stop()
sc.tickerCh = ticker.C
}
// Call scrape method during initialization to ensure
// that scrapers start from when the component starts
// instead of waiting for the full duration to start.
_ = sc.scrapeFunc(context.Background(), sc)
for {
select {
case <-sc.tickerCh:
_ = sc.scrapeFunc(context.Background(), sc)
case <-sc.done:
return
}
}
})
}
func GetSettings(sType component.Type, rSet receiver.Settings) scraper.Settings {
id := component.NewID(sType)
telemetry := rSet.TelemetrySettings
telemetry.Logger = telemetry.Logger.With(zap.String("scraper", id.String()))
return scraper.Settings{
ID: id,
TelemetrySettings: telemetry,
BuildInfo: rSet.BuildInfo,
}
}