mirror of
https://github.com/hanzoai/telemetry.git
synced 2026-10-30 20:01:49 +00:00
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description This PR adds the `scraperID` to the error logs for scrapers (metrics, logs, and profiles). Why is this important? Currently if an error occurs in any of the scrapers in `hostmetricsreceiver/internal/scraper/...` there is no info about the which scraper errored in the logs: ```bash 2026-01-21T10:42:42.830Z error [email protected].0/obs_metrics.go:61 Error scraping metrics {"resource": {"service.instance.id": "63071771-056d-40ca-83e3-585dc1a00d57", "service.name": "otelcontribcol", "service.version": "0.144.0-dev"}, "otelcol.component.id": "hostmetrics", "otelcol.component.kind": "receiver", "otelcol.signal": "metrics", "error": "forced test error for scraper identification"} ``` The update in this PR add's the scraper ID (In this case the memory scraper errored) to the logs which then gives us: ```bash 2026-01-21T10:23:52.759Z error scraperhelper/obs_metrics.go:62 Error scraping metrics {"resource": {"service.instance.id": "7137f19d-706f-4095-8367-0d68b2732402", "service.name": "otelcontribcol", "service.version": "0.144.0-dev"}, "otelcol.component.id": "hostmetrics", "otelcol.component.kind": "receiver", "otelcol.signal": "metrics", "scraper": "memory", "error": "forced test error for scraper identification"} ``` <!-- Issue number if applicable --> #### Link to tracking issue Fixes Issue in [collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35814#issuecomment-2676316294). <!--Describe what testing was performed and which tests were added.--> #### Testing **Metrics scrapers:** Locally tested with `collector-contrib` by updating the mod file... **Logs scrapers:** The same pattern was applied. Logs scrapers (MySQL, PostgreSQL, Oracle, SQL Server receivers) require database connections for integration testing. **Profiles scrapers (xscraperhelper):** The same pattern was applied. Note: There are currently no profiles scrapers in collector-contrib to test against. **Metrics scraper local test**: Locally this was updated and included in the `collector-contrib` via updating the mod file. An error was forced in the `hostmetricsreceiver/internal/scraper/...` both memory and cpu, we can now see which scraper component had an error: ```bash 2026-01-21T11:51:34.176Z error scraperhelper/obs_metrics.go:61 Error scraping metrics {"resource": {"service.instance.id": "e6832705-bc54-43dd-9b1f-0c06d58b7ead", "service.name": "otelcontribcol", "service.version": "0.144.0-dev"}, "otelcol.component.id": "hostmetrics", "otelcol.component.kind": "receiver", "otelcol.signal": "metrics", "scraper": "memory", "error": "forced test error for scraper identification"} go.opentelemetry.io/collector/scraper/scraperhelper.wrapObsMetrics.func1 /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/obs_metrics.go:61 go.opentelemetry.io/collector/scraper.ScrapeMetricsFunc.ScrapeMetrics /home/dos/go/pkg/mod/go.opentelemetry.io/collector/[email protected].0/metrics.go:24 go.opentelemetry.io/collector/scraper/scraperhelper.scrapeMetrics /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/controller.go:167 go.opentelemetry.io/collector/scraper/scraperhelper.NewMetricsController.func1 /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/controller.go:139 go.opentelemetry.io/collector/scraper/scraperhelper/internal/controller.(*Controller[...]).startScraping.func1 /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/internal/controller/controller.go:118 2026-01-21T11:51:34.177Z error scraperhelper/obs_metrics.go:61 Error scraping metrics {"resource": {"service.instance.id": "e6832705-bc54-43dd-9b1f-0c06d58b7ead", "service.name": "otelcontribcol", "service.version": "0.144.0-dev"}, "otelcol.component.id": "hostmetrics", "otelcol.component.kind": "receiver", "otelcol.signal": "metrics", "scraper": "cpu", "error": "forced test error for scraper identification"} go.opentelemetry.io/collector/scraper/scraperhelper.wrapObsMetrics.func1 /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/obs_metrics.go:61 go.opentelemetry.io/collector/scraper.ScrapeMetricsFunc.ScrapeMetrics /home/dos/go/pkg/mod/go.opentelemetry.io/collector/[email protected].0/metrics.go:24 go.opentelemetry.io/collector/scraper/scraperhelper.scrapeMetrics /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/controller.go:167 go.opentelemetry.io/collector/scraper/scraperhelper.NewMetricsController.func1 /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/controller.go:139 go.opentelemetry.io/collector/scraper/scraperhelper/internal/controller.(*Controller[...]).startScraping.func1 /home/dos/Documents/opentelemetry-collector/scraper/scraperhelper/internal/controller/controller.go:118 2026-01-21T11:51:34.199Z info Metrics {"resource": {"service.instance.id": "e6832705-bc54-43dd-9b1f-0c06d58b7ead", "service.name": "otelcontribcol", "service.version": "0.144.0-dev"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "metrics", "resource metrics": 1, "metrics": 1, "data points": 1} 2026-01-21T11:51:34.199Z info ResourceMetrics #0 Resource SchemaURL: https://opentelemetry.io/schemas/1.9.0 ``` Closes https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35814
155 lines
5.6 KiB
Go
155 lines
5.6 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package scraperhelper
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/codes"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata"
|
|
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zaptest/observer"
|
|
|
|
"go.opentelemetry.io/collector/component"
|
|
"go.opentelemetry.io/collector/component/componenttest"
|
|
"go.opentelemetry.io/collector/pdata/plog"
|
|
"go.opentelemetry.io/collector/pdata/testdata"
|
|
"go.opentelemetry.io/collector/receiver"
|
|
"go.opentelemetry.io/collector/scraper"
|
|
"go.opentelemetry.io/collector/scraper/scraperhelper/internal/controller"
|
|
"go.opentelemetry.io/collector/scraper/scraperhelper/internal/metadatatest"
|
|
)
|
|
|
|
func TestScrapeLogsDataOp(t *testing.T) {
|
|
tel := componenttest.NewTelemetry()
|
|
t.Cleanup(func() { require.NoError(t, tel.Shutdown(context.Background())) })
|
|
|
|
set := tel.NewTelemetrySettings()
|
|
|
|
parentCtx, parentSpan := set.TracerProvider.Tracer("test").Start(context.Background(), t.Name())
|
|
defer parentSpan.End()
|
|
|
|
params := []testParams{
|
|
{items: 23, err: partialErrFake},
|
|
{items: 29, err: errFake},
|
|
{items: 15, err: nil},
|
|
}
|
|
for i := range params {
|
|
sm, err := scraper.NewLogs(func(context.Context) (plog.Logs, error) {
|
|
return testdata.GenerateLogs(params[i].items), params[i].err
|
|
})
|
|
require.NoError(t, err)
|
|
sf, err := wrapObsLogs(sm, receiverID, scraperID, set)
|
|
require.NoError(t, err)
|
|
_, err = sf.ScrapeLogs(parentCtx)
|
|
require.ErrorIs(t, err, params[i].err)
|
|
}
|
|
|
|
spans := tel.SpanRecorder.Ended()
|
|
require.Len(t, spans, len(params))
|
|
|
|
var scrapedLogRecords, erroredLogRecords int
|
|
for i, span := range spans {
|
|
assert.Equal(t, "scraper/"+scraperID.String()+"/ScrapeLogs", span.Name())
|
|
switch {
|
|
case params[i].err == nil:
|
|
scrapedLogRecords += params[i].items
|
|
require.Contains(t, span.Attributes(), attribute.Int64(scrapedLogRecordsKey, int64(params[i].items)))
|
|
require.Contains(t, span.Attributes(), attribute.Int64(erroredLogRecordsKey, 0))
|
|
assert.Equal(t, codes.Unset, span.Status().Code)
|
|
case errors.Is(params[i].err, errFake):
|
|
// Since we get an error, we cannot record any metrics because we don't know if the returned plog.Logs is valid instance.
|
|
require.Contains(t, span.Attributes(), attribute.Int64(scrapedLogRecordsKey, 0))
|
|
require.Contains(t, span.Attributes(), attribute.Int64(erroredLogRecordsKey, 0))
|
|
assert.Equal(t, codes.Error, span.Status().Code)
|
|
assert.Equal(t, params[i].err.Error(), span.Status().Description)
|
|
case errors.Is(params[i].err, partialErrFake):
|
|
scrapedLogRecords += params[i].items
|
|
erroredLogRecords += 2
|
|
require.Contains(t, span.Attributes(), attribute.Int64(scrapedLogRecordsKey, int64(params[i].items)))
|
|
require.Contains(t, span.Attributes(), attribute.Int64(erroredLogRecordsKey, 2))
|
|
assert.Equal(t, codes.Error, span.Status().Code)
|
|
assert.Equal(t, params[i].err.Error(), span.Status().Description)
|
|
default:
|
|
t.Fatalf("unexpected err param: %v", params[i].err)
|
|
}
|
|
}
|
|
|
|
checkScraperLogs(t, tel, receiverID, scraperID, int64(scrapedLogRecords), int64(erroredLogRecords))
|
|
}
|
|
|
|
func TestCheckScraperLogs(t *testing.T) {
|
|
tel := componenttest.NewTelemetry()
|
|
t.Cleanup(func() { require.NoError(t, tel.Shutdown(context.Background())) })
|
|
|
|
sm, err := scraper.NewLogs(func(context.Context) (plog.Logs, error) {
|
|
return testdata.GenerateLogs(7), nil
|
|
})
|
|
require.NoError(t, err)
|
|
sf, err := wrapObsLogs(sm, receiverID, scraperID, tel.NewTelemetrySettings())
|
|
require.NoError(t, err)
|
|
_, err = sf.ScrapeLogs(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
checkScraperLogs(t, tel, receiverID, scraperID, 7, 0)
|
|
}
|
|
|
|
func TestScrapeLogsDataOp_LogsScraperID(t *testing.T) {
|
|
tel := componenttest.NewTelemetry()
|
|
t.Cleanup(func() { require.NoError(t, tel.Shutdown(context.Background())) })
|
|
|
|
core, observedLogs := observer.New(zap.ErrorLevel)
|
|
telset := tel.NewTelemetrySettings()
|
|
telset.Logger = zap.New(core)
|
|
|
|
rSet := receiver.Settings{
|
|
ID: receiverID,
|
|
TelemetrySettings: telset,
|
|
}
|
|
set := controller.GetSettings(scraperID.Type(), rSet)
|
|
|
|
sm, err := scraper.NewLogs(func(context.Context) (plog.Logs, error) {
|
|
return plog.NewLogs(), errFake
|
|
})
|
|
require.NoError(t, err)
|
|
sf, err := wrapObsLogs(sm, receiverID, scraperID, set.TelemetrySettings)
|
|
require.NoError(t, err)
|
|
_, err = sf.ScrapeLogs(context.Background())
|
|
require.ErrorIs(t, err, errFake)
|
|
|
|
errorLogs := observedLogs.FilterLevelExact(zap.ErrorLevel).All()
|
|
require.Len(t, errorLogs, 1)
|
|
assert.Equal(t, "Error scraping logs", errorLogs[0].Message)
|
|
assert.Equal(t, scraperID.String(), errorLogs[0].ContextMap()["scraper"])
|
|
assert.Equal(t, errFake.Error(), errorLogs[0].ContextMap()["error"])
|
|
}
|
|
|
|
func checkScraperLogs(t *testing.T, tel *componenttest.Telemetry, receiver, scraper component.ID, scrapedLogRecords, erroredLogRecords int64) {
|
|
metadatatest.AssertEqualScraperScrapedLogRecords(t, tel,
|
|
[]metricdata.DataPoint[int64]{
|
|
{
|
|
Attributes: attribute.NewSet(
|
|
attribute.String(receiverKey, receiver.String()),
|
|
attribute.String(scraperKey, scraper.String())),
|
|
Value: scrapedLogRecords,
|
|
},
|
|
}, metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars())
|
|
|
|
metadatatest.AssertEqualScraperErroredLogRecords(t, tel,
|
|
[]metricdata.DataPoint[int64]{
|
|
{
|
|
Attributes: attribute.NewSet(
|
|
attribute.String(receiverKey, receiver.String()),
|
|
attribute.String(scraperKey, scraper.String())),
|
|
Value: erroredLogRecords,
|
|
},
|
|
}, metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreExemplars())
|
|
}
|