Skip to content

Commit 4565692

Browse files
authored
Instrument obsreport.Scraper (#6460)
* Instrument obsreport.Scraper (#19) * instrument obsreport.scraper metrics with otel go * add changelog * update API to use MustNewScraper * fix typo add testing for checkScraperMetrics * remove accidental merge conflict references * fix references after rebase * address review comments * remove unneded log * add newlines * run gofmt * fix indenting
1 parent 1d4100c commit 4565692

File tree

9 files changed

+178
-46
lines changed

9 files changed

+178
-46
lines changed

.chloggen/obsreport-scraper.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
5+
component: obsreport
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: "Instrument `obsreport.Scraper` metrics with otel-go"
9+
10+
# One or more tracking issues or pull requests related to the change
11+
issues: [6460]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext:

internal/obsreportconfig/obsreportconfig.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,7 @@ func allViews() []*view.View {
7171
views = append(views, receiverViews()...)
7272

7373
// Scraper views.
74-
measures = []*stats.Int64Measure{
75-
obsmetrics.ScraperScrapedMetricPoints,
76-
obsmetrics.ScraperErroredMetricPoints,
77-
}
78-
tagKeys = []tag.Key{obsmetrics.TagKeyReceiver, obsmetrics.TagKeyScraper}
79-
views = append(views, genViews(measures, tagKeys, view.Sum())...)
74+
views = append(views, scraperViews()...)
8075

8176
// Exporter views.
8277
measures = []*stats.Int64Measure{
@@ -136,6 +131,20 @@ func receiverViews() []*view.View {
136131
return genViews(measures, tagKeys, view.Sum())
137132
}
138133

134+
func scraperViews() []*view.View {
135+
if featuregate.GetRegistry().IsEnabled(UseOtelForInternalMetricsfeatureGateID) {
136+
return nil
137+
}
138+
139+
measures := []*stats.Int64Measure{
140+
obsmetrics.ScraperScrapedMetricPoints,
141+
obsmetrics.ScraperErroredMetricPoints,
142+
}
143+
tagKeys := []tag.Key{obsmetrics.TagKeyReceiver, obsmetrics.TagKeyScraper}
144+
145+
return genViews(measures, tagKeys, view.Sum())
146+
}
147+
139148
func genViews(
140149
measures []*stats.Int64Measure,
141150
tagKeys []tag.Key,

obsreport/obsreport_scraper.go

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,40 @@ import (
2121
"go.opencensus.io/stats"
2222
"go.opencensus.io/tag"
2323
"go.opentelemetry.io/otel/attribute"
24+
"go.opentelemetry.io/otel/metric/instrument"
25+
"go.opentelemetry.io/otel/metric/instrument/syncint64"
26+
"go.opentelemetry.io/otel/metric/unit"
2427
"go.opentelemetry.io/otel/trace"
28+
"go.uber.org/multierr"
29+
"go.uber.org/zap"
2530

2631
"go.opentelemetry.io/collector/component"
2732
"go.opentelemetry.io/collector/config/configtelemetry"
33+
"go.opentelemetry.io/collector/featuregate"
34+
"go.opentelemetry.io/collector/internal/obsreportconfig"
2835
"go.opentelemetry.io/collector/internal/obsreportconfig/obsmetrics"
2936
"go.opentelemetry.io/collector/receiver/scrapererror"
3037
)
3138

39+
var (
40+
scraperName = "scraper"
41+
scraperScope = scopeName + nameSep + scraperName
42+
)
43+
3244
// Scraper is a helper to add observability to a component.Scraper.
3345
type Scraper struct {
3446
level configtelemetry.Level
3547
receiverID component.ID
3648
scraper component.ID
3749
mutators []tag.Mutator
3850
tracer trace.Tracer
51+
52+
logger *zap.Logger
53+
54+
useOtelForMetrics bool
55+
otelAttrs []attribute.KeyValue
56+
scrapedMetricsPoints syncint64.Counter
57+
erroredMetricsPoints syncint64.Counter
3958
}
4059

4160
// ScraperSettings are settings for creating a Scraper.
@@ -47,25 +66,67 @@ type ScraperSettings struct {
4766

4867
// NewScraper creates a new Scraper.
4968
func NewScraper(cfg ScraperSettings) (*Scraper, error) {
50-
return &Scraper{
69+
return newScraper(cfg, featuregate.GetRegistry())
70+
}
71+
72+
// Deprecated: [v0.65.0] use NewScraper.
73+
func MustNewScraper(cfg ScraperSettings) *Scraper {
74+
scr, err := newScraper(cfg, featuregate.GetRegistry())
75+
if err != nil {
76+
panic(err)
77+
}
78+
79+
return scr
80+
}
81+
82+
func newScraper(cfg ScraperSettings, registry *featuregate.Registry) (*Scraper, error) {
83+
scraper := &Scraper{
5184
level: cfg.ReceiverCreateSettings.TelemetrySettings.MetricsLevel,
5285
receiverID: cfg.ReceiverID,
5386
scraper: cfg.Scraper,
5487
mutators: []tag.Mutator{
5588
tag.Upsert(obsmetrics.TagKeyReceiver, cfg.ReceiverID.String(), tag.WithTTL(tag.TTLNoPropagation)),
5689
tag.Upsert(obsmetrics.TagKeyScraper, cfg.Scraper.String(), tag.WithTTL(tag.TTLNoPropagation))},
5790
tracer: cfg.ReceiverCreateSettings.TracerProvider.Tracer(cfg.Scraper.String()),
58-
}, nil
91+
92+
logger: cfg.ReceiverCreateSettings.Logger,
93+
useOtelForMetrics: registry.IsEnabled(obsreportconfig.UseOtelForInternalMetricsfeatureGateID),
94+
otelAttrs: []attribute.KeyValue{
95+
attribute.String(obsmetrics.ReceiverKey, cfg.ReceiverID.String()),
96+
attribute.String(obsmetrics.ScraperKey, cfg.Scraper.String()),
97+
},
98+
}
99+
100+
if err := scraper.createOtelMetrics(cfg); err != nil {
101+
return nil, err
102+
}
103+
104+
return scraper, nil
59105
}
60106

61-
// Deprecated: [v0.65.0] use NewScraper.
62-
func MustNewScraper(cfg ScraperSettings) *Scraper {
63-
scrap, err := NewScraper(cfg)
64-
if err != nil {
65-
panic(err)
107+
func (s *Scraper) createOtelMetrics(cfg ScraperSettings) error {
108+
if !s.useOtelForMetrics {
109+
return nil
66110
}
111+
meter := cfg.ReceiverCreateSettings.MeterProvider.Meter(scraperScope)
112+
113+
var errors, err error
67114

68-
return scrap
115+
s.scrapedMetricsPoints, err = meter.SyncInt64().Counter(
116+
obsmetrics.ScraperPrefix+obsmetrics.ScrapedMetricPointsKey,
117+
instrument.WithDescription("Number of metric points successfully scraped."),
118+
instrument.WithUnit(unit.Dimensionless),
119+
)
120+
errors = multierr.Append(errors, err)
121+
122+
s.erroredMetricsPoints, err = meter.SyncInt64().Counter(
123+
obsmetrics.ScraperPrefix+obsmetrics.ErroredMetricPointsKey,
124+
instrument.WithDescription("Number of metric points that were unable to be scraped."),
125+
instrument.WithUnit(unit.Dimensionless),
126+
)
127+
errors = multierr.Append(errors, err)
128+
129+
return errors
69130
}
70131

71132
// StartMetricsOp is called when a scrape operation is started. The
@@ -100,10 +161,7 @@ func (s *Scraper) EndMetricsOp(
100161
span := trace.SpanFromContext(scraperCtx)
101162

102163
if s.level != configtelemetry.LevelNone {
103-
stats.Record(
104-
scraperCtx,
105-
obsmetrics.ScraperScrapedMetricPoints.M(int64(numScrapedMetrics)),
106-
obsmetrics.ScraperErroredMetricPoints.M(int64(numErroredMetrics)))
164+
s.recordMetrics(scraperCtx, numScrapedMetrics, numErroredMetrics)
107165
}
108166

109167
// end span according to errors
@@ -118,3 +176,15 @@ func (s *Scraper) EndMetricsOp(
118176

119177
span.End()
120178
}
179+
180+
func (s *Scraper) recordMetrics(scraperCtx context.Context, numScrapedMetrics, numErroredMetrics int) {
181+
if s.useOtelForMetrics {
182+
s.scrapedMetricsPoints.Add(scraperCtx, int64(numScrapedMetrics), s.otelAttrs...)
183+
s.erroredMetricsPoints.Add(scraperCtx, int64(numErroredMetrics), s.otelAttrs...)
184+
} else { // OC for metrics
185+
stats.Record(
186+
scraperCtx,
187+
obsmetrics.ScraperScrapedMetricPoints.M(int64(numScrapedMetrics)),
188+
obsmetrics.ScraperErroredMetricPoints.M(int64(numErroredMetrics)))
189+
}
190+
}

obsreport/obsreport_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ type testParams struct {
5252
err error
5353
}
5454

55-
func testTelemetry(t *testing.T, testFunc func(tt obsreporttest.TestTelemetry, registry *featuregate.Registry)) {
55+
func testTelemetry(t *testing.T, testFunc func(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry)) {
5656
t.Run("WithOC", func(t *testing.T) {
5757
tt, err := obsreporttest.SetupTelemetry()
5858
require.NoError(t, err)
5959
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
6060

61-
testFunc(tt, featuregate.NewRegistry())
61+
testFunc(t, tt, featuregate.NewRegistry())
6262
})
6363

6464
t.Run("WithOTel", func(t *testing.T) {
@@ -70,12 +70,12 @@ func testTelemetry(t *testing.T, testFunc func(tt obsreporttest.TestTelemetry, r
7070
require.NoError(t, err)
7171
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
7272

73-
testFunc(tt, registry)
73+
testFunc(t, tt, registry)
7474
})
7575
}
7676

7777
func TestReceiveTraceDataOp(t *testing.T) {
78-
testTelemetry(t, func(tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
78+
testTelemetry(t, func(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
7979
parentCtx, parentSpan := tt.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
8080
defer parentSpan.End()
8181

@@ -122,7 +122,7 @@ func TestReceiveTraceDataOp(t *testing.T) {
122122
}
123123

124124
func TestReceiveLogsOp(t *testing.T) {
125-
testTelemetry(t, func(tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
125+
testTelemetry(t, func(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
126126
parentCtx, parentSpan := tt.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
127127
defer parentSpan.End()
128128

@@ -170,7 +170,7 @@ func TestReceiveLogsOp(t *testing.T) {
170170
}
171171

172172
func TestReceiveMetricsOp(t *testing.T) {
173-
testTelemetry(t, func(tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
173+
testTelemetry(t, func(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
174174
parentCtx, parentSpan := tt.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
175175
defer parentSpan.End()
176176

@@ -219,10 +219,10 @@ func TestReceiveMetricsOp(t *testing.T) {
219219
}
220220

221221
func TestScrapeMetricsDataOp(t *testing.T) {
222-
tt, err := obsreporttest.SetupTelemetry()
223-
require.NoError(t, err)
224-
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
222+
testTelemetry(t, testScrapeMetricsDataOp)
223+
}
225224

225+
func testScrapeMetricsDataOp(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
226226
parentCtx, parentSpan := tt.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
227227
defer parentSpan.End()
228228

@@ -232,12 +232,12 @@ func TestScrapeMetricsDataOp(t *testing.T) {
232232
{items: 15, err: nil},
233233
}
234234
for i := range params {
235-
scrp, serr := NewScraper(ScraperSettings{
235+
scrp, err := newScraper(ScraperSettings{
236236
ReceiverID: receiver,
237237
Scraper: scraper,
238238
ReceiverCreateSettings: tt.ToReceiverCreateSettings(),
239-
})
240-
require.NoError(t, serr)
239+
}, registry)
240+
require.NoError(t, err)
241241
ctx := scrp.StartMetricsOp(parentCtx)
242242
assert.NotNil(t, ctx)
243243
scrp.EndMetricsOp(ctx, params[i].items, params[i].err)
@@ -278,7 +278,7 @@ func TestScrapeMetricsDataOp(t *testing.T) {
278278
}
279279

280280
func TestExportTraceDataOp(t *testing.T) {
281-
testTelemetry(t, func(tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
281+
testTelemetry(t, func(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
282282
parentCtx, parentSpan := tt.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
283283
defer parentSpan.End()
284284

@@ -327,7 +327,7 @@ func TestExportTraceDataOp(t *testing.T) {
327327
}
328328

329329
func TestExportMetricsOp(t *testing.T) {
330-
testTelemetry(t, func(tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
330+
testTelemetry(t, func(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
331331
parentCtx, parentSpan := tt.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
332332
defer parentSpan.End()
333333

@@ -376,7 +376,7 @@ func TestExportMetricsOp(t *testing.T) {
376376
}
377377

378378
func TestExportLogsOp(t *testing.T) {
379-
testTelemetry(t, func(tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
379+
testTelemetry(t, func(t *testing.T, tt obsreporttest.TestTelemetry, registry *featuregate.Registry) {
380380
parentCtx, parentSpan := tt.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
381381
defer parentSpan.End()
382382

obsreport/obsreporttest/obsreporttest.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,8 @@ func CheckReceiverMetrics(tts TestTelemetry, receiver component.ID, protocol str
205205

206206
// CheckScraperMetrics checks that for the current exported values for metrics scraper metrics match given values.
207207
// When this function is called it is required to also call SetupTelemetry as first thing.
208-
func CheckScraperMetrics(_ TestTelemetry, receiver component.ID, scraper component.ID, scrapedMetricPoints, erroredMetricPoints int64) error {
209-
scraperTags := tagsForScraperView(receiver, scraper)
210-
return multierr.Combine(
211-
checkValueForView(scraperTags, scrapedMetricPoints, "scraper/scraped_metric_points"),
212-
checkValueForView(scraperTags, erroredMetricPoints, "scraper/errored_metric_points"))
208+
func CheckScraperMetrics(tts TestTelemetry, receiver component.ID, scraper component.ID, scrapedMetricPoints, erroredMetricPoints int64) error {
209+
return tts.otelPrometheusChecker.checkScraperMetrics(receiver, scraper, scrapedMetricPoints, erroredMetricPoints)
213210
}
214211

215212
// checkValueForView checks that for the current exported value in the view with the given name
@@ -237,14 +234,6 @@ func checkValueForView(wantTags []tag.Tag, value int64, vName string) error {
237234
return fmt.Errorf("[%s]: could not find tags, wantTags: %s in rows %v", vName, wantTags, rows)
238235
}
239236

240-
// tagsForScraperView returns the tags that are needed for the scraper views.
241-
func tagsForScraperView(receiver component.ID, scraper component.ID) []tag.Tag {
242-
return []tag.Tag{
243-
{Key: receiverTag, Value: receiver.String()},
244-
{Key: scraperTag, Value: scraper.String()},
245-
}
246-
}
247-
248237
// tagsForProcessorView returns the tags that are needed for the processor views.
249238
func tagsForProcessorView(processor component.ID) []tag.Tag {
250239
return []tag.Tag{

obsreport/obsreporttest/obsreporttest_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,32 @@ const (
3232
)
3333

3434
var (
35+
scraper = component.NewID("fakeScraper")
3536
receiver = component.NewID("fakeReicever")
3637
exporter = component.NewID("fakeExporter")
3738
)
3839

40+
func TestCheckScraperMetricsViews(t *testing.T) {
41+
tt, err := obsreporttest.SetupTelemetry()
42+
require.NoError(t, err)
43+
t.Cleanup(func() { require.NoError(t, tt.Shutdown(context.Background())) })
44+
45+
s, err := obsreport.NewScraper(obsreport.ScraperSettings{
46+
ReceiverID: receiver,
47+
Scraper: scraper,
48+
ReceiverCreateSettings: tt.ToReceiverCreateSettings(),
49+
})
50+
require.NoError(t, err)
51+
ctx := s.StartMetricsOp(context.Background())
52+
require.NotNil(t, ctx)
53+
s.EndMetricsOp(ctx, 7, nil)
54+
55+
assert.NoError(t, obsreporttest.CheckScraperMetrics(tt, receiver, scraper, 7, 0))
56+
assert.Error(t, obsreporttest.CheckScraperMetrics(tt, receiver, scraper, 7, 7))
57+
assert.Error(t, obsreporttest.CheckScraperMetrics(tt, receiver, scraper, 0, 0))
58+
assert.Error(t, obsreporttest.CheckScraperMetrics(tt, receiver, scraper, 0, 7))
59+
}
60+
3961
func TestCheckReceiverTracesViews(t *testing.T) {
4062
tt, err := obsreporttest.SetupTelemetry()
4163
require.NoError(t, err)

obsreport/obsreporttest/otelprometheuschecker.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ type prometheusChecker struct {
3434
promHandler http.Handler
3535
}
3636

37+
func (pc *prometheusChecker) checkScraperMetrics(receiver component.ID, scraper component.ID, scrapedMetricPoints, erroredMetricPoints int64) error {
38+
scraperAttrs := attributesForScraperMetrics(receiver, scraper)
39+
return multierr.Combine(
40+
pc.checkCounter("scraper_scraped_metric_points", scrapedMetricPoints, scraperAttrs),
41+
pc.checkCounter("scraper_errored_metric_points", erroredMetricPoints, scraperAttrs))
42+
}
43+
3744
func (pc *prometheusChecker) checkReceiverTraces(receiver component.ID, protocol string, acceptedSpans, droppedSpans int64) error {
3845
receiverAttrs := attributesForReceiverMetrics(receiver, protocol)
3946
return multierr.Combine(
@@ -145,6 +152,13 @@ func fetchPrometheusMetrics(handler http.Handler) (map[string]*io_prometheus_cli
145152
return parser.TextToMetricFamilies(rr.Body)
146153
}
147154

155+
func attributesForScraperMetrics(receiver component.ID, scraper component.ID) []attribute.KeyValue {
156+
return []attribute.KeyValue{
157+
attribute.String(receiverTag.Name(), receiver.String()),
158+
attribute.String(scraperTag.Name(), scraper.String()),
159+
}
160+
}
161+
148162
// attributesForReceiverMetrics returns the attributes that are needed for the receiver metrics.
149163
func attributesForReceiverMetrics(receiver component.ID, transport string) []attribute.KeyValue {
150164
return []attribute.KeyValue{

0 commit comments

Comments
 (0)