Skip to content

Commit

Permalink
Add a new scraper package to replace scraperhelper
Browse files Browse the repository at this point in the history
Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com>
  • Loading branch information
bogdandrutu committed Nov 12, 2024
1 parent 7b31c96 commit 8d188b2
Show file tree
Hide file tree
Showing 13 changed files with 579 additions and 0 deletions.
1 change: 1 addition & 0 deletions scraper/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../Makefile.Common
12 changes: 12 additions & 0 deletions scraper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# General Information

A scraper defines how to connect and scrape telemetry data from an external source.

<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Stability | [development]: metrics |
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Apkg%2F%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Apkg%2F) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Apkg%2F%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Apkg%2F) |

[development]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#development
<!-- end autogenerated section -->
7 changes: 7 additions & 0 deletions scraper/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

//go:generate mdatagen metadata.yaml

// Package scraper allows to define pull based receivers that can be configured using the scraperreceiver.
package scraper // import "go.opentelemetry.io/collector/scraper"
118 changes: 118 additions & 0 deletions scraper/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package scraper // import "go.opentelemetry.io/collector/scraper"

import (
"context"
"fmt"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/pipeline"
)

// Settings configures scraper creators.
type Settings struct {
// ID returns the ID of the component that will be created.
ID component.ID

component.TelemetrySettings

// BuildInfo can be used by components for informational purposes.
BuildInfo component.BuildInfo
}

// Factory is factory interface for scrapers.
//
// This interface cannot be directly implemented. Implementations must
// use the NewFactory to implement it.
type Factory interface {
component.Factory

// CreateMetrics creates a Metrics scraper based on this config.
// If the scraper type does not support metrics,
// this function returns the error [pipeline.ErrSignalNotSupported].
// Implementers can assume `next` is never nil.
CreateMetrics(ctx context.Context, set Settings, cfg component.Config) (Metrics, error)

// MetricsStability gets the stability level of the Metrics scraper.
MetricsStability() component.StabilityLevel

unexportedFactoryFunc()
}

// FactoryOption apply changes to Options.
type FactoryOption interface {
// applyOption applies the option.
applyOption(o *factory)
}

var _ FactoryOption = (*factoryOptionFunc)(nil)

// factoryOptionFunc is a FactoryOption created through a function.
type factoryOptionFunc func(*factory)

func (f factoryOptionFunc) applyOption(o *factory) {
f(o)
}

type factory struct {
cfgType component.Type
component.CreateDefaultConfigFunc
CreateMetricsFunc
metricsStabilityLevel component.StabilityLevel
}

func (f *factory) Type() component.Type {
return f.cfgType
}

func (f *factory) unexportedFactoryFunc() {}

func (f *factory) MetricsStability() component.StabilityLevel {
return f.metricsStabilityLevel
}

// CreateMetricsFunc is the equivalent of Factory.CreateMetrics().
type CreateMetricsFunc func(context.Context, Settings, component.Config) (Metrics, error)

// CreateMetrics implements Factory.CreateMetrics.
func (f CreateMetricsFunc) CreateMetrics(ctx context.Context, set Settings, cfg component.Config) (Metrics, error) {
if f == nil {
return nil, pipeline.ErrSignalNotSupported
}
return f(ctx, set, cfg)
}

// WithMetrics overrides the default "error not supported" implementation for CreateMetrics and the default "undefined" stability level.
func WithMetrics(createMetrics CreateMetricsFunc, sl component.StabilityLevel) FactoryOption {
return factoryOptionFunc(func(o *factory) {
o.metricsStabilityLevel = sl
o.CreateMetricsFunc = createMetrics
})
}

// NewFactory returns a Factory.
func NewFactory(cfgType component.Type, createDefaultConfig component.CreateDefaultConfigFunc, options ...FactoryOption) Factory {
f := &factory{
cfgType: cfgType,
CreateDefaultConfigFunc: createDefaultConfig,
}
for _, opt := range options {
opt.applyOption(f)
}
return f
}

// MakeFactoryMap takes a list of receiver factories and returns a map with factory type as keys.
// It returns a non-nil error when there are factories with duplicate type.
func MakeFactoryMap(factories ...Factory) (map[component.Type]Factory, error) {
fMap := map[component.Type]Factory{}
for _, f := range factories {
if _, ok := fMap[f.Type()]; ok {
return fMap, fmt.Errorf("duplicate scraper factory %q", f.Type())
}
fMap[f.Type()] = f
}
return fMap, nil
}
91 changes: 91 additions & 0 deletions scraper/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package scraper

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/pipeline"
)

var testType = component.MustNewType("test")

func nopSettings() Settings {
return Settings{
ID: component.NewID(testType),
TelemetrySettings: componenttest.NewNopTelemetrySettings()}
}

func TestNewFactory(t *testing.T) {
defaultCfg := struct{}{}
f := NewFactory(
testType,
func() component.Config { return &defaultCfg })
assert.EqualValues(t, testType, f.Type())
assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig())
_, err := f.CreateMetrics(context.Background(), nopSettings(), &defaultCfg)
require.ErrorIs(t, err, pipeline.ErrSignalNotSupported)
}

func TestNewFactoryWithOptions(t *testing.T) {
var testType = component.MustNewType("test")
defaultCfg := struct{}{}
f := NewFactory(
testType,
func() component.Config { return &defaultCfg },
WithMetrics(createMetrics, component.StabilityLevelAlpha))
assert.EqualValues(t, testType, f.Type())
assert.EqualValues(t, &defaultCfg, f.CreateDefaultConfig())

assert.Equal(t, component.StabilityLevelAlpha, f.MetricsStability())
_, err := f.CreateMetrics(context.Background(), Settings{}, &defaultCfg)
require.NoError(t, err)
}

func TestMakeFactoryMap(t *testing.T) {
type testCase struct {
name string
in []Factory
out map[component.Type]Factory
}

p1 := NewFactory(component.MustNewType("p1"), nil)
p2 := NewFactory(component.MustNewType("p2"), nil)
testCases := []testCase{
{
name: "different names",
in: []Factory{p1, p2},
out: map[component.Type]Factory{
p1.Type(): p1,
p2.Type(): p2,
},
},
{
name: "same name",
in: []Factory{p1, p2, NewFactory(component.MustNewType("p1"), nil)},
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
out, err := MakeFactoryMap(tt.in...)
if tt.out == nil {
assert.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.out, out)
})
}
}

func createMetrics(context.Context, Settings, component.Config) (Metrics, error) {
return NewMetrics(context.Background(), nopSettings(), newTestScrapeMetricsFunc(nil))
}
13 changes: 13 additions & 0 deletions scraper/generated_package_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions scraper/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module go.opentelemetry.io/collector/scraper

go 1.22.0

require (
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/collector/component v0.113.0
go.opentelemetry.io/collector/pdata v1.19.0
go.opentelemetry.io/collector/pipeline v0.113.0
go.uber.org/goleak v1.3.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/collector/config/configtelemetry v0.113.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/sdk v1.31.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.17.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
104 changes: 104 additions & 0 deletions scraper/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 8d188b2

Please sign in to comment.