otel-weaver
OpenTelemetry Weaver registry authoring, codegen, and CI enforcement. Use when adopting Weaver, authoring or reviewing a registry (manifest, attributes, metrics, spans, events), writing Jinja2 templates against the resolved schema, migrating hand-maintained telemetry constants, w
Install
npx skills add https://github.com/ollygarden/opentelemetry-agent-skills/tree/main/skills/otel-weaver
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install ollygarden-opentelemetry-agent-skills@llmmart
git clone https://github.com/ollygarden/opentelemetry-agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole ollygarden/opentelemetry-agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
OpenTelemetry Weaver
Use this skill when an organization wants to define its own semantic conventions on top of upstream OTel and generate language bindings from them.
Usage:
- pair with
otel-semantic-conventionsto decide which attributes already exist upstream and should not be redeclared in the local registry - latest Weaver version: the
otel-sdk-versionsindex if it lists Weaver, else https://github.com/open-telemetry/weaver/releases;references/ci-integration.mdkeeps a dated Weaver/semconv compatibility record for offline answers
If a companion skill is unavailable:
- do not stop
- do not rely on memory alone when the guidance can be checked from official sources
- consult the Weaver repo,
schemas/semconv-syntax.v2.md, anddocs/usage.md/docs/validate.md - state which fallback you used and leave any unverified item unresolved
Mental Model
Three moving parts:
- Registry — directory of YAML files.
manifest.yamlis required; itsschema_url(OTel schema URL format,http[s]://host/path/<version>) both names the registry and carries its version in the final path segment. Dependency entries also requireschema_urlplus optionalregistry_path. The rest declareattributes,metrics,spans,events,entities. The version segment ofschema_urlis yours to manage; bump it on changes. (semconv_versionandschema_base_urlare deprecated in favor ofschema_url; top-levelnameis not a v0.26.1 manifest field.) - Templates — directory of MiniJinja files (Jinja2-compatible, not full Jinja2 — auto-escaping is off by default since v0.22.0 and loop
break/continueare supported) plus aweaver.yamlper target language describing which templates to run, with what filter, in whatapplication_mode, and with what output filename. - Policies — Rego rules evaluated by the Regorus (OPA-compatible) engine, in four packages:
before_resolution(raw parsed groups; unsupported and skipped with--v2),after_resolution(resolved registry),comparison_after_resolution(only when--baseline-registryis passed), andlive_check_advice(per-sample duringlive-check). Built-in OTel policies are the floor; custom policies layer on org rules.
These three replace a hand-rolled const.go (or equivalent): const blocks become the registry, the act of writing them becomes codegen, and tribal knowledge becomes policies.
Non-Negotiable Rules
- Install Weaver via one of the methods documented at https://github.com/open-telemetry/weaver#install (release binary,
otel/weaver:vX.Y.ZDocker image, or thesetup-weaverGitHub Action). Neverbrew install weaver— that resolves to an unrelated Scribd tool. - Reference upstream semconv attributes by
refrather than redeclaring them. Boundary domains (http,db,messaging,rpc,network,gen-ai, ...) belong in upstream OTel semconv, not in a local registry. Use the language SDK's semconv package for those at runtime. - Every attribute and signal definition needs
stability; include it on enum members too, as required by the v2 syntax guide. Weaver v0.26.1 rejects missing definition stability but reports missing enum-member stability only as a non-fatal warning in normal mode. - In v2, an attribute reference may refine fields such as
brief,note,examples, andannotations, but notstabilityordeprecated; those always come from the attribute definition. - Use a domain prefix (e.g.
ecommerce.,acme.) for org-local attributes, metrics, and spans. - Run the language formatter (
gofmt -w,prettier,ruff format, ...) on generated output. Jinja whitespace produces multiple blank lines; without formatting, the diff check in CI will fail spuriously. - Confirm the resolved schema shape before writing a template. For a
definition/2registry, call the grouped jq helpers with{"v2": true}; the v2 templatectxpreserves fields such as attributekey, metricname, spantype/kindand structuredspan.name.note, plus entitytype/identity/description. Seereferences/template-authoring.mdfor how to dump the exact shape.
Workflow
- Install or locate Weaver. Follow the upstream install instructions at https://github.com/open-telemetry/weaver#install — pick a pinned release binary, the
otel/weaver:vX.Y.ZDocker image, or thesetup-weaverGitHub Action. Use Docker for CI and reproducible local runs. - Author the registry. Required:
manifest.yamlplus one or moredefinition/2YAML files declaring attributes, attribute groups, metrics, spans, events, or entities. Seereferences/registry-authoring.md. - Author templates. One target dir per language under
templates/registry/<lang>/withweaver.yamlplus*.j2. Seereferences/template-authoring.md. - Validate and generate.
weaver registry check --v2 -r ./telemetry/registry/for fast feedback.weaver registry generate --v2 --registry ./telemetry/registry/ --templates ./telemetry/templates/ <lang> <output-dir>for codegen. Run the language formatter on the output. - Wire into CI. Three gates:
check(schema),generate+git diff --exit-code(checked-in code is current),diffagainst the base branch (surfaces breaking changes). Seereferences/ci-integration.md.
The Weaver CLI has more subcommands than this workflow touches: stats and json-schema for quick registry sanity checks, update-markdown for keeping semconv snippets in docs current, emit/live-check/infer for working against live OTLP telemetry, package for writing a self-contained resolved registry artifact (the replacement for the deprecated resolve), mcp for exposing a registry to LLM tooling, and serve for an HTTP+UI mode. All are out of scope here (see below) but worth knowing exist before assuming check/generate/diff is the whole surface.
Gotchas
These cost time and are not obvious from the upstream docs:
brew install weaverinstalls the wrong tool. Use GitHub releases or Docker.- Generated output is not formatter-clean. Always run the language formatter after
weaver registry generate. - Jq helper defaults target the legacy schema. For
definition/2, pass{"v2": true}and use the preserved v2 fields: attributekey, metricname, spantype/kindandspan.name.note, plus entitytype/identity/description. Always generate and inspect the filteredctxbefore writing a template. - The
commentJinja filter takes a keyword argument:attr.brief | comment(format="go"). It already emits the//prefix; do not add another. - Attributes, metrics, spans, events, and entities all have prebuilt grouped jq filters (
semconv_grouped_attributes,semconv_grouped_metrics,semconv_grouped_spans,semconv_grouped_events,semconv_grouped_entities). Fordefinition/2, use (for example)semconv_grouped_spans({"v2": true})as a folded YAML scalar; the bare helper defaults select the legacy schema. weaver registry checkemits "File formatdefinition/2is not yet stable" (a warning) for custom v2 definition files (still so in the dated record inreferences/ci-integration.md). This is normal; do not treat it as a failure.--futureis opt-in but still elevates thedefinition/2instability warning to an error for a custom v2 registry. Leave it off until the format goes stable.- CLI argument ordering for
generate: target directory name is positional after--registryand--templates; the output directory follows.--templatespoints at the parent that contains target dirs, not at the language-specific subdir. - Span name in registry vs. runtime: required schema fields are
type,kind(client/server/producer/consumer/internal),brief,stability, and a structuredname: { note: "..." }. For internal business spans, putting the dotted type identifier inname.noteand rendering the resolvedspan.name.notestring at runtime is clean. - What does NOT belong in your local registry. DB, HTTP, messaging, RPC, network, GenAI, and similar boundary spans/attributes follow upstream OTel semconv. Until upstream is pulled in as a manifest dependency, instrumentation for those should reference the language SDK's semconv package directly. This is the most common modeling mistake.
- Counter and UpDownCounter names should not append
_total; this is the semconv v1.44.0 naming rule. - Duration instruments should use seconds (
s) under the semconv v1.44.0 unit guidance.
References To Load On Demand
- registry YAML field reference:
references/registry-authoring.md - Jinja2 patterns, jq filters, resolved-shape cheat sheet:
references/template-authoring.md - ready-to-lift GitHub Actions example and the dated version-compatibility record:
references/ci-integration.md - hand-maintained-constants → registry walkthrough:
references/migration-playbook.md - semantic conventions skill:
otel-semantic-conventions - manual instrumentation skill:
manual-instrumentation
Out Of Scope
These are natural follow-ups but not part of this skill:
- publishing the registry as a versioned artifact for downstream consumers (
weaver registry package) - declaring upstream semantic-conventions as a manifest dependency
weaver registry live-check/emit/inferagainst live OTLP telemetryweaver registry mcp/weaver serve- custom Rego policies beyond the built-ins
- helper-function codegen (
MyMetricName(meter)wrappers)
Verification Contract
If you authored or modified a Weaver registry, templates, or CI integration:
- re-open the changed files before finishing
- run
weaver registry check --v2against thedefinition/2registry and capture the result - run
weaver registry generate --v2and the language formatter, then verifygit diff --exit-codeis clean - confirm each applicable item with codebase evidence
Report the final check with:
[x]completed[~]not applicable, with a reason[ ]unresolved
Use these items:
- registry has
manifest.yamlwith aschema_urlwhose final path segment is the version - every definition and enum member has
stability - org-local attributes/metrics/spans use a domain prefix
- no boundary-domain (http/db/messaging/rpc/network/gen-ai) entries duplicated locally
- Counter and UpDownCounter names have no
_totalsuffix - duration histograms use
s(seconds) - templates use jq filters that match the resolved schema (for
definition/2, call the prebuiltsemconv_grouped_*helpers with{"v2": true}) - generated output is formatter-clean
- CI runs
check,generate+git diff --exit-code, anddiffagainst the base branch - changed files were re-read
- remaining risks or gaps are stated
Files (opentelemetry-agent-skills)
-
evals
-
evals.json 1.1 KB
{ "skill": "otel-weaver", "cases": [ { "id": "offline-weaver-semconv-compatibility", "prompt": "No web lookups please, just your knowledge and skills. Our CI pins otel/weaver:v0.26.1 for a custom definition/2 registry, and we want to depend on semantic-conventions v1.44.0. Which Weaver version did upstream semantic-conventions v1.44.0 itself use, what is the newest Weaver release you know of, is definition/2 stable there, and can we turn on --future in CI?", "files": [], "expectations": [ "Says semantic-conventions v1.44.0 pinned Weaver v0.25.1.", "Names v0.28.0 as the newest Weaver release known, as of the recorded date rather than as currently latest.", "Says definition/2 is still not stable through v0.28.0 and each definition/2 file produces a warning.", "Advises against enabling --future because it turns the definition/2 warning into an error.", "Suggests confirming against the Weaver release notes or changelog before upgrading the CI pin.", "Does not perform any web search, web fetch, or network command." ] } ] }
-
-
references
-
ci-integration.md 4.6 KB
# CI Integration Three gates protect the telemetry contract: 1. **Schema check** — registry parses and validates. 2. **Codegen freshness** — checked-in generated files match what `weaver` produces today. 3. **Breaking-change diff** — surfaces removed/modified entries against the base branch. ## GitHub Actions example ```yaml name: telemetry on: pull_request: paths: - "telemetry/**" - "internal/telemetry/**_gen.go" - ".github/workflows/telemetry.yml" jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Validate the registry env: WORKSPACE: ${{ github.workspace }} run: | docker run --rm \ -v "$WORKSPACE:/work" \ otel/weaver:v0.26.1 \ registry check \ --v2 \ --registry /work/telemetry/registry/ - name: Generate and verify checked-in code is current env: WORKSPACE: ${{ github.workspace }} run: | docker run --rm \ -v "$WORKSPACE:/work" \ otel/weaver:v0.26.1 \ registry generate \ --v2 \ --registry /work/telemetry/registry/ \ --templates /work/telemetry/templates/ \ go \ /work/internal/telemetry/ gofmt -w internal/telemetry/ git diff --exit-code internal/telemetry/ - name: Diff against base branch if: github.event_name == 'pull_request' env: WORKSPACE: ${{ github.workspace }} BASE_REF: ${{ github.base_ref }} run: | git fetch origin "$BASE_REF":__base git worktree add /tmp/base __base docker run --rm \ -v "$WORKSPACE:/work" \ -v /tmp/base:/baseline \ otel/weaver:v0.26.1 \ registry diff \ --v2 \ --baseline-registry /baseline/telemetry/registry/ \ --registry /work/telemetry/registry/ \ --format markdown ``` ## Notes - **Pin the Weaver version.** `otel/weaver:v0.26.1` (or the version your registry was authored against). Don't use `latest` — schema validation behavior changes between versions. - **Format before diffing.** Without `gofmt -w` (or your language's formatter), Jinja whitespace produces multi-blank-line diffs that fail the `git diff --exit-code` gate spuriously. - **`--future` is opt-in but still breaks custom `definition/2` registries.** It elevates the format's own instability warning to an error ([Version compatibility](#version-compatibility)). Re-enable it once the format goes stable. - **Expected diagnostic noise.** `weaver registry check` emits `File format definition/2 is not yet stable` for each custom v2 definition file. Do not treat these warnings as a failure. - **The diff job is informational.** It produces markdown breaking-change output; failing the build on it is too aggressive while a registry is young. Promote to a hard gate once the registry stabilizes. ## Version compatibility ### Verified 2026-10-15 Offline, cite this date, never call a value currently latest, and confirm it against the evidence before deploying. | Fact | Value | |---|---| | This skill's examples | authored against `v0.26.1`; the next row re-checked at `v0.28.0` (released 2026-10-14) | | `definition/2` status | not stable through `v0.28.0`: each file warns `File format definition/2 is not yet stable`; `--future` turns that warning into an error | | Weaver pinned by semantic-conventions | `v1.43.0`: `v0.24.2`; `v1.44.0`: `v0.25.1`; `main`: `v0.28.0` | | semconv `v1.44.0` model with Weaver `v0.28.0` | `weaver registry check -r model` exits 0; its `definition/2` files only warn | Changelog-marked breaking changes after `v0.26.1` are in `registry live-check`, `registry infer`, and `registry mcp` (`v0.27.0`); `v0.28.0` warns when an entity `identity` reference sets `requirement_level`. Evidence: Weaver source and `CHANGELOG.md` at `v0.28.0`; `dependencies.Dockerfile` at each semantic-conventions tag; a `v0.28.0` release-binary run on the `v1.44.0` tag. ## Local equivalent ```bash docker run --rm -v "$PWD:/work" otel/weaver:v0.26.1 \ registry check --v2 --registry /work/telemetry/registry/ docker run --rm -v "$PWD:/work" otel/weaver:v0.26.1 \ registry generate \ --v2 \ --registry /work/telemetry/registry/ \ --templates /work/telemetry/templates/ \ go \ /work/internal/telemetry/ gofmt -w internal/telemetry/ git diff -- internal/telemetry/ ``` A pinned local binary from the [Weaver releases page](https://github.com/open-telemetry/weaver/releases) avoids the Docker startup cost while developing. -
migration-playbook.md 4.3 KB
# Migration Playbook: Hand-Maintained Constants → Weaver Registry Use this playbook when an application has a hand-rolled file like `const.go`, `telemetry/constants.py`, or `Telemetry.java` declaring attribute keys, metric names, and span names — and you want to replace it with a generated, schema-backed package. The walkthrough uses Go conventions; mappings to other languages are straightforward. ## Inventory the existing constants For each constant in the legacy file, classify it: | Kind | Action | |--------------------------------------|-------------------------------------------------------------| | Org-local attribute (`ecommerce.*`) | Move to `attributes.yaml`. | | Org-local metric name + unit | Move to `metrics.yaml` with `instrument` and `unit`. | | Org-local span name | Move to `spans.yaml` with `kind` and `name.note`. | | HTTP/db/messaging/rpc/network/gen-ai | **Delete.** Replace call sites with the SDK semconv package. | | Free-form log keys, config flags | Out of scope for Weaver; leave them. | This is the most common modeling mistake: do not redeclare upstream-domain attributes locally. Use the language SDK's semconv package directly. ## Author the registry Build the four files: - `telemetry/registry/manifest.yaml` - `telemetry/registry/attributes.yaml` - `telemetry/registry/metrics.yaml` - `telemetry/registry/spans.yaml` See `references/registry-authoring.md` for field reference and a working sample. While moving entries, normalize: - Counter and UpDownCounter names: drop an appended `_total` - duration histograms: convert `ms` → `s` and rescale bucket boundaries (e.g. `0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5`) - enum-shaped strings: declare them as enum types so consumers see allowed values Validate as you go: ``` weaver registry check --v2 -r ./telemetry/registry/ ``` ## Author templates Copy the Go templates from `references/template-authoring.md` into `telemetry/templates/registry/go/`. Adjust: - `params.package_name` to your target package - the output filenames (`attributes_gen.go`, `metrics_gen.go`, `spans_gen.go`) — keep the `_gen.go` suffix so it's grep-obvious that they are generated ## Generate and format ``` weaver registry generate \ --v2 \ --registry ./telemetry/registry/ \ --templates ./telemetry/templates/ \ go \ ./internal/telemetry/ gofmt -w ./internal/telemetry/ ``` The generated `attributes_gen.go`, `metrics_gen.go`, `spans_gen.go` should compile cleanly alongside whatever non-generated code remains in the package (`setup.go`, helpers, etc.). ## Switch call sites Replace references one constant at a time: | Before | After | |-------------------------------------------|--------------------------------------------------------| | `telemetry.AttrUserID` | `telemetry.AttrEcommerceUserId` | | `"ecommerce.orders.processing.duration"` | `telemetry.EcommerceOrdersProcessingDurationName` | | `"ecommerce.order.process"` | `telemetry.SpanEcommerceOrderProcessName` | | `semconv.HTTPMethodKey` (already correct) | unchanged — keep using the SDK semconv package | For instrumentation hygiene as you do this, pair with the `manual-instrumentation` skill. ## Delete the legacy file Once all call sites compile against the generated symbols, delete the hand-maintained `const.go`. Run the test suite. Inspect a sample trace and metric to confirm names and attributes still look right end-to-end. ## Wire CI See `references/ci-integration.md`. Ship the workflow in the same PR as the migration so the contract is enforced from day one. ## Verify before declaring victory - `weaver registry check --v2` returns success (modulo the expected `definition/2` warnings) - `weaver registry generate --v2` followed by the language formatter leaves `git diff --exit-code` clean - the application builds and tests pass - a sample trace shows org-local span names and attributes from the generated symbols - a sample metric shows the renamed/rescaled units and no leftover `_total` suffix -
registry-authoring.md 7.9 KB
# Registry Authoring Authoritative schema reference: [`schemas/semconv-syntax.v2.md`](https://github.com/open-telemetry/weaver/blob/main/schemas/semconv-syntax.v2.md) in the Weaver repo. Use this file as a quick map; do not duplicate field tables. ## Layout ``` telemetry/registry/ ├── manifest.yaml # required ├── attributes.yaml # optional ├── metrics.yaml # optional ├── spans.yaml # optional ├── events.yaml # optional └── entities.yaml # optional ``` Files other than `manifest.yaml` are discovered by their `file_format: definition/2` header and the top-level array key (`attributes`, `metrics`, `spans`, ...). ## `manifest.yaml` Minimal example: ```yaml schema_url: https://example.com/schemas/ecommerce/0.1.0 description: "Telemetry conventions for the ecommerce monolith" stability: development ``` `schema_url` is required and must follow the OTel schema URL format `http[s]://host/path/<version>`. The registry name is derived from the path (`example.com/schemas/ecommerce`) and the version from the final segment (`0.1.0`) — bump that segment on any schema change. Pick a stable URL even if it does not yet resolve. `description`, `stability`, and `dependencies` are optional. Dependency entries require `schema_url` and may add `registry_path` for the local, archive, or Git location; a legacy v1 manifest may instead identify a dependency by `name` plus `registry_path`. The older top-level `semconv_version`/`schema_base_url` pair is deprecated; top-level `name` is not a v0.26.1 v2 manifest field. ## Attributes ```yaml file_format: definition/2 attributes: - key: ecommerce.user.id type: string stability: stable brief: "Identifier of the customer performing the operation." examples: ['user-9f3a', 'user-1c2d'] - key: ecommerce.customer.tier type: string stability: stable brief: "Customer tier used when the order was placed." examples: ['standard', 'premium'] - key: ecommerce.order.id type: string stability: stable brief: "Identifier of the order being processed." examples: ['order-8f2a', 'order-4d1c'] - key: ecommerce.warehouse.id type: string stability: development brief: "Identifier of the warehouse fulfilling the order." examples: ['warehouse-1', 'warehouse-2'] - key: ecommerce.payment.method type: members: - id: credit_card value: credit_card stability: stable brief: "Credit or debit card" - id: paypal value: paypal stability: stable brief: "PayPal account" stability: stable brief: "Payment method selected by the customer." ``` Notes: - Required fields: `key`, `type`, `stability`, `brief`. - Primitive `type` values: `string`, `int`, `double`, `boolean`, plus their `[]` array variants. - Enum types use the `members` form. Semantic-convention enums are open by definition — values outside the listed `id`s are allowed (the removed `allow_custom_values` flag no longer applies). Since v0.25.0, omitting a member `value` defaults it to the member `id` as a string; otherwise the explicit `value` type (`string`/`int`/`boolean`) determines the attribute type. The v2 syntax guide requires member `stability`; v0.26.1 reports only a non-fatal warning when it is missing in normal mode. - Provide `examples` for non-enum strings; it improves generated docs and helps reviewers. ## Metrics ```yaml file_format: definition/2 metrics: - name: ecommerce.orders.processing.duration instrument: histogram unit: s stability: stable requirement_level: recommended brief: "End-to-end duration of processing a single order." attributes: - ref: ecommerce.payment.method requirement_level: required - ref: ecommerce.customer.tier requirement_level: recommended - name: ecommerce.orders.active instrument: updowncounter unit: "{order}" stability: stable requirement_level: recommended brief: "Number of orders currently being processed." - name: ecommerce.products.inventory.value instrument: gauge unit: "USD" stability: stable requirement_level: recommended brief: "Current value of products currently in stock, in USD." ``` Notes: - `instrument` is one of `counter`, `updowncounter`, `histogram`, `gauge`. - Counter and UpDownCounter names should not append `_total`. - Duration instruments should use `s`. Bucket boundaries scale accordingly (e.g. `0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5`). - Signal-level `requirement_level` is `recommended` or `opt_in`; it defaults to `recommended`, but spelling it out avoids the v0.26.1 future-validation warning. This field is separate from each attribute reference's requirement level. - `attributes:` here are by `ref` only. Declare attributes once in `attributes.yaml` and reference them across metrics, spans, and events. - Attribute references cannot set `stability` or `deprecated` in v2; those fields come from the referenced definition. ## Spans ```yaml file_format: definition/2 spans: - type: ecommerce.order.process kind: internal stability: stable requirement_level: recommended name: note: "ecommerce.order.process" brief: "Process a single customer order end-to-end." attributes: - ref: ecommerce.user.id requirement_level: required sampling_relevant: true - ref: ecommerce.payment.method requirement_level: required sampling_relevant: true - ref: ecommerce.order.id requirement_level: recommended ``` Notes: - Required fields: `type`, `kind`, `stability`, `brief`, and a structured `name: { note: "..." }`. - `kind`: one of `client`, `server`, `producer`, `consumer`, `internal`. - For internal business spans with static names, put the dotted type identifier in `name.note` and render the resolved `span.name.note` string at runtime. - Upstream OTel HTTP/db/messaging spans use `{action} {target}` patterns derived from attributes — those should not appear in an org-local registry. - `sampling_relevant: true` flags attributes that the SDK should make available at sampling time. - Signal-level `requirement_level` is also available on events and entities. ## Entities ```yaml file_format: definition/2 entities: - type: ecommerce.warehouse stability: development requirement_level: recommended brief: "A physical or virtual warehouse fulfilling orders." identity: - ref: ecommerce.warehouse.id ``` Entities describe resource-like things your telemetry is *about* (was called `resource` before v0.15.0 — use `entity` now). An entity uses `type`, not `id`, and must have a non-empty `identity` list; optional non-identifying attributes go under `description`. The same attribute cannot appear in both lists. Most application registries won't need one: OTel's built-in resource entities (`service`, `host`, `k8s.*`, ...) already cover the common cases. Add an org-local entity only when you have a first-class domain concept that spans can attach attributes to but that isn't itself a span, metric, or event. ## What does NOT belong in your local registry Boundary domains owned by upstream OTel semconv: - `http`, `db`, `messaging`, `rpc`, `network`, `gen-ai`, ... Reference the language SDK's semconv package for these (e.g. `go.opentelemetry.io/otel/semconv/v1.X.0`) until upstream is pulled in as a manifest dependency. Redeclaring them locally creates a maintenance burden and silently diverges from the ecosystem. ## Validation Fast feedback loop: ``` weaver registry check --v2 -r ./telemetry/registry/ ``` Expected diagnostic noise: `File format definition/2 is not yet stable` (a warning for each custom v2 definition file). This is normal; its status by Weaver version is in the dated record in `ci-integration.md`. `--future` elevates that format warning to an error, so it cannot yet be used for a custom `definition/2` registry. -
template-authoring.md 8 KB
# Template Authoring Templates render the output of the `weaver.yaml` jq `filter`, not the input YAML. Field names differ — always confirm the shape before writing a template. The most reliable way to see the exact `ctx` a template receives is to render it through the real filter with a throwaway template: ``` # templates/registry/dbg/weaver.yaml # templates: # - template: dump.j2 # filter: > # semconv_grouped_spans({"v2": true}) # application_mode: single # file_name: dump.json # templates/registry/dbg/dump.j2 contains only: {{ ctx | tojson }} weaver registry generate --v2 --registry ./telemetry/registry/ --templates ./telemetry/templates/ dbg /tmp/out jq '.' /tmp/out/dump.json | less ``` (`weaver registry resolve` still works for a raw dump but is deprecated — and it errors under `--future` for a custom `definition/2` registry — and its raw shape differs from the filtered `ctx` above.) ## Resolved-shape cheat sheet (`definition/2`) Field names as they appear in the template `ctx` after the `semconv_grouped_*` filters: | Input field | In v2 `ctx` | |-----------------|-------------------| | attribute `key` | `key` | | metric `name` | `name` | | span `type` | `type` | | span `kind` | `kind` | | span `name.note` | `name.note` | | entity `type` | `type` | | entity `identity` | `identity` | | entity `description` | `description` | The `semconv_grouped_*` helpers wrap each signal list in `[{ root_namespace, <attributes|metrics|spans|events|entities> }]`. For v2 input, pass `{"v2": true}` or the helper takes its legacy-schema path. Fields are otherwise mostly verbatim (`brief`, `stability`, `unit`, `instrument`, `attributes`, ...). ## Layout ``` telemetry/templates/registry/ └── go/ # one dir per target language ├── weaver.yaml # required ├── attributes.go.j2 ├── metrics.go.j2 └── spans.go.j2 ``` The directory name (`go`) becomes the positional argument to `weaver registry generate`. ## `weaver.yaml` ```yaml text_maps: go_types: int: int64 double: float64 string: string boolean: bool acronyms: ["HTTP", "API", "ID", "URL", "DB"] comment_formats: go: format: markdown prefix: "// " params: package_name: telemetry templates: - template: attributes.go.j2 filter: > semconv_grouped_attributes({"v2": true}) application_mode: single file_name: "attributes_gen.go" - template: metrics.go.j2 filter: > semconv_grouped_metrics({"v2": true}) application_mode: single file_name: "metrics_gen.go" - template: spans.go.j2 filter: > semconv_grouped_spans({"v2": true}) application_mode: single file_name: "spans_gen.go" ``` Notes: - `application_mode: single` renders the template once with the filter result bound to `ctx`. - Bundled jq filters live in [`defaults/jq/semconv.jq`](https://github.com/open-telemetry/weaver/blob/main/defaults/jq/semconv.jq) in the Weaver repo. In v0.26.1, released grouped helpers are `semconv_grouped_attributes`, `semconv_grouped_metrics`, `semconv_grouped_spans`, `semconv_grouped_events`, and `semconv_grouped_entities`. - The no-argument helpers default to the legacy schema. For a `definition/2` registry, call them with `{"v2": true}` (as shown above). Use a folded YAML scalar (`filter: >`) so the object's colon doesn't collide with YAML mapping syntax. A custom multi-clause jq expression that takes no arguments should be single-quoted instead — for the same reason. - `acronyms` configures the explicit `acronym` filter; case filters such as `pascal_case_const` do not consult that list automatically. - `comment_formats` lets `comment(format="go")` know what comment prefix to emit. - A template entry may add `when: <jq-expression>` to gate the entire template before its `filter` runs. It must return exactly one boolean; empty streams and non-booleans are errors. Template parameters are exposed as `$params`. - v0.26.1 also lets a project-wide `.weaver.toml` `[template]` section layer `acronyms` and `text_maps` over every template package; case-insensitive acronym collisions and text-map name collisions favor the TOML values. Other `weaver.yaml` settings are not project-level overrides. ## Jinja2 template patterns **Attributes** (`attributes.go.j2`): ```jinja {# attributes.go.j2 #} // Code generated by weaver. DO NOT EDIT. package {{ params.package_name }} // Attribute keys from the ecommerce semantic convention registry. const ( {% for group in ctx %} {% for attr in group.attributes %} {{ attr.brief | comment(format="go") }} Attr{{ attr.key | pascal_case_const }} = "{{ attr.key }}" {% endfor %} {% endfor %} ) ``` **Metrics** (`metrics.go.j2`): ```jinja {# metrics.go.j2 #} // Code generated by weaver. DO NOT EDIT. package {{ params.package_name }} // Metric names and units from the ecommerce semantic convention registry. {% for group in ctx %} {% for metric in group.metrics %} {{ metric.brief | comment(format="go") }} const ( {{ metric.name | pascal_case_const }}Name = "{{ metric.name }}" {{ metric.name | pascal_case_const }}Unit = "{{ metric.unit }}" ) {% endfor %} {% endfor %} ``` **Spans** (`spans.go.j2`): ```jinja {# spans.go.j2 #} // Code generated by weaver. DO NOT EDIT. package {{ params.package_name }} // Span names from the ecommerce semantic convention registry. const ( {% for group in ctx %} {% for span in group.spans %} {{ span.brief | comment(format="go") }} Span{{ span.type | pascal_case_const }}Name = "{{ span.name.note }}" {% endfor %} {% endfor %} ) ``` ## Filter reference - `comment(format="go")`: emits a comment with the configured prefix. Already includes `// `; do not double-prefix. - `pascal_case_const`: converts `ecommerce.order.id` → `EcommerceOrderId`. Weaver ships more MiniJinja filters/tests/functions than any one template needs — reach for these instead of hand-rolling string manipulation in a template: - **Case filters**: `snake_case`, `camel_case`, `pascal_case`, `screaming_snake_case`, `kebab_case`, `pascal_case_const`; use `acronym` explicitly where configured acronym replacement is needed. - **Other filters**: `map_text` (looks up a value in a `text_maps` table from `weaver.yaml`, e.g. `attr.type | map_text("go_types")`), `attribute_sort`, `required`, `not_required`, `instantiated_type`, `enum_type`, `body_fields`, `prometheus_metric_name`, `prometheus_unit_name`, plus ANSI color filters (rarely needed outside CLI output templates). - **Tests**: `stable`, `experimental`, `deprecated`, `enum`, `simple_type`, `template_type`, `enum_type`, `array` — e.g. `{% if attr is deprecated %}`. - **Functions**: `concat_if(...)`; for v2 generation, `lookup_entity(leaf)` resolves an `entity_associations` leaf (not a `one_of`/`all_of` node) to its local or dependency-owned entity definition. - **Loop controls**: `break`/`continue` are supported inside `{% for %}` loops (a MiniJinja extension beyond stock Jinja2). ## Generation ``` weaver registry generate \ --v2 \ --registry ./telemetry/registry/ \ --templates ./telemetry/templates/ \ go \ ./internal/telemetry/ ``` CLI ordering matters: - `--templates` points at the **parent** directory containing target dirs (`./telemetry/templates/`), not the language subdir. - The target directory name (`go`) is positional after the flags. - The output directory is the next positional. ## Format the output Jinja whitespace produces multiple blank lines inside `const ( ... )` blocks. Run the language formatter before committing: ``` gofmt -w ./internal/telemetry/ ``` Equivalents: `prettier -w`, `ruff format`, `rustfmt`, etc. ## Inspecting the resolved schema `scripts/inspect-resolved.sh` wraps `weaver registry resolve --v2` and pretty-prints with `jq`. Useful for a quick raw v2 dump, but note `resolve` is deprecated and its raw shape is pre-filter — to see the exact fields a template gets, dump `{{ ctx | tojson }}` through the real `semconv_grouped_*` filter as shown at the top of this file.
-
-
scripts
-
inspect-resolved.sh 1.4 KB
#!/usr/bin/env bash # Resolve a Weaver registry to JSON and pretty-print a slice with jq. # # Use this for a quick raw dump of the resolved schema. Note: `weaver registry # resolve --v2` is deprecated (still works; errors under --future for a custom # v2 registry), and its raw shape is PRE-filter — it differs from the `ctx` a # template receives via the semconv_grouped_* jq helpers. To see the exact # template fields, render # {{ ctx | tojson }} through the real filter (see references/template-authoring.md). # # Usage: # inspect-resolved.sh <registry-dir> [jq-filter] # # Examples: # inspect-resolved.sh ./telemetry/registry/ # inspect-resolved.sh ./telemetry/registry/ '.registry.spans[]' # inspect-resolved.sh ./telemetry/registry/ 'semconv_grouped_attributes' # not supported here; jq only set -euo pipefail if [ $# -lt 1 ]; then sed -n '2,12p' "$0" exit 2 fi REGISTRY="$1" FILTER="${2:-.}" WEAVER="${WEAVER:-weaver}" if ! command -v "$WEAVER" >/dev/null 2>&1; then echo "weaver binary not found on PATH (set WEAVER=... or install per https://github.com/open-telemetry/weaver#install)" >&2 exit 1 fi if ! command -v jq >/dev/null 2>&1; then echo "jq is required" >&2 exit 1 fi tmp="$(mktemp -t weaver-resolved.XXXXXX.json)" trap 'rm -f "$tmp"' EXIT "$WEAVER" registry resolve --v2 -r "$REGISTRY" -f json -o "$tmp" >/dev/null jq "$FILTER" "$tmp"
-
-
SKILL.md 10.9 KB
--- name: otel-weaver description: OpenTelemetry Weaver registry authoring, codegen, and CI enforcement. Use when adopting Weaver, authoring or reviewing a registry (manifest, attributes, metrics, spans, events), writing Jinja2 templates against the resolved schema, migrating hand-maintained telemetry constants, wiring `weaver registry check`/`generate`/`diff` into CI, or choosing a Weaver version for a semantic-conventions release. --- # OpenTelemetry Weaver Use this skill when an organization wants to define its own semantic conventions on top of upstream OTel and generate language bindings from them. Usage: - pair with `otel-semantic-conventions` to decide which attributes already exist upstream and should not be redeclared in the local registry - latest Weaver version: the `otel-sdk-versions` index if it lists Weaver, else <https://github.com/open-telemetry/weaver/releases>; `references/ci-integration.md` keeps a dated Weaver/semconv compatibility record for offline answers If a companion skill is unavailable: - do not stop - do not rely on memory alone when the guidance can be checked from official sources - consult the Weaver repo, `schemas/semconv-syntax.v2.md`, and `docs/usage.md` / `docs/validate.md` - state which fallback you used and leave any unverified item unresolved ## Mental Model Three moving parts: 1. **Registry** — directory of YAML files. `manifest.yaml` is required; its `schema_url` (OTel schema URL format, `http[s]://host/path/<version>`) both names the registry and carries its version in the final path segment. Dependency entries also require `schema_url` plus optional `registry_path`. The rest declare `attributes`, `metrics`, `spans`, `events`, `entities`. The version segment of `schema_url` is yours to manage; bump it on changes. (`semconv_version` and `schema_base_url` are deprecated in favor of `schema_url`; top-level `name` is not a v0.26.1 manifest field.) 2. **Templates** — directory of MiniJinja files (Jinja2-compatible, not full Jinja2 — auto-escaping is off by default since v0.22.0 and loop `break`/`continue` are supported) plus a `weaver.yaml` per target language describing which templates to run, with what filter, in what `application_mode`, and with what output filename. 3. **Policies** — Rego rules evaluated by the Regorus (OPA-compatible) engine, in four packages: `before_resolution` (raw parsed groups; unsupported and skipped with `--v2`), `after_resolution` (resolved registry), `comparison_after_resolution` (only when `--baseline-registry` is passed), and `live_check_advice` (per-sample during `live-check`). Built-in OTel policies are the floor; custom policies layer on org rules. These three replace a hand-rolled `const.go` (or equivalent): const blocks become the registry, the act of writing them becomes codegen, and tribal knowledge becomes policies. ## Non-Negotiable Rules - Install Weaver via one of the methods documented at <https://github.com/open-telemetry/weaver#install> (release binary, `otel/weaver:vX.Y.Z` Docker image, or the `setup-weaver` GitHub Action). Never `brew install weaver` — that resolves to an unrelated Scribd tool. - Reference upstream semconv attributes by `ref` rather than redeclaring them. Boundary domains (`http`, `db`, `messaging`, `rpc`, `network`, `gen-ai`, ...) belong in upstream OTel semconv, not in a local registry. Use the language SDK's semconv package for those at runtime. - Every attribute and signal definition needs `stability`; include it on enum members too, as required by the v2 syntax guide. Weaver v0.26.1 rejects missing definition stability but reports missing enum-member stability only as a non-fatal warning in normal mode. - In v2, an attribute reference may refine fields such as `brief`, `note`, `examples`, and `annotations`, but not `stability` or `deprecated`; those always come from the attribute definition. - Use a domain prefix (e.g. `ecommerce.`, `acme.`) for org-local attributes, metrics, and spans. - Run the language formatter (`gofmt -w`, `prettier`, `ruff format`, ...) on generated output. Jinja whitespace produces multiple blank lines; without formatting, the diff check in CI will fail spuriously. - Confirm the resolved schema shape before writing a template. For a `definition/2` registry, call the grouped jq helpers with `{"v2": true}`; the v2 template `ctx` preserves fields such as attribute `key`, metric `name`, span `type`/`kind` and structured `span.name.note`, plus entity `type`/`identity`/`description`. See `references/template-authoring.md` for how to dump the exact shape. ## Workflow 1. **Install or locate Weaver.** Follow the upstream install instructions at <https://github.com/open-telemetry/weaver#install> — pick a pinned release binary, the `otel/weaver:vX.Y.Z` Docker image, or the `setup-weaver` GitHub Action. Use Docker for CI and reproducible local runs. 2. **Author the registry.** Required: `manifest.yaml` plus one or more `definition/2` YAML files declaring attributes, attribute groups, metrics, spans, events, or entities. See `references/registry-authoring.md`. 3. **Author templates.** One target dir per language under `templates/registry/<lang>/` with `weaver.yaml` plus `*.j2`. See `references/template-authoring.md`. 4. **Validate and generate.** `weaver registry check --v2 -r ./telemetry/registry/` for fast feedback. `weaver registry generate --v2 --registry ./telemetry/registry/ --templates ./telemetry/templates/ <lang> <output-dir>` for codegen. Run the language formatter on the output. 5. **Wire into CI.** Three gates: `check` (schema), `generate` + `git diff --exit-code` (checked-in code is current), `diff` against the base branch (surfaces breaking changes). See `references/ci-integration.md`. The Weaver CLI has more subcommands than this workflow touches: `stats` and `json-schema` for quick registry sanity checks, `update-markdown` for keeping semconv snippets in docs current, `emit`/`live-check`/`infer` for working against live OTLP telemetry, `package` for writing a self-contained resolved registry artifact (the replacement for the deprecated `resolve`), `mcp` for exposing a registry to LLM tooling, and `serve` for an HTTP+UI mode. All are out of scope here (see below) but worth knowing exist before assuming `check`/`generate`/`diff` is the whole surface. ## Gotchas These cost time and are not obvious from the upstream docs: 1. `brew install weaver` installs the wrong tool. Use GitHub releases or Docker. 2. Generated output is not formatter-clean. Always run the language formatter after `weaver registry generate`. 3. Jq helper defaults target the legacy schema. For `definition/2`, pass `{"v2": true}` and use the preserved v2 fields: attribute `key`, metric `name`, span `type`/`kind` and `span.name.note`, plus entity `type`/`identity`/`description`. Always generate and inspect the filtered `ctx` before writing a template. 4. The `comment` Jinja filter takes a keyword argument: `attr.brief | comment(format="go")`. It already emits the `// ` prefix; do not add another. 5. Attributes, metrics, spans, events, and entities all have prebuilt grouped jq filters (`semconv_grouped_attributes`, `semconv_grouped_metrics`, `semconv_grouped_spans`, `semconv_grouped_events`, `semconv_grouped_entities`). For `definition/2`, use (for example) `semconv_grouped_spans({"v2": true})` as a folded YAML scalar; the bare helper defaults select the legacy schema. 6. `weaver registry check` emits "File format `definition/2` is not yet stable" (a warning) for custom v2 definition files (still so in the dated record in `references/ci-integration.md`). This is normal; do not treat it as a failure. 7. `--future` is opt-in but still elevates the `definition/2` instability warning to an error for a custom v2 registry. Leave it off until the format goes stable. 8. CLI argument ordering for `generate`: target directory name is positional **after** `--registry` and `--templates`; the output directory follows. `--templates` points at the **parent** that contains target dirs, not at the language-specific subdir. 9. Span name in registry vs. runtime: required schema fields are `type`, `kind` (`client`/`server`/`producer`/`consumer`/`internal`), `brief`, `stability`, and a structured `name: { note: "..." }`. For internal business spans, putting the dotted type identifier in `name.note` and rendering the resolved `span.name.note` string at runtime is clean. 10. **What does NOT belong in your local registry.** DB, HTTP, messaging, RPC, network, GenAI, and similar boundary spans/attributes follow upstream OTel semconv. Until upstream is pulled in as a manifest dependency, instrumentation for those should reference the language SDK's semconv package directly. This is the most common modeling mistake. 11. Counter and UpDownCounter names should not append `_total`; this is the semconv v1.44.0 naming rule. 12. Duration instruments should use seconds (`s`) under the semconv v1.44.0 unit guidance. ## References To Load On Demand - registry YAML field reference: `references/registry-authoring.md` - Jinja2 patterns, jq filters, resolved-shape cheat sheet: `references/template-authoring.md` - ready-to-lift GitHub Actions example and the dated version-compatibility record: `references/ci-integration.md` - hand-maintained-constants → registry walkthrough: `references/migration-playbook.md` - semantic conventions skill: `otel-semantic-conventions` - manual instrumentation skill: `manual-instrumentation` ## Out Of Scope These are natural follow-ups but not part of this skill: - publishing the registry as a versioned artifact for downstream consumers (`weaver registry package`) - declaring upstream semantic-conventions as a manifest dependency - `weaver registry live-check`/`emit`/`infer` against live OTLP telemetry - `weaver registry mcp` / `weaver serve` - custom Rego policies beyond the built-ins - helper-function codegen (`MyMetricName(meter)` wrappers) ## Verification Contract If you authored or modified a Weaver registry, templates, or CI integration: - re-open the changed files before finishing - run `weaver registry check --v2` against the `definition/2` registry and capture the result - run `weaver registry generate --v2` and the language formatter, then verify `git diff --exit-code` is clean - confirm each applicable item with codebase evidence Report the final check with: - `[x]` completed - `[~]` not applicable, with a reason - `[ ]` unresolved Use these items: - registry has `manifest.yaml` with a `schema_url` whose final path segment is the version - every definition and enum member has `stability` - org-local attributes/metrics/spans use a domain prefix - no boundary-domain (http/db/messaging/rpc/network/gen-ai) entries duplicated locally - Counter and UpDownCounter names have no `_total` suffix - duration histograms use `s` (seconds) - templates use jq filters that match the resolved schema (for `definition/2`, call the prebuilt `semconv_grouped_*` helpers with `{"v2": true}`) - generated output is formatter-clean - CI runs `check`, `generate` + `git diff --exit-code`, and `diff` against the base branch - changed files were re-read - remaining risks or gaps are stated
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.