Skip to content

Commit 18b3b57

Browse files
authored
[cmd/mdatagen] Add support for optional attribute (#13007)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Add support for optional attribute, ref: [Conditional Required Attribute](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attribute-requirement-level.md#conditionally-required) Notes: - Add `helper.tmpl` for helper templates - Add `With<AttributeName>EventAttribute` and `With<AttributeName>MetricAttribute` for optional attributes - Add some helper functions in go files. <!-- Issue number if applicable --> #### Link to tracking issue Relevant to #12571 <!--Describe what testing was performed and which tests were added.--> #### Testing Updated <!--Describe the documentation added.--> #### Documentation Added <!--Please delete paragraphs that you did not use before submitting.-->
1 parent a4d851b commit 18b3b57

26 files changed

+492
-159
lines changed

.chloggen/optional-attribute.yaml

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: cmd/mdatagen
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for optional attribute
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [12571]
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: [api]

cmd/mdatagen/internal/command.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"regexp"
1515
"runtime/debug"
1616
"slices"
17+
"sort"
1718
"strings"
1819
"text/template"
1920

@@ -217,6 +218,40 @@ func templatize(tmplFile string, md Metadata) *template.Template {
217218
"attributeInfo": func(an AttributeName) Attribute {
218219
return md.Attributes[an]
219220
},
221+
"getEventOptionalAttributes": func(attrs map[AttributeName]Attribute) []AttributeName {
222+
seen := make(map[AttributeName]bool)
223+
used := make([]AttributeName, 0)
224+
225+
for _, event := range md.Events {
226+
for _, attribute := range event.Attributes {
227+
v, exists := attrs[attribute]
228+
if exists && v.Optional && !seen[attribute] {
229+
used = append(used, attribute)
230+
seen[attribute] = true
231+
}
232+
}
233+
}
234+
sort.Slice(used, func(i, j int) bool { return string(used[i]) < string(used[j]) })
235+
236+
return used
237+
},
238+
"getMetricOptionalAttributes": func(attrs map[AttributeName]Attribute) []AttributeName {
239+
seen := make(map[AttributeName]bool)
240+
used := make([]AttributeName, 0)
241+
242+
for _, event := range md.Metrics {
243+
for _, attribute := range event.Attributes {
244+
v, exists := attrs[attribute]
245+
if exists && v.Optional && !seen[attribute] {
246+
used = append(used, attribute)
247+
seen[attribute] = true
248+
}
249+
}
250+
}
251+
sort.Slice(used, func(i, j int) bool { return string(used[i]) < string(used[j]) })
252+
253+
return used
254+
},
220255
"metricInfo": func(mn MetricName) Metric {
221256
return md.Metrics[mn]
222257
},
@@ -300,7 +335,7 @@ func templatize(tmplFile string, md Metadata) *template.Template {
300335
// which uses the `\` as a special character.
301336
// Meaning on windows based machines, the `\` needs to be replaced
302337
// with a `/` for it to find the file.
303-
}).ParseFS(TemplateFS, strings.ReplaceAll(tmplFile, "\\", "/")))
338+
}).ParseFS(TemplateFS, "templates/helper.tmpl", strings.ReplaceAll(tmplFile, "\\", "/")))
304339
}
305340

306341
func executeTemplate(tmplFile string, md Metadata, goPackage string) ([]byte, error) {

cmd/mdatagen/internal/command_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func TestRunContents(t *testing.T) {
3636
// TODO: we should add one more flag for logs builder
3737
wantEventsGenerated bool
3838
wantMetricsContext bool
39+
wantLogsGenerated bool
3940
wantConfigGenerated bool
4041
wantTelemetryGenerated bool
4142
wantResourceAttributesGenerated bool
@@ -66,6 +67,7 @@ func TestRunContents(t *testing.T) {
6667
wantStatusGenerated: true,
6768
wantReadmeGenerated: true,
6869
wantComponentTestGenerated: true,
70+
wantLogsGenerated: true,
6971
},
7072
{
7173
yml: "basic_pkg.yaml",
@@ -88,6 +90,7 @@ func TestRunContents(t *testing.T) {
8890
wantResourceAttributesGenerated: true,
8991
wantReadmeGenerated: true,
9092
wantComponentTestGenerated: true,
93+
wantLogsGenerated: true,
9194
},
9295
{
9396
yml: "status_only.yaml",
@@ -100,6 +103,7 @@ func TestRunContents(t *testing.T) {
100103
wantStatusGenerated: true,
101104
wantReadmeGenerated: true,
102105
wantComponentTestGenerated: true,
106+
wantLogsGenerated: true,
103107
},
104108
{
105109
yml: "with_tests_exporter.yaml",
@@ -160,6 +164,7 @@ func TestRunContents(t *testing.T) {
160164
wantReadmeGenerated: true,
161165
wantComponentTestGenerated: true,
162166
wantAttributes: []string{"name"},
167+
wantLogsGenerated: true,
163168
},
164169
{
165170
yml: "invalid_telemetry_missing_value_type_for_histogram.yaml",
@@ -180,6 +185,16 @@ func TestRunContents(t *testing.T) {
180185
wantStatusGenerated: true,
181186
wantReadmeGenerated: true,
182187
wantComponentTestGenerated: true,
188+
wantLogsGenerated: true,
189+
},
190+
{
191+
yml: "with_optional_attribute.yaml",
192+
wantStatusGenerated: true,
193+
wantReadmeGenerated: true,
194+
wantMetricsGenerated: true,
195+
wantLogsGenerated: true,
196+
wantConfigGenerated: true,
197+
wantComponentTestGenerated: true,
183198
},
184199
{
185200
yml: "events/basic_event.yaml",
@@ -188,6 +203,7 @@ func TestRunContents(t *testing.T) {
188203
wantComponentTestGenerated: true,
189204
wantConfigGenerated: true,
190205
wantEventsGenerated: true,
206+
wantLogsGenerated: true,
191207
},
192208
}
193209
for _, tt := range tests {
@@ -244,6 +260,14 @@ foo
244260
require.NoFileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_metrics_test.go"))
245261
}
246262

263+
if tt.wantLogsGenerated {
264+
require.FileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_logs.go"))
265+
require.FileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_logs_test.go"))
266+
} else {
267+
require.NoFileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_logs.go"))
268+
require.NoFileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_logs_test.go"))
269+
}
270+
247271
if tt.wantConfigGenerated {
248272
require.FileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_config.go"))
249273
require.FileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_config_test.go"))

cmd/mdatagen/internal/embedded_templates_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func TestEnsureTemplatesLoaded(t *testing.T) {
3939
path.Join(rootDir, "testdata", "config.yaml.tmpl"): {},
4040
path.Join(rootDir, "telemetrytest.go.tmpl"): {},
4141
path.Join(rootDir, "telemetrytest_test.go.tmpl"): {},
42+
path.Join(rootDir, "helper.tmpl"): {},
4243
}
4344
count = 0
4445
)

cmd/mdatagen/internal/event.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,12 @@ func (l *Event) Unmarshal(parser *confmap.Conf) error {
5555
}
5656
return parser.Unmarshal(l)
5757
}
58+
59+
func (l Event) HasOptionalAttribute(attrs map[AttributeName]Attribute) bool {
60+
for _, attr := range l.Attributes {
61+
if v, exists := attrs[attr]; exists && v.Optional {
62+
return true
63+
}
64+
}
65+
return false
66+
}

cmd/mdatagen/internal/loader_test.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,22 @@ func TestLoadMetadata(t *testing.T) {
197197
},
198198
FullName: "map_attr",
199199
},
200+
"optional_int_attr": {
201+
Description: "An optional attribute with an integer value",
202+
Type: ValueType{
203+
ValueType: pcommon.ValueTypeInt,
204+
},
205+
FullName: "optional_int_attr",
206+
Optional: true,
207+
},
208+
"optional_string_attr": {
209+
Description: "An optional attribute with any string value",
210+
Type: ValueType{
211+
ValueType: pcommon.ValueTypeStr,
212+
},
213+
FullName: "optional_string_attr",
214+
Optional: true,
215+
},
200216
},
201217
Metrics: map[MetricName]Metric{
202218
"default.metric": {
@@ -212,7 +228,7 @@ func TestLoadMetadata(t *testing.T) {
212228
AggregationTemporality: AggregationTemporality{Aggregation: pmetric.AggregationTemporalityCumulative},
213229
Mono: Mono{Monotonic: true},
214230
},
215-
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr"},
231+
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "optional_int_attr", "optional_string_attr"},
216232
},
217233
"optional.metric": {
218234
Enabled: false,
@@ -224,7 +240,7 @@ func TestLoadMetadata(t *testing.T) {
224240
Gauge: &Gauge{
225241
MetricValueType: MetricValueType{pmetric.NumberDataPointValueTypeDouble},
226242
},
227-
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2"},
243+
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "optional_string_attr"},
228244
},
229245
"optional.metric.empty_unit": {
230246
Enabled: false,
@@ -273,7 +289,7 @@ func TestLoadMetadata(t *testing.T) {
273289
Warnings: Warnings{
274290
IfEnabledNotSet: "This event will be disabled by default soon.",
275291
},
276-
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr"},
292+
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "optional_int_attr", "optional_string_attr"},
277293
},
278294
"default.event.to_be_renamed": {
279295
Enabled: false,
@@ -282,7 +298,7 @@ func TestLoadMetadata(t *testing.T) {
282298
Warnings: Warnings{
283299
IfConfigured: "This event is deprecated and will be renamed soon.",
284300
},
285-
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2"},
301+
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "optional_string_attr"},
286302
},
287303
"default.event.to_be_removed": {
288304
Enabled: true,

cmd/mdatagen/internal/metadata.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ type Attribute struct {
304304
FullName AttributeName `mapstructure:"-"`
305305
// Warnings that will be shown to user under specified conditions.
306306
Warnings Warnings `mapstructure:"warnings"`
307+
// Optional defines whether the attribute is required.
308+
Optional bool `mapstructure:"optional"`
307309
}
308310

309311
// Name returns actual name of the attribute that is set on the metric after applying NameOverride.

cmd/mdatagen/internal/metric.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,15 @@ func (m Metric) Data() MetricData {
124124
return nil
125125
}
126126

127+
func (m Metric) HasOptionalAttribute(attrs map[AttributeName]Attribute) bool {
128+
for _, attr := range m.Attributes {
129+
if v, exists := attrs[attr]; exists && v.Optional {
130+
return true
131+
}
132+
}
133+
return false
134+
}
135+
127136
// MetricData is generic interface for all metric datatypes.
128137
type MetricData interface {
129138
Type() string

cmd/mdatagen/internal/sampleconnector/documentation.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ The metric will be become optional soon.
2424
2525
#### Attributes
2626
27-
| Name | Description | Values |
28-
| ---- | ----------- | ------ |
29-
| string_attr | Attribute with any string value. | Any Str |
30-
| state | Integer attribute with overridden name. | Any Int |
31-
| enum_attr | Attribute with a known set of string values. | Str: ``red``, ``green``, ``blue`` |
32-
| slice_attr | Attribute with a slice value. | Any Slice |
33-
| map_attr | Attribute with a map value. | Any Map |
27+
| Name | Description | Values | Optional |
28+
| ---- | ----------- | ------ | -------- |
29+
| string_attr | Attribute with any string value. | Any Str | false |
30+
| state | Integer attribute with overridden name. | Any Int | false |
31+
| enum_attr | Attribute with a known set of string values. | Str: ``red``, ``green``, ``blue`` | false |
32+
| slice_attr | Attribute with a slice value. | Any Slice | false |
33+
| map_attr | Attribute with a map value. | Any Map | false |
3434
3535
### default.metric.to_be_removed
3636
@@ -52,13 +52,13 @@ Monotonic cumulative sum int metric with string input_type enabled by default.
5252
5353
#### Attributes
5454
55-
| Name | Description | Values |
56-
| ---- | ----------- | ------ |
57-
| string_attr | Attribute with any string value. | Any Str |
58-
| state | Integer attribute with overridden name. | Any Int |
59-
| enum_attr | Attribute with a known set of string values. | Str: ``red``, ``green``, ``blue`` |
60-
| slice_attr | Attribute with a slice value. | Any Slice |
61-
| map_attr | Attribute with a map value. | Any Map |
55+
| Name | Description | Values | Optional |
56+
| ---- | ----------- | ------ | -------- |
57+
| string_attr | Attribute with any string value. | Any Str | false |
58+
| state | Integer attribute with overridden name. | Any Int | false |
59+
| enum_attr | Attribute with a known set of string values. | Str: ``red``, ``green``, ``blue`` | false |
60+
| slice_attr | Attribute with a slice value. | Any Slice | false |
61+
| map_attr | Attribute with a map value. | Any Map | false |
6262
6363
## Optional Metrics
6464
@@ -80,11 +80,11 @@ metrics:
8080
8181
#### Attributes
8282
83-
| Name | Description | Values |
84-
| ---- | ----------- | ------ |
85-
| string_attr | Attribute with any string value. | Any Str |
86-
| boolean_attr | Attribute with a boolean value. | Any Bool |
87-
| boolean_attr2 | Another attribute with a boolean value. | Any Bool |
83+
| Name | Description | Values | Optional |
84+
| ---- | ----------- | ------ | -------- |
85+
| string_attr | Attribute with any string value. | Any Str | false |
86+
| boolean_attr | Attribute with a boolean value. | Any Bool | false |
87+
| boolean_attr2 | Another attribute with a boolean value. | Any Bool | false |
8888
8989
### optional.metric.empty_unit
9090
@@ -96,10 +96,10 @@ metrics:
9696
9797
#### Attributes
9898
99-
| Name | Description | Values |
100-
| ---- | ----------- | ------ |
101-
| string_attr | Attribute with any string value. | Any Str |
102-
| boolean_attr | Attribute with a boolean value. | Any Bool |
99+
| Name | Description | Values | Optional |
100+
| ---- | ----------- | ------ | -------- |
101+
| string_attr | Attribute with any string value. | Any Str | false |
102+
| boolean_attr | Attribute with a boolean value. | Any Bool | false |
103103
104104
## Resource Attributes
105105

0 commit comments

Comments
 (0)