diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3a65bdc656f..522ebc6e9de 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -101,6 +101,7 @@ /pkg/middleware/ @grafana/grafana-backend-group /pkg/mocks/ @grafana/grafana-backend-group /pkg/models/ @grafana/grafana-backend-group +/pkg/semconv/ @grafana/grafana-backend-group /pkg/server/ @grafana/grafana-backend-group /pkg/apiserver @grafana/grafana-app-platform-squad /pkg/apimachinery @grafana/grafana-app-platform-squad diff --git a/.github/workflows/go_lint.yml b/.github/workflows/go_lint.yml index bd454d7b4a1..50493a86db8 100644 --- a/.github/workflows/go_lint.yml +++ b/.github/workflows/go_lint.yml @@ -27,6 +27,6 @@ jobs: with: version: v1.59.1 args: | - --config .golangci.toml --max-same-issues=0 --max-issues-per-linter=0 --verbose ./pkg/... ./pkg/apiserver/... ./pkg/apimachinery/... ./pkg/promlib/... + --config .golangci.toml --max-same-issues=0 --max-issues-per-linter=0 --verbose ./pkg/... ./pkg/apiserver/... ./pkg/apimachinery/... ./pkg/promlib/... ./pkg/semconv/... skip-cache: true install-mode: binary diff --git a/Makefile b/Makefile index 59f198e1775..662c41e164c 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ include .bingo/Variables.mk GO = go GO_VERSION = 1.22.4 -GO_FILES ?= ./pkg/... ./pkg/apiserver/... ./pkg/apimachinery/... ./pkg/promlib/... +GO_FILES ?= ./pkg/... ./pkg/apiserver/... ./pkg/apimachinery/... ./pkg/promlib/... ./pkg/semconv/... SH_FILES ?= $(shell find ./scripts -name *.sh) GO_RACE := $(shell [ -n "$(GO_RACE)" -o -e ".go-race-enabled-locally" ] && echo 1 ) GO_RACE_FLAG := $(if $(GO_RACE),-race) diff --git a/go.work b/go.work index 67f2d6728c2..27919cfed71 100644 --- a/go.work +++ b/go.work @@ -7,6 +7,7 @@ use ( ./pkg/build ./pkg/build/wire ./pkg/promlib + ./pkg/semconv ./pkg/storage/unified/resource ./pkg/util/xorm ) diff --git a/go.work.sum b/go.work.sum index a3077a016a3..2548e4c4a46 100644 --- a/go.work.sum +++ b/go.work.sum @@ -829,6 +829,8 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhp golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= diff --git a/pkg/semconv/Makefile b/pkg/semconv/Makefile new file mode 100644 index 00000000000..6544e6ad79e --- /dev/null +++ b/pkg/semconv/Makefile @@ -0,0 +1,18 @@ +.PHONY: all +all: go markdown + +.PHONY: go +go: + @docker run --rm -u 1000:1000 -v./model:/tmp/grafana/model -v./:/tmp/grafana/output -v./templates:/tmp/grafana/templates \ + otel/semconvgen --yaml-root "/tmp/grafana/model/" \ + code --template "/tmp/grafana/templates/template.j2" \ + --output "/tmp/grafana/output/attributes.go" + @go fmt + +.PHONY: markdown +markdown: + @docker run --rm -u 1000:1000 -v./model:/tmp/grafana/model -v./:/tmp/grafana/output -v./templates:/tmp/grafana/templates \ + otel/semconvgen --yaml-root "/tmp/grafana/model/" \ + markdown --markdown-root "/tmp/grafana/output/" + @npx --yes -- markdown-toc --bullets "-" --no-first-h1 --no-stripHeadingTags -i README.md || exit 1 + diff --git a/pkg/semconv/README.md b/pkg/semconv/README.md new file mode 100644 index 00000000000..f4322dc25f9 --- /dev/null +++ b/pkg/semconv/README.md @@ -0,0 +1,50 @@ +# Grafana OpenTelemetry Semantic Conventions + + + +- [Adding new attributes](#adding-new-attributes) +- [Attribute Groups](#attribute-groups) + - [grafana.datasource](#grafanadatasource) + - [grafana.datasource.request](#grafanadatasourcerequest) + - [grafana.plugin](#grafanaplugin) + + + +## Adding new attributes + +1. Add a new attribute to a new or existing attribute group in [model/registry](./model/registry). +1. Add a reference to the new attribute in a new or existing attribute group in [model/trace](./model/trace). +1. If you are adding a new attribute group, add a new `semconv` HTML comment tag to the README.md file with the name of the new attribute group. +1. Run `make all` to update the generated files. + +For more information: +- [Semantic Convention generator + Docker](https://github.com/open-telemetry/build-tools/blob/main/semantic-conventions/README.md) +- [OpenTelemetry Semantic Conventions](https://github.com/open-telemetry/semantic-conventions/tree/main/model) (these can be used as a reference) + +## Attribute Groups + +### grafana.datasource + +| Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | +|---|---|---|---|---|---| +| `grafana.datasource.type` | string | The datasource type. | `prometheus`; `loki`; `grafana-github-datasource` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | +| `grafana.datasource.uid` | string | The datasource unique identifier. | `abcdefg-123456` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + + +#### grafana.datasource.request + + +| Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | +|---|---|---|---|---|---| +| `grafana.datasource.request.query_count` | int | The number of queries in the request. | `3` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + + + +### grafana.plugin + + +| Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability | +|---|---|---|---|---|---| +| `grafana.plugin.id` | string | The plugin ID. | `prometheus`; `loki`; `grafana-github-datasource` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | +| `grafana.plugin.type` | string | The plugin type. | `datasource` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | + diff --git a/pkg/semconv/attributes.go b/pkg/semconv/attributes.go new file mode 100644 index 00000000000..d9718bd7e70 --- /dev/null +++ b/pkg/semconv/attributes.go @@ -0,0 +1,103 @@ +// Code generated from semantic convention specification. DO NOT EDIT. + +package semconv + +import "go.opentelemetry.io/otel/attribute" + +// Describes Grafana datasource attributes. +const ( + // GrafanaDatasourceTypeKey is the attribute Key conforming to the + // "grafana.datasource.type" semantic conventions. It represents the + // datasource type. + // + // Type: string + // RequirementLevel: Optional + // Stability: stable + // Examples: 'prometheus', 'loki', 'grafana-github-datasource' + GrafanaDatasourceTypeKey = attribute.Key("grafana.datasource.type") + + // GrafanaDatasourceUidKey is the attribute Key conforming to the + // "grafana.datasource.uid" semantic conventions. It represents the + // datasource unique identifier. + // + // Type: string + // RequirementLevel: Optional + // Stability: stable + // Examples: 'abcdefg-123456' + GrafanaDatasourceUidKey = attribute.Key("grafana.datasource.uid") +) + +// GrafanaDatasourceType returns an attribute KeyValue conforming to the +// "grafana.datasource.type" semantic conventions. It represents the datasource +// type. +func GrafanaDatasourceType(val string) attribute.KeyValue { + return GrafanaDatasourceTypeKey.String(val) +} + +// GrafanaDatasourceUid returns an attribute KeyValue conforming to the +// "grafana.datasource.uid" semantic conventions. It represents the datasource +// unique identifier. +func GrafanaDatasourceUid(val string) attribute.KeyValue { + return GrafanaDatasourceUidKey.String(val) +} + +// Describes Grafana data source request attributes. +const ( + // GrafanaDatasourceRequestQueryCountKey is the attribute Key conforming to + // the "grafana.datasource.request.query_count" semantic conventions. It + // represents the number of queries in the request. + // + // Type: int + // RequirementLevel: Optional + // Stability: stable + // Examples: 3 + GrafanaDatasourceRequestQueryCountKey = attribute.Key("grafana.datasource.request.query_count") +) + +// GrafanaDatasourceRequestQueryCount returns an attribute KeyValue +// conforming to the "grafana.datasource.request.query_count" semantic +// conventions. It represents the number of queries in the request. +func GrafanaDatasourceRequestQueryCount(val int) attribute.KeyValue { + return GrafanaDatasourceRequestQueryCountKey.Int(val) +} + +// Describes Grafana plugin attributes. +const ( + // GrafanaPluginIdKey is the attribute Key conforming to the + // "grafana.plugin.id" semantic conventions. It represents the plugin ID. + // + // Type: string + // RequirementLevel: Optional + // Stability: stable + // Examples: 'prometheus', 'loki', 'grafana-github-datasource' + GrafanaPluginIdKey = attribute.Key("grafana.plugin.id") + + // GrafanaPluginTypeKey is the attribute Key conforming to the + // "grafana.plugin.type" semantic conventions. It represents the plugin + // type. + // + // Type: Enum + // RequirementLevel: Optional + // Stability: stable + // Examples: 'datasource' + GrafanaPluginTypeKey = attribute.Key("grafana.plugin.type") +) + +var ( + // Data Source Plugin + GrafanaPluginTypeDatasource = GrafanaPluginTypeKey.String("datasource") + // Panel Plugin + GrafanaPluginTypePanel = GrafanaPluginTypeKey.String("panel") + // App Plugin + GrafanaPluginTypeApp = GrafanaPluginTypeKey.String("app") + // Renderer Plugin + GrafanaPluginTypeRenderer = GrafanaPluginTypeKey.String("renderer") + // Secret Manager Plugin + GrafanaPluginTypeSecretmanager = GrafanaPluginTypeKey.String("secretmanager") +) + +// GrafanaPluginId returns an attribute KeyValue conforming to the +// "grafana.plugin.id" semantic conventions. It represents the plugin ID. +func GrafanaPluginId(val string) attribute.KeyValue { + return GrafanaPluginIdKey.String(val) +} diff --git a/pkg/semconv/doc.go b/pkg/semconv/doc.go new file mode 100644 index 00000000000..3af6aff37e0 --- /dev/null +++ b/pkg/semconv/doc.go @@ -0,0 +1,5 @@ +// Package semconv contains Grafana's OpenTelemetry semantic conventions. +// +// This package contains the standard attributes that are emitted +// by Grafana's OpenTelemetry instrumentation. +package semconv // import "github.com/grafana/grafana/pkg/semconv" diff --git a/pkg/semconv/go.mod b/pkg/semconv/go.mod new file mode 100644 index 00000000000..52890761c48 --- /dev/null +++ b/pkg/semconv/go.mod @@ -0,0 +1,10 @@ +module github.com/grafana/grafana/pkg/semconv + +go 1.22.4 + +require go.opentelemetry.io/otel v1.28.0 + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect +) diff --git a/pkg/semconv/go.sum b/pkg/semconv/go.sum new file mode 100644 index 00000000000..6601c18dd25 --- /dev/null +++ b/pkg/semconv/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/semconv/model/registry/datasource.yml b/pkg/semconv/model/registry/datasource.yml new file mode 100644 index 00000000000..8cede0698e4 --- /dev/null +++ b/pkg/semconv/model/registry/datasource.yml @@ -0,0 +1,29 @@ +groups: + - id: registry.grafana.datasource + type: attribute_group + display_name: Grafana Data Source Attributes + brief: "Describes Grafana datasource attributes." + attributes: + - id: grafana.datasource.type + type: string + brief: The datasource type. + examples: + - "prometheus" + - "loki" + - "grafana-github-datasource" + stability: stable + - id: grafana.datasource.uid + type: string + brief: The datasource unique identifier. + examples: abcdefg-123456 + stability: stable + - id: registry.grafana.datasource.request + type: attribute_group + display_name: Grafana Data Source Request Attributes + brief: "Describes Grafana data source request attributes." + attributes: + - id: grafana.datasource.request.query_count + type: int + brief: The number of queries in the request. + examples: 3 + stability: stable \ No newline at end of file diff --git a/pkg/semconv/model/registry/plugin.yml b/pkg/semconv/model/registry/plugin.yml new file mode 100644 index 00000000000..89f088fe2cb --- /dev/null +++ b/pkg/semconv/model/registry/plugin.yml @@ -0,0 +1,40 @@ +groups: + - id: registry.grafana.plugin + type: attribute_group + display_name: Grafana Plugin Attributes + brief: "Describes Grafana plugin attributes." + attributes: + - id: grafana.plugin.id + type: string + brief: The plugin ID. + examples: + - "prometheus" + - "loki" + - "grafana-github-datasource" + stability: stable + - id: grafana.plugin.type + type: + members: + - id: datasource + value: "datasource" + brief: 'Data Source Plugin' + stability: stable + - id: panel + value: "panel" + brief: 'Panel Plugin' + stability: stable + - id: app + value: "app" + brief: 'App Plugin' + stability: stable + - id: renderer + value: "renderer" + brief: 'Renderer Plugin' + stability: stable + - id: secretmanager + value: "secretmanager" + brief: 'Secret Manager Plugin' + stability: stable + brief: The plugin type. + examples: datasource + stability: stable \ No newline at end of file diff --git a/pkg/semconv/model/trace/datasource.yaml b/pkg/semconv/model/trace/datasource.yaml new file mode 100644 index 00000000000..4b15f9b0385 --- /dev/null +++ b/pkg/semconv/model/trace/datasource.yaml @@ -0,0 +1,15 @@ +groups: + - id: trace.grafana.datasource + type: span + brief: 'Semantic Convention for Grafana datasources' + stability: stable + attributes: + - ref: grafana.datasource.type + - ref: grafana.datasource.uid + - id: trace.grafana.datasource.request + type: span + span_kind: client + brief: 'Semantic Convention for Grafana datasource requests' + stability: stable + attributes: + - ref: grafana.datasource.request.query_count \ No newline at end of file diff --git a/pkg/semconv/model/trace/plugin.yaml b/pkg/semconv/model/trace/plugin.yaml new file mode 100644 index 00000000000..f25e894efbc --- /dev/null +++ b/pkg/semconv/model/trace/plugin.yaml @@ -0,0 +1,8 @@ +groups: + - id: trace.grafana.plugin + type: span + brief: 'Semantic Convention for Grafana plugins' + stability: stable + attributes: + - ref: grafana.plugin.id + - ref: grafana.plugin.type \ No newline at end of file diff --git a/pkg/semconv/templates/template.j2 b/pkg/semconv/templates/template.j2 new file mode 100644 index 00000000000..597c3b7e374 --- /dev/null +++ b/pkg/semconv/templates/template.j2 @@ -0,0 +1,142 @@ +{%- macro keyval_method(type) -%} + {%- if type == "string" -%} + String + {%- elif type == "string[]" -%} + StringSlice + {%- elif type == "int" -%} + Int + {%- elif type == "int[]" -%} + IntSlice + {%- elif type == "double" -%} + Float64 + {%- elif type == "double[]" -%} + Float64Slice + {%- elif type == "boolean" -%} + Bool + {%- elif type == "boolean[]" -%} + BoolSlice + {%- endif -%} +{%- endmacro -%} +{%- macro to_go_attr_type(type, val) -%} +{{keyval_method(type)}}({% if type == "string" %}"{{val}}"{% else %}{{val}}{% endif %}) +{%- endmacro -%} +{%- macro to_go_name(fqn) -%} +{{fqn | replace(".", " ") | replace("_", " ") | title | replace(" ", "")}} +{%- endmacro -%} +{%- macro it_reps(brief) -%} +It represents {% if brief[:2] == "A " or brief[:3] == "An " or brief[:4] == "The " -%} + {{ brief[0]|lower }}{{ brief[1:] }} +{%- else -%} + the {{ brief[0]|lower }}{{ brief[1:] }} +{%- endif -%} +{%- endmacro -%} +{%- macro keydoc(attr) -%} +{%- if attr.stability|string() == "StabilityLevel.DEPRECATED" -%} +{{ to_go_name(attr.fqn) }}Key is the attribute Key conforming to the "{{ attr.fqn }}" semantic conventions. +{%- else -%} +{{ to_go_name(attr.fqn) }}Key is the attribute Key conforming to the "{{ attr.fqn }}" semantic conventions. {{ it_reps(attr.brief) }} +{%- endif %} +{%- endmacro -%} +{%- macro keydetails(attr) -%} +{%- if attr.attr_type is string %} +Type: {{ attr.attr_type }} +{%- else %} +Type: Enum +{%- endif %} +{%- if attr.requirement_level == RequirementLevel.REQUIRED %} +RequirementLevel: Required +{%- elif attr.requirement_level == RequirementLevel.CONDITIONALLY_REQUIRED %} +RequirementLevel: ConditionallyRequired + {%- if attr.requirement_level_msg != "" %} ({{ attr.requirement_level_msg }}){%- endif %} +{%- elif attr.requirement_level == RequirementLevel.RECOMMENDED %} +RequirementLevel: Recommended + {%- if attr.requirement_level_msg != "" %} ({{ attr.requirement_level_msg }}){%- endif %} +{%- else %} +RequirementLevel: Optional +{%- endif %} +{{ attr.stability | replace("Level.", ": ") | capitalize }} +{%- if attr.examples is iterable %} +Examples: {{ attr.examples | pprint | trim("[]") }} +{%- endif %} +{%- if attr.note %} +Note: {{ attr.note }} +{%- endif %} +{%- if attr.stability|string() == "StabilityLevel.DEPRECATED" %} +Deprecated: {{ attr.brief | replace("Deprecated, ", "") }} +{%- endif %} +{%- endmacro -%} +{%- macro fndoc(attr) -%} +{%- if attr.stability|string() == "StabilityLevel.DEPRECATED" -%} +// {{ to_go_name(attr.fqn) }} returns an attribute KeyValue conforming to the "{{ attr.fqn }}" semantic conventions. + +Deprecated: {{ attr.brief | replace("Deprecated, ", "") }} +{%- else -%} +// {{ to_go_name(attr.fqn) }} returns an attribute KeyValue conforming to the "{{ attr.fqn }}" semantic conventions. {{ it_reps(attr.brief) }} +{%- endif %} +{%- endmacro -%} +{%- macro to_go_func(type, name) -%} +{%- if type == "string" -%} +func {{name}}(val string) attribute.KeyValue { +{%- elif type == "string[]" -%} +func {{name}}(val ...string) attribute.KeyValue { +{%- elif type == "int" -%} +func {{name}}(val int) attribute.KeyValue { +{%- elif type == "int[]" -%} +func {{name}}(val ...int) attribute.KeyValue { +{%- elif type == "double" -%} +func {{name}}(val float64) attribute.KeyValue { +{%- elif type == "double[]" -%} +func {{name}}(val ...float64) attribute.KeyValue { +{%- elif type == "boolean" -%} +func {{name}}(val bool) attribute.KeyValue { +{%- elif type == "boolean[]" -%} +func {{name}}(val ...bool) attribute.KeyValue { +{%- endif -%} + return {{name}}Key.{{keyval_method(type)}}(val) +} +{%- endmacro -%} +{%- macro sentence_case(text) -%} + {{ text[0]|upper}}{{text[1:] }} +{%- endmacro -%} +// Code generated from semantic convention specification. DO NOT EDIT. + +package semconv + +import "go.opentelemetry.io/otel/attribute" + +{% for semconv in semconvs -%} +{%- if semconvs[semconv].attributes | rejectattr("ref") | rejectattr("deprecated") | selectattr("is_local") | sort(attribute=fqn) | length > 0 -%} +// {{ sentence_case(semconvs[semconv].brief | replace("This document defines ", "")) | wordwrap(76, break_long_words=false, break_on_hyphens=false, wrapstring="\n// ") }} +const ( +{%- for attr in semconvs[semconv].attributes if attr.is_local and not attr.ref and not attr.deprecated %} + // {{ keydoc(attr) | wordwrap(72, break_long_words=false, break_on_hyphens=false, wrapstring="\n\t// ") }} + // {{ keydetails(attr) | wordwrap(72, break_long_words=false, break_on_hyphens=false, wrapstring="\n\t// ") }} + {{to_go_name(attr.fqn)}}Key = attribute.Key("{{attr.fqn}}") +{% endfor -%} +) +{%- for attr in semconvs[semconv].attributes if attr.is_local and not attr.ref and not attr.deprecated -%} +{%- if attr.attr_type is not string %} + +var ( +{%- for val in attr.attr_type.members %} + // {{ val.brief | to_doc_brief }} +{%- if attr.stability|string() == "StabilityLevel.DEPRECATED" %} + // + // Deprecated: {{ attr.brief | replace("Deprecated, ", "") | wordwrap(76, break_long_words=false, break_on_hyphens=false, wrapstring="\n// ") }} +{%- endif %} + {{to_go_name("{}.{}".format(attr.fqn, val.member_id))}} = {{to_go_name(attr.fqn)}}Key.{{to_go_attr_type(attr.attr_type.enum_type, val.value)}} +{%- endfor %} +) +{%- endif -%} +{%- endfor %} +{%- for attr in semconvs[semconv].attributes if attr.is_local and not attr.ref and not attr.deprecated -%} +{%- if attr.attr_type is string %} + +{{ fndoc(attr) | wordwrap(76, break_long_words=false, break_on_hyphens=false, wrapstring="\n// ") }} +{{to_go_func(attr.attr_type, to_go_name(attr.fqn))}} +{%- endif -%} +{%- endfor %} + +{% endif %} +{% endfor -%} +