Governing Semantic Conventions with Weaver

Originally published in Japanese at https://zenn.dev/ymotongpoo/books/observability-platform-with-otel/viewer/50-semconv_governance.

In OpenTelemetry, the attribute name for the HTTP status code changed from http.status_code to http.response.status_code. The rename came when the HTTP semantic conventions became stable. Some organizations ran several SDK versions during the migration period. To check error rates by status code, they needed queries that used both the old and the new attribute names. If you search for only one of them, your dashboards and alerts miss some of the services.

Even the official conventions go through migrations like this one. The attributes that each team adds on its own produce many more inconsistent spellings: for example, user_id, userId, and user.id, or env, environment, and deployment.environment.name. You can standardize the instrumentation and the Collector, but you still cannot search across services unless the attributes mean the same thing. The third pillar governs this meaning.

An ungoverned schema

The damage from inconsistent attribute names goes beyond queries that are hard to write.

  • Dashboards and alerts depend on the schema implicitly. When someone renames an attribute, the alert condition no longer matches, and the alert stops firing
  • The meaning of an attribute collides between teams. One team’s status is the state of an order, and another team’s status is the HTTP status. When you aggregate them, you get meaningless numbers
  • You cannot control high cardinality. A metric gets a new time series for each combination of label values. If you use an attribute with many distinct values, such as a user ID, as a label, the storage volume grows fast
  • The attribute processing at the gateway has no shared basis. The list of attributes to drop grows only from individual incident responses, not from the conventions

These problems occur because the names, meanings, and types of attributes are scattered across code and dashboards, with no shared definition. The name for the approach that generates code, documentation, and check rules from one definition is Schema-as-Code.

The structure of the official semantic conventions

OpenTelemetry defines the names and meanings of attributes as semantic conventions. The project manages definitions such as http.response.status_code and service.name in a YAML registry. From the registry, it generates the documentation and a constant package for each language.

The unit of structure in the registry is the group. There are several kinds of group: attribute_group defines a set of attributes, span defines span conventions, and metric defines metrics. Each attribute has a type, a description, and a stability level (stable, release_candidate, development, and so on). The conventions as a whole have a version, and the telemetry itself carries that version as a schema URL.

The official conventions also keep changing. As of September 2026, the stability differs from area to area. The OpenTelemetry project uses OpenTelemetry Weaver to check the conventions, to generate artifacts from them, and to detect differences between versions. CI checks the more than 900 attributes in the official registry with it1.

Weaver is a command-line tool that takes the YAML of this registry as input. The OpenTelemetry project develops it in Rust. It does four things.

  • Check (check): tests whether the definitions follow the syntax and the naming rules
  • Diff (diff): compares two versions and finds breaking changes such as renames and removals
  • Generate (generate): outputs code and documentation from the definitions
  • Live check (live-check): tests whether the telemetry that actually flows matches the definitions

In short, Weaver is the tool that carries out the Schema-as-Code approach from the previous section. The same tool serves the official conventions and an organization that maintains its own conventions. The following sections explain how to build an internal registry and bring these four operations into the processes of your organization.

Designing an internal namespace

The internal conventions add only the concepts that are specific to the organization and absent from the official conventions.

For a concept that the official conventions cover, use the official attribute. For example, do not add your own attribute name for the HTTP status. For a concept specific to your organization, use a namespace in reverse-domain form so that it does not collide with the official conventions. This book uses com.example.* and defines the delivery ID as com.example.delivery.id.

The internal registry writes this rule down in a form that a tool can check. A registry in OpenTelemetry Weaver consists of group definitions in YAML and a manifest. The manifest (manifest.yaml) declares the name and version of the registry and the official registry that it depends on2.

name: example
description: Internal semantic conventions registry
schema_url: https://schemas.example.com/1.0.0
dependencies:
  - schema_url: https://opentelemetry.io/schemas/1.44.0
    registry_path: https://github.com/open-telemetry/semantic-conventions@v1.44.0[model]

The manifest declares the official registry, with its version, as a dependency. Weaver then resolves the internal registry as “the internal definitions layered on top of the official v1.44.0.” You write the attribute definitions in the YAML of a group.

groups:
  - id: registry.com.example.delivery
    type: attribute_group
    display_name: Delivery Attributes
    brief: Attributes of the delivery domain
    attributes:
      - id: com.example.delivery.id
        type: string
        stability: development
        brief: ID that uniquely identifies a delivery
        examples: ["dlv-2026-000123"]
      - id: com.example.delivery.carrier
        type: string
        stability: development
        brief: Identifier of the delivery carrier
        examples: ["carrier-a"]

You can also reference an official attribute in an internal context. For example, you pull in an official attribute with ref and override only its requirement level for internal use. Dependencies can have several layers (up to 10 layers as of September 2026). For example, you can build a division registry on top of a company-wide registry.

Dependencies between registries Figure 1: Each arrow points from a registry to the registry that it depends on. The internal registry depends on the official registry. You pin conventions in the Development stage, such as the GenAI conventions, to a commit SHA (Chapter 7).

Managing the schema with Weaver

You use the Weaver subcommands at three points: before a convention change, after the merge, and at run time. Before the change, you use check and diff. After the merge, you use generate. At run time, you use live-check3.

weaver registry check checks the syntax and the references. It also applies policies that you write in Rego, the language of OPA (Open Policy Agent). For example, it can check rules such as “do not create new attributes outside the com.example. namespace” and “do not change the type of a stable attribute.” The official opentelemetry-weaver-packages repository publishes policies for naming rules, stability constraints, and backward compatibility. You can specify them by Git URL.

$ weaver registry check -r ./registry \
    -p https://github.com/open-telemetry/opentelemetry-weaver-packages.git[policies/check/naming_conventions]

weaver registry diff compares the registry in a PR against a baseline such as the main branch. It outputs the additions, renames, removals, and type changes of attributes as a structured diff. You can detect breaking changes mechanically and send them for explicit approval.

weaver registry generate generates artifacts from the registry with minijinja templates and jq-style filters. In this book, I generate a Go package of constants for the internal attributes and include it in the distribution from Chapter 2. Development teams use the generated constants. They do not hand-write strings, as in attribute.String("com.example.delivery.id", id).

However, Go’s attribute.String accepts any string. The generated constants therefore cannot forbid hand-written strings, and a typo does not cause a compile error. The generated constants reduce the chances to hand-write a name, and they let you pick an available attribute from IDE completion. To forbid hand-written names, you need an internal wrapper API that does not accept strings, or static analysis. You then detect the remaining violations in the actual telemetry with live-check.

You can write your own templates. The semconv/templates in opentelemetry-go use Weaver to generate the official Go semconv package. If you also generate Markdown documentation from the same registry, you update the definitions and their descriptions together.

The cycle of schema management with Weaver Figure 2: The arrows show the flow of the steps. You use check and diff before a change, generate after the merge, and live-check at run time. The violations that you find feed back into changes to the registry.

Checking live data with live-check

The definitions and the generated artifacts can match, and the actual telemetry can still break the conventions. Zero-code instrumentation can emit old attribute names. Code can remain that hand-writes attribute names instead of using the generated constants. weaver registry live-check compares the definitions with the actual telemetry.

The live-check command itself acts as an OTLP receiver. It receives telemetry over gRPC and compares the attributes of spans, metrics, logs, and resources against the registry. It reports unregistered attributes, type mismatches, the use of deprecated attributes, and similar problems. You can add Rego policies to the evaluation. When live-check finds a violation, it exits with a nonzero code, so you can build it into CI.

In the integration tests of a service, you temporarily point the telemetry destination at live-check. It checks the telemetry that the tests produce, and it fails the test when it finds a convention violation. This way, CI detects instrumentation violations before anyone notices them on a production dashboard.

Integrating with CI

The CI for the registry runs the checks and the generation in this order.

  • On a PR that changes the registry, run check with the policies. Naming and structure violations stop here
  • Also run diff against the baseline (the registry on the main branch). If diff finds a breaking change, label the PR and require explicit approval
  • After the merge, run generate to regenerate the constant packages for each language and the documentation. Then open an update PR to the distribution (Chapter 2) automatically
  • If needed, also generate the attribute transformation configuration of the gateway (Chapter 4) from the same registry. You can derive the transform configuration that rewrites deprecated attributes to their new names mechanically, from the rename information. As Chapter 4 describes, generate each transformation with its input schema URL and output schema URL as a pair. This prevents an inconsistency where the attributes change but the declared schema URL stays old

You can generate the SDK constants, the documentation, and the Collector transformation configuration from one registry. Then you no longer apply the same change by hand in separate places. The generated artifacts reach each environment through the paths that distribute the SDK distribution and the Collector configuration.

Governance as an organizational process

Even with automated checks, people decide the meaning and the granularity of the attributes to add.

A development team opens a PR against the registry, and check and diff in CI test its structure and compatibility. In the review, the reviewers look for overlap in meaning with existing attributes and confirm the granularity of the values. To keep every attribute review from landing on the platform team, document what to confirm and delegate the review to an owner for each domain.

To retire an attribute, mark it deprecated in the registry and name its replacement. During the migration period, the gateway converts the old attribute to the new attribute. After the dashboards migrate, delete the old definition. If you emit the old and the new attributes side by side, include three things in the migration plan: which attribute is authoritative, the date the dashboards switch, and the date you delete the old attribute.

The weaver registry mcp subcommand of Weaver exposes the registry to LLMs over MCP (Model Context Protocol). MCP is a connection standard that gives LLMs access to external data and operations. Chapter 8 uses this connection to pass attribute definitions to an AI agent.

The platform provides the registry, the generated artifacts, and the checks. Development teams decide the meaning of the domain attributes. When a team needs a new attribute, the team changes the definition through a PR to the registry.


  1. The official blog post Observability by Design explains that the project uses Weaver to maintain the official semconv itself. ↩︎

  2. Older material names the manifest file registry_manifest.yaml. That is the old name. The current name is manifest.yaml (as of September 2026, Weaver v0.26.x). ↩︎

  3. As of September 2026, Weaver is at v0.26.1 and has not reached 1.0. The weaver registry resolve and search commands that existed before are now deprecated. Be careful with the command examples in older articles. ↩︎