Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mdatagen] Improve generated documentation #16556

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .chloggen/mdatagen-improve-documentation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: cmd/mdatagen

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Improve generated documentation

# One or more tracking issues related to the change
issues: [16556]
2 changes: 1 addition & 1 deletion cmd/mdatagen/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func Test_loadMetadata(t *testing.T) {
Unit: "s",
Sum: &sum{
MetricValueType: MetricValueType{pmetric.NumberDataPointValueTypeDouble},
Aggregated: Aggregated{Aggregation: "cumulative"},
Aggregated: Aggregated{Aggregation: pmetric.AggregationTemporalityCumulative},
Mono: Mono{Monotonic: true},
},
Attributes: []attributeName{"freeFormAttribute", "freeFormAttributeWithValue", "enumAttribute", "booleanValueType"},
Expand Down
75 changes: 29 additions & 46 deletions cmd/mdatagen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,21 @@ func run(ymlPath string) error {
if err = os.MkdirAll(codeDir, 0700); err != nil {
return fmt.Errorf("unable to create output directory %q: %w", codeDir, err)
}
if err = generateCode(tmplDir, codeDir, "metrics.go", md); err != nil {
if err = generateFile(filepath.Join(tmplDir, "metrics.go.tmpl"),
filepath.Join(codeDir, "generated_metrics.go"), md); err != nil {
return err
}
if err = generateCode(tmplDir, codeDir, "metrics_test.go", md); err != nil {
if err = generateFile(filepath.Join(tmplDir, "metrics_test.go.tmpl"),
filepath.Join(codeDir, "generated_metrics_test.go"), md); err != nil {
return err
}
return generateDocumentation(tmplDir, ymlDir, md)
return generateFile(filepath.Join(tmplDir, "documentation.md.tmpl"), filepath.Join(ymlDir, "documentation.md"), md)
}

func generateCode(tmplDir string, outputDir string, tmplFile string, md metadata) error {
func generateFile(tmplFile string, outputFile string, md metadata) error {
tmpl := template.Must(
template.
New(tmplFile + ".tmpl").
New(filepath.Base(tmplFile)).
Option("missingkey=error").
Funcs(map[string]interface{}{
"publicVar": func(s string) (string, error) {
Expand All @@ -85,6 +87,9 @@ func generateCode(tmplDir string, outputDir string, tmplFile string, md metadata
}
return string(an)
},
"metricInfo": func(mn metricName) metric {
return md.Metrics[mn]
},
"parseImportsRequired": func(metrics map[metricName]metric) bool {
for _, m := range metrics {
if m.Data().HasMetricInputType() {
Expand All @@ -93,56 +98,34 @@ func generateCode(tmplDir string, outputDir string, tmplFile string, md metadata
}
return false
},
}).ParseFiles(filepath.Join(tmplDir, tmplFile+".tmpl")))
"stringsJoin": strings.Join,
}).ParseFiles(tmplFile))

buf := bytes.Buffer{}

if err := tmpl.Execute(&buf, templateContext{metadata: md, Package: "metadata"}); err != nil {
return fmt.Errorf("failed executing template: %w", err)
}

formatted, err := format.Source(buf.Bytes())

if err != nil {
errstr := strings.Builder{}
_, _ = fmt.Fprintf(&errstr, "failed formatting source: %v", err)
errstr.WriteString("--- BEGIN SOURCE ---")
errstr.Write(buf.Bytes())
errstr.WriteString("--- END SOURCE ---")
return errors.New(errstr.String())
}

outputFilepath := filepath.Join(outputDir, "generated_"+tmplFile)
if err := os.Remove(outputFilepath); err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("unable to remove genererated file %q: %w", outputFilepath, err)
}
if err := os.WriteFile(outputFilepath, formatted, 0600); err != nil {
return fmt.Errorf("failed writing %q: %w", outputFilepath, err)
result := buf.Bytes()

if strings.HasSuffix(outputFile, ".go") {
var err error
result, err = format.Source(buf.Bytes())
if err != nil {
errstr := strings.Builder{}
_, _ = fmt.Fprintf(&errstr, "failed formatting source: %v", err)
errstr.WriteString("--- BEGIN SOURCE ---")
errstr.Write(buf.Bytes())
errstr.WriteString("--- END SOURCE ---")
return errors.New(errstr.String())
}
}

return nil
}

func generateDocumentation(tmplDir string, outputDir string, md metadata) error {
tmpl := template.Must(
template.
New("documentation.md.tmpl").
Option("missingkey=error").
Funcs(map[string]interface{}{
"publicVar": func(s string) (string, error) {
return formatIdentifier(s, true)
},
"stringsJoin": strings.Join,
}).ParseFiles(filepath.Join(tmplDir, "documentation.md.tmpl")))

buf := bytes.Buffer{}

tmplCtx := templateContext{metadata: md, Package: "metadata"}
if err := tmpl.Execute(&buf, tmplCtx); err != nil {
return fmt.Errorf("failed executing template: %w", err)
if err := os.Remove(outputFile); err != nil && !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("unable to remove genererated file %q: %w", outputFile, err)
}

outputFile := filepath.Join(outputDir, "documentation.md")
if err := os.WriteFile(outputFile, buf.Bytes(), 0600); err != nil {
if err := os.WriteFile(outputFile, result, 0600); err != nil {
return fmt.Errorf("failed writing %q: %w", outputFile, err)
}

Expand Down
28 changes: 6 additions & 22 deletions cmd/mdatagen/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,17 @@ func Test_runContents(t *testing.T) {
yml string
}
tests := []struct {
name string
args args
expectedDocumentation string
want string
wantErr bool
name string
args args
wantErr bool
}{
{
name: "valid metadata",
args: args{validMetadata},
expectedDocumentation: "testdata/documentation.md",
want: "",
name: "valid metadata",
args: args{validMetadata},
},
{
name: "invalid yaml",
args: args{"invalid"},
want: "",
wantErr: true,
},
}
Expand All @@ -89,18 +84,7 @@ func Test_runContents(t *testing.T) {
require.NoError(t, err)

require.FileExists(t, filepath.Join(tmpdir, "internal/metadata/generated_metrics.go"))

actualDocumentation := filepath.Join(tmpdir, "documentation.md")
require.FileExists(t, actualDocumentation)
if tt.expectedDocumentation != "" {
expectedFileBytes, err := os.ReadFile(tt.expectedDocumentation)
require.NoError(t, err)

actualFileBytes, err := os.ReadFile(actualDocumentation)
require.NoError(t, err)

require.Equal(t, expectedFileBytes, actualFileBytes)
}
require.FileExists(t, filepath.Join(tmpdir, "documentation.md"))
})
}
}
Expand Down
25 changes: 16 additions & 9 deletions cmd/mdatagen/metricdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,29 @@ type MetricData interface {
}

// Aggregated defines a metric aggregation type.
// TODO: Rename to AggregationTemporality
type Aggregated struct {
// Aggregation describes if the aggregator reports delta changes
// since last report time, or cumulative changes since a fixed start time.
Aggregation string `mapstructure:"aggregation" validate:"oneof=delta cumulative"`
Aggregation pmetric.AggregationTemporality `validate:"required"`
}

// Type gets the metric aggregation type.
func (agg Aggregated) Type() string {
switch agg.Aggregation {
case "delta":
return "pmetric.AggregationTemporalityDelta"
// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (agg *Aggregated) UnmarshalText(text []byte) error {
switch vtStr := string(text); vtStr {
case "cumulative":
return "pmetric.AggregationTemporalityCumulative"
agg.Aggregation = pmetric.AggregationTemporalityCumulative
case "delta":
agg.Aggregation = pmetric.AggregationTemporalityDelta
default:
return "pmetric.AggregationTemporalityUnknown"
return fmt.Errorf("invalid aggregation: %q", vtStr)
}
return nil
}

// String returns string representation of the aggregation temporality.
func (agg *Aggregated) String() string {
return agg.Aggregation.String()
}

// Mono defines the metric monotonicity.
Expand Down Expand Up @@ -127,7 +134,7 @@ func (d gauge) HasMetricInputType() bool {
}

type sum struct {
Aggregated `mapstructure:",squash"`
Aggregated `mapstructure:"aggregation"`
Mono `mapstructure:",squash"`
MetricValueType `mapstructure:"value_type"`
MetricInputType `mapstructure:",squash"`
Expand Down
11 changes: 0 additions & 11 deletions cmd/mdatagen/metricdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,3 @@ func TestMetricData(t *testing.T) {
assert.Equal(t, arg.hasMonotonic, arg.metricData.HasMonotonic())
}
}

func TestAggregation(t *testing.T) {
delta := Aggregated{Aggregation: "delta"}
assert.Equal(t, "pmetric.AggregationTemporalityDelta", delta.Type())

cumulative := Aggregated{Aggregation: "cumulative"}
assert.Equal(t, "pmetric.AggregationTemporalityCumulative", cumulative.Type())

unknown := Aggregated{Aggregation: ""}
assert.Equal(t, "pmetric.AggregationTemporalityUnknown", unknown.Type())
}
85 changes: 65 additions & 20 deletions cmd/mdatagen/templates/documentation.md.tmpl
Original file line number Diff line number Diff line change
@@ -1,43 +1,88 @@
{{- define "metric-documenation" -}}
{{- $metricName := . }}
{{- $metric := $metricName | metricInfo -}}

### {{ $metricName }}

{{ $metric.Description }}

{{- if $metric.ExtendedDocumentation }}

{{ $metric.ExtendedDocumentation }}

{{- end }}

| Unit | Metric Type | Value Type |{{ if $metric.Data.HasAggregated }} Aggregation Temporality |{{ end }}{{ if $metric.Data.HasMonotonic }} Monotonic |{{ end }}
| ---- | ----------- | ---------- |{{ if $metric.Data.HasAggregated }} ----------------------- |{{ end }}{{ if $metric.Data.HasMonotonic }} --------- |{{ end }}
| {{ $metric.Unit }} | {{ $metric.Data.Type }} | {{ $metric.Data.MetricValueType }} |
{{- if $metric.Data.HasAggregated }} {{ $metric.Data.Aggregated }} |{{ end }}
{{- if $metric.Data.HasMonotonic }} {{ $metric.Data.Monotonic }} |{{ end }}

{{- if $metric.Attributes }}

#### Attributes

| Name | Description | Values |
| ---- | ----------- | ------ |
{{- range $metric.Attributes }}
{{- $attribute := . | attributeInfo }}
| {{ . | attributeKey }} | {{ $attribute.Description }} |
{{- if $attribute.Enum }} {{ $attribute.Type }}: ``{{ stringsJoin $attribute.Enum "``, ``" }}``{{ else }} Any {{ $attribute.Type }}{{ end }} |
{{- end }}

{{- end }}

{{- end -}}

[comment]: <> (Code generated by mdatagen. DO NOT EDIT.)

# {{ .Name }}

## Metrics
## Default Metrics

The following metrics are emitted by default. Each of them can be disabled by applying the following configuration:

```yaml
metrics:
<metric_name>:
enabled: false
```

{{- range $metricName, $metric := .Metrics }}
{{- if $metric.IsEnabled }}

These are the metrics available for this scraper.
{{ template "metric-documenation" $metricName }}

| Name | Description | Unit | Type | Attributes |
| ---- | ----------- | ---- | ---- | ---------- |
{{- range $metricName, $metricInfo := .Metrics }}
| {{ if $metricInfo.IsEnabled }}**{{ end }}{{ $metricName }}{{ if $metricInfo.IsEnabled }}**
{{- end }} | {{ $metricInfo.Description }}{{ if $metricInfo.ExtendedDocumentation }} {{ $metricInfo.ExtendedDocumentation }}{{ end }} | {{ $metricInfo.Unit }} | {{ $metricInfo.Data.Type }}({{ $metricInfo.Data.MetricValueType }}) | <ul>
{{- range $index, $attributeName := $metricInfo.Attributes }} <li>{{ $attributeName }}</li> {{- end }} </ul> |
{{- end }}
{{- end }}

## Optional Metrics

**Highlighted metrics** are emitted by default. Other metrics are optional and not emitted by default.
Any metric can be enabled or disabled with the following scraper configuration:
The following metrics are not emitted by default. Each of them can be enabled by applying the following configuration:

```yaml
metrics:
<metric_name>:
enabled: <true|false>
enabled: true
```

{{- if .ResourceAttributes }}
{{- range $metricName, $metric := .Metrics }}
{{- if $metric.IsEnabled }}

## Resource attributes
{{ template "metric-documenation" $metricName }}

| Name | Description | Type |
| ---- | ----------- | ---- |
{{- range $attributeName, $attributeInfo := .ResourceAttributes }}
| {{ $attributeName }} | {{ $attributeInfo.Description }} | {{ $attributeInfo.Type }} |
{{- end }}
{{- end }}

## Metric attributes
{{- if .ResourceAttributes }}

## Resource Attributes

| Name | Description | Values |
| ---- | ----------- | ------ |
{{- range $attributeName, $attributeInfo := .Attributes }}
| {{ $attributeName }}{{- if $attributeInfo.Value }} ({{ $attributeInfo.Value }}){{- end}} | {{ $attributeInfo.Description }} | {{ stringsJoin $attributeInfo.Enum ", " }} |
{{- range $attributeName, $attribute := .ResourceAttributes }}
| {{ $attributeName }} | {{ $attribute.Description }} |
{{- if $attribute.Enum }} {{ $attribute.Type }}: ``{{ stringsJoin $attribute.Enum "``, ``" }}``{{ else }} Any {{ $attribute.Type }}{{ end }} |
{{- end }}

{{- end }}
2 changes: 1 addition & 1 deletion cmd/mdatagen/templates/metrics.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (m *metric{{ $name.Render }}) init() {
m.data.{{ $metric.Data.Type }}().SetIsMonotonic({{ $metric.Data.Monotonic }})
{{- end }}
{{- if $metric.Data.HasAggregated }}
m.data.{{ $metric.Data.Type }}().SetAggregationTemporality({{ $metric.Data.Aggregated.Type }})
m.data.{{ $metric.Data.Type }}().SetAggregationTemporality(pmetric.AggregationTemporality{{ $metric.Data.Aggregated }})
{{- end }}
{{- if $metric.Attributes }}
m.data.{{ $metric.Data.Type }}().DataPoints().EnsureCapacity(m.capacity)
Expand Down
2 changes: 1 addition & 1 deletion cmd/mdatagen/templates/metrics_test.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestAllMetrics(t *testing.T) {
assert.Equal(t, {{ $metric.Data.Monotonic }}, ms.At(i).{{ $metric.Data.Type }}().IsMonotonic())
{{- end }}
{{- if $metric.Data.HasAggregated }}
assert.Equal(t, {{ $metric.Data.Aggregated.Type }}, ms.At(i).{{ $metric.Data.Type }}().AggregationTemporality())
assert.Equal(t, pmetric.AggregationTemporality{{ $metric.Data.Aggregated }}, ms.At(i).{{ $metric.Data.Type }}().AggregationTemporality())
{{- end }}
dp := ms.At(i).{{ $metric.Data.Type }}().DataPoints().At(0)
assert.Equal(t, start, dp.StartTimestamp())
Expand Down
27 changes: 0 additions & 27 deletions cmd/mdatagen/testdata/documentation.md

This file was deleted.

Loading