Skip to content

Commit caf0a1f

Browse files
codebotensteves-canva
authored andcommitted
[mdatagen] generate utility code to test telemetry (open-telemetry#10212)
This will allow components to more easily test the telemetry they should be emitting. --------- Signed-off-by: Alex Boten <[email protected]>
1 parent 8721a19 commit caf0a1f

22 files changed

+641
-24
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: mdatagen
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: auto-generate utilities to test component telemetry
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [19783]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# Optional: The change log or logs in which this entry should be included.
21+
# e.g. '[user]' or '[user, api]'
22+
# Include 'user' if the change is relevant to end users.
23+
# Include 'api' if there is a change to a library API.
24+
# Default: '[user]'
25+
change_logs: []

cmd/mdatagen/embeded_templates_test.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,21 @@ func TestEnsureTemplatesLoaded(t *testing.T) {
2020

2121
var (
2222
templateFiles = map[string]struct{}{
23-
path.Join(rootDir, "component_test.go.tmpl"): {},
24-
path.Join(rootDir, "documentation.md.tmpl"): {},
25-
path.Join(rootDir, "metrics.go.tmpl"): {},
26-
path.Join(rootDir, "metrics_test.go.tmpl"): {},
27-
path.Join(rootDir, "resource.go.tmpl"): {},
28-
path.Join(rootDir, "resource_test.go.tmpl"): {},
29-
path.Join(rootDir, "config.go.tmpl"): {},
30-
path.Join(rootDir, "config_test.go.tmpl"): {},
31-
path.Join(rootDir, "package_test.go.tmpl"): {},
32-
path.Join(rootDir, "readme.md.tmpl"): {},
33-
path.Join(rootDir, "status.go.tmpl"): {},
34-
path.Join(rootDir, "telemetry.go.tmpl"): {},
35-
path.Join(rootDir, "telemetry_test.go.tmpl"): {},
36-
path.Join(rootDir, "testdata", "config.yaml.tmpl"): {},
23+
path.Join(rootDir, "component_test.go.tmpl"): {},
24+
path.Join(rootDir, "component_telemetry_test.go.tmpl"): {},
25+
path.Join(rootDir, "documentation.md.tmpl"): {},
26+
path.Join(rootDir, "metrics.go.tmpl"): {},
27+
path.Join(rootDir, "metrics_test.go.tmpl"): {},
28+
path.Join(rootDir, "resource.go.tmpl"): {},
29+
path.Join(rootDir, "resource_test.go.tmpl"): {},
30+
path.Join(rootDir, "config.go.tmpl"): {},
31+
path.Join(rootDir, "config_test.go.tmpl"): {},
32+
path.Join(rootDir, "package_test.go.tmpl"): {},
33+
path.Join(rootDir, "readme.md.tmpl"): {},
34+
path.Join(rootDir, "status.go.tmpl"): {},
35+
path.Join(rootDir, "telemetry.go.tmpl"): {},
36+
path.Join(rootDir, "telemetry_test.go.tmpl"): {},
37+
path.Join(rootDir, "testdata", "config.yaml.tmpl"): {},
3738
}
3839
count = 0
3940
)

cmd/mdatagen/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
go.opentelemetry.io/collector/receiver v0.101.0
1515
go.opentelemetry.io/collector/semconv v0.101.0
1616
go.opentelemetry.io/otel/metric v1.27.0
17+
go.opentelemetry.io/otel/sdk/metric v1.27.0
1718
go.opentelemetry.io/otel/trace v1.27.0
1819
go.uber.org/goleak v1.3.0
1920
go.uber.org/zap v1.27.0
@@ -46,7 +47,6 @@ require (
4647
go.opentelemetry.io/otel v1.27.0 // indirect
4748
go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect
4849
go.opentelemetry.io/otel/sdk v1.27.0 // indirect
49-
go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect
5050
go.uber.org/multierr v1.11.0 // indirect
5151
golang.org/x/net v0.24.0 // indirect
5252
golang.org/x/sys v0.20.0 // indirect

cmd/mdatagen/internal/samplereceiver/factory.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ func createTraces(context.Context, receiver.CreateSettings, component.Config, co
2626
return nopInstance, nil
2727
}
2828

29-
func createMetrics(context.Context, receiver.CreateSettings, component.Config, consumer.Metrics) (receiver.Metrics, error) {
29+
func createMetrics(ctx context.Context, set receiver.CreateSettings, _ component.Config, _ consumer.Metrics) (receiver.Metrics, error) {
30+
telemetryBuilder, err := metadata.NewTelemetryBuilder(set.TelemetrySettings, metadata.WithProcessRuntimeTotalAllocBytesCallback(func() int64 { return 2 }))
31+
if err != nil {
32+
return nil, err
33+
}
34+
telemetryBuilder.BatchSizeTriggerSend.Add(ctx, 1)
3035
return nopInstance, nil
3136
}
3237

cmd/mdatagen/internal/samplereceiver/generated_component_telemetry_test.go

Lines changed: 76 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/mdatagen/internal/samplereceiver/metrics_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
package samplereceiver
55

66
import (
7+
"context"
78
"testing"
89

910
"github.com/stretchr/testify/require"
11+
"go.opentelemetry.io/otel/sdk/metric/metricdata"
1012

1113
"go.opentelemetry.io/collector/cmd/mdatagen/internal/samplereceiver/internal/metadata"
14+
"go.opentelemetry.io/collector/component/componenttest"
15+
"go.opentelemetry.io/collector/consumer/consumertest"
1216
"go.opentelemetry.io/collector/receiver/receivertest"
1317
)
1418

@@ -18,3 +22,41 @@ func TestGeneratedMetrics(t *testing.T) {
1822
m := mb.Emit()
1923
require.Equal(t, 0, m.ResourceMetrics().Len())
2024
}
25+
26+
func TestComponentTelemetry(t *testing.T) {
27+
tt := setupTestTelemetry()
28+
factory := NewFactory()
29+
_, err := factory.CreateMetricsReceiver(context.Background(), tt.NewCreateSettings(), componenttest.NewNopHost(), new(consumertest.MetricsSink))
30+
require.NoError(t, err)
31+
tt.assertMetrics(t, []metricdata.Metrics{
32+
{
33+
Name: "batch_size_trigger_send",
34+
Description: "Number of times the batch was sent due to a size trigger",
35+
Unit: "1",
36+
Data: metricdata.Sum[int64]{
37+
Temporality: metricdata.CumulativeTemporality,
38+
IsMonotonic: true,
39+
DataPoints: []metricdata.DataPoint[int64]{
40+
{
41+
Value: 1,
42+
},
43+
},
44+
},
45+
},
46+
{
47+
Name: "process_runtime_total_alloc_bytes",
48+
Description: "Cumulative bytes allocated for heap objects (see 'go doc runtime.MemStats.TotalAlloc')",
49+
Unit: "By",
50+
Data: metricdata.Sum[int64]{
51+
Temporality: metricdata.CumulativeTemporality,
52+
IsMonotonic: true,
53+
DataPoints: []metricdata.DataPoint[int64]{
54+
{
55+
Value: 2,
56+
},
57+
},
58+
},
59+
},
60+
})
61+
require.NoError(t, tt.Shutdown(context.Background()))
62+
}

cmd/mdatagen/main.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func run(ymlPath string) error {
5959
return fmt.Errorf("unable to create output directory %q: %w", codeDir, err)
6060
}
6161
if md.Status != nil {
62-
if md.Status.Class != "cmd" && md.Status.Class != "pkg" {
62+
if md.Status.Class != "cmd" && md.Status.Class != "pkg" && !md.Status.NotComponent {
6363
if err = generateFile(filepath.Join(tmplDir, "status.go.tmpl"),
6464
filepath.Join(codeDir, "generated_status.go"), md, "metadata"); err != nil {
6565
return err
@@ -88,6 +88,10 @@ func run(ymlPath string) error {
8888
toGenerate := map[string]string{}
8989

9090
if len(md.Telemetry.Metrics) != 0 { // if there are telemetry metrics, generate telemetry specific files
91+
if err = generateFile(filepath.Join(tmplDir, "component_telemetry_test.go.tmpl"),
92+
filepath.Join(ymlDir, "generated_component_telemetry_test.go"), md, packageName); err != nil {
93+
return err
94+
}
9195
toGenerate[filepath.Join(tmplDir, "telemetry.go.tmpl")] = filepath.Join(codeDir, "generated_telemetry.go")
9296
toGenerate[filepath.Join(tmplDir, "telemetry_test.go.tmpl")] = filepath.Join(codeDir, "generated_telemetry_test.go")
9397
}

cmd/mdatagen/statusdata.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Status struct {
3939
Warnings []string `mapstructure:"warnings"`
4040
Codeowners *Codeowners `mapstructure:"codeowners"`
4141
UnsupportedPlatforms []string `mapstructure:"unsupported_platforms"`
42+
NotComponent bool `mapstructure:"not_component"`
4243
}
4344

4445
func (s *Status) SortedDistributions() []string {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Code generated by mdatagen. DO NOT EDIT.
2+
3+
package {{ if isCommand -}}main{{ else }}{{ .Package }}{{- end }}
4+
5+
import (
6+
"context"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
"go.opentelemetry.io/otel/sdk/metric/metricdata"
11+
"go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
12+
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
13+
14+
"go.opentelemetry.io/collector/component"
15+
"go.opentelemetry.io/collector/{{ .Status.Class }}"
16+
"go.opentelemetry.io/collector/{{ .Status.Class }}/{{ .Status.Class }}test"
17+
)
18+
19+
type componentTestTelemetry struct {
20+
reader *sdkmetric.ManualReader
21+
meterProvider *sdkmetric.MeterProvider
22+
}
23+
24+
func (tt *componentTestTelemetry) NewCreateSettings() {{ .Status.Class }}.CreateSettings {
25+
settings := {{ .Status.Class }}test.NewNopCreateSettings()
26+
settings.MeterProvider = tt.meterProvider
27+
settings.ID = component.NewID(component.MustNewType("{{ .Type }}"))
28+
29+
return settings
30+
}
31+
32+
func setupTestTelemetry() componentTestTelemetry {
33+
reader := sdkmetric.NewManualReader()
34+
return componentTestTelemetry{
35+
reader: reader,
36+
meterProvider: sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)),
37+
}
38+
}
39+
40+
func (tt *componentTestTelemetry) assertMetrics(t *testing.T, expected []metricdata.Metrics) {
41+
var md metricdata.ResourceMetrics
42+
require.NoError(t, tt.reader.Collect(context.Background(), &md))
43+
// ensure all required metrics are present
44+
for _, want := range expected {
45+
got := tt.getMetric(want.Name, md)
46+
metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp())
47+
}
48+
49+
// ensure no additional metrics are emitted
50+
require.Equal(t, len(expected), tt.len(md))
51+
}
52+
53+
func (tt *componentTestTelemetry) getMetric(name string, got metricdata.ResourceMetrics) metricdata.Metrics {
54+
for _, sm := range got.ScopeMetrics {
55+
for _, m := range sm.Metrics {
56+
if m.Name == name {
57+
return m
58+
}
59+
}
60+
}
61+
62+
return metricdata.Metrics{}
63+
}
64+
65+
func (tt *componentTestTelemetry) len(got metricdata.ResourceMetrics) int {
66+
metricsCount := 0
67+
for _, sm := range got.ScopeMetrics {
68+
metricsCount += len(sm.Metrics)
69+
}
70+
71+
return metricsCount
72+
}
73+
74+
func (tt *componentTestTelemetry) Shutdown(ctx context.Context) error {
75+
return tt.meterProvider.Shutdown(ctx)
76+
}

cmd/mdatagen/templates/telemetry_test.go.tmpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77

88
"github.com/stretchr/testify/require"
99
"go.opentelemetry.io/otel/metric"
10+
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
11+
"go.opentelemetry.io/otel/sdk/metric/metricdata"
1012
embeddedmetric "go.opentelemetry.io/otel/metric/embedded"
1113
noopmetric "go.opentelemetry.io/otel/metric/noop"
1214
"go.opentelemetry.io/otel/trace"
@@ -75,4 +77,5 @@ func TestNewTelemetryBuilder(t *testing.T) {
7577
require.NoError(t, err)
7678
require.True(t, applied)
7779
}
80+
7881
{{- end }}

0 commit comments

Comments
 (0)