Skip to content

About

qyl's OpenTelemetry semantic-conventions registry: one Weaver registry generates the stable and incubating C# bindings and analyzers (NuGet) and the TypeScript constants (npm @ancplua/qyl-semantic-conventions)

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

349 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Qyl.Telemetry.SemanticConventions

.NET packages generated by Weaver from one semantic-convention registry. The registry is the one artifact both halves of qyl share: what an application emits and what the collector recognises are generated from it, so qyl cannot emit an attribute name its own collector does not know.

qyl owns these .NET bindings and generates them directly from the language-independent OpenTelemetry registries, together with qyl and library-native definitions. registry/manifest.yaml pins core and GenAI coverage; registry/vendor/ records the verified library versions and their native telemetry. qyl can update and publish these bindings independently of upstream .NET SDK and automatic-instrumentation releases.

Packages

Package Contents
Qyl.Telemetry.SemanticConventions The stable tier: attribute-key constants, SchemaUrl, the qyl-owned telemetry names, typed Activity setter extensions with their value-set structs, and the metric, span, event and entity definitions — plus the definition types themselves (MetricDefinition<TInstrument>, SpanDefinition<TKind>, EventDefinition, EntityDefinition, and their Stability/Deprecation/RequirementLevel/AttributeRef/AttributePhase/EntityRef companions). The agent-first API in namespace Qyl: the span starters Spans (Source, Work, DbClient, RecordFailure) and SpanNames; and the trace contract SpanRecord, StoreIdentity, the Views over them and RegistryPolicy
Qyl.Telemetry.SemanticConventions.Incubating Attribute-key constants at every stability tier, the rows the stable tier does not carry for the other five kinds and their value-set structs, the incubating starters Qyl.IncubatingSpans and their views, the qyl-owned instrument names, and Mapping.AttributeMapping — the collector's normalize table
Qyl.Telemetry.SemanticConventions.Analyzers Roslyn diagnostics and code fixes for semantic-convention consumers, with a generated rule catalog, a migration catalog, and editorconfig severity profiles

Three packages ship. Qyl.Telemetry.SemanticConventions.SourceGeneration is retired: its last published version is 8.1.0, and no later release ships it.

All three are published on nuget.org; VersionPrefix in Directory.Build.props names the current release line. Incubating APIs track unstable upstream conventions and may change between minor releases.

First consumer

All three packages, at whatever line nuget.org holds today (pin --version when you need one):

dotnet add package Qyl.Telemetry.SemanticConventions
dotnet add package Qyl.Telemetry.SemanticConventions.Incubating
dotnet add package Qyl.Telemetry.SemanticConventions.Analyzers

The Analyzers package is a development dependency, so the third command writes its PrivateAssets="all" itself. This Program.cs then compiles unchanged on net10.0 and touches each surface once:

using System.Diagnostics;
using Qyl.Telemetry.SemanticConventions;
using Qyl.Telemetry.SemanticConventions.Activities;
using Qyl.Telemetry.SemanticConventions.Attributes.Http;
using Qyl.Telemetry.SemanticConventions.Metrics;
using Qyl.Telemetry.SemanticConventions.Names;
using Qyl.Telemetry.SemanticConventions.Incubating.Activities;
using Qyl.Telemetry.SemanticConventions.Incubating.Mapping;

// Attribute keys, the pinned schema URL, and the qyl-owned names.
Console.WriteLine(HttpAttributes.RequestMethod);           // http.request.method
Console.WriteLine(SchemaUrl.Current);                      // https://opentelemetry.io/schemas/1.44.0
Console.WriteLine(QylTelemetryNames.Scopes.QylCollector);  // Qyl.Collector

// A metric is an object, not a name constant.
var duration = HttpMetricDefinitions.HttpClientRequestDuration;
Console.WriteLine($"{duration.Name} {duration.Instrument} {duration.Unit}");
// http.client.request.duration histogram s

// The stable setter extension and its incubating sibling, on one Activity. An enum attribute
// takes its value-set struct: a member, or Custom for a value the registry does not list.
using var activity = new Activity("GET").Start();
activity.SetHttpRequestMethod(HttpRequestMethod.Get);
activity.SetHttpRequestBodySize(1234);

// A span starter on the application scope AddQyl subscribes: required attributes positional,
// conditional ones without a default, the name from the registry's chain ("write orders").
using var span = Qyl.Spans.DbClient(DbSystemName.Postgresql, collectionName: "orders",
    @namespace: "shop", operationName: "write", serverPort: null);

// The collector's normalize table.
Console.WriteLine(AttributeMapping.TryGetRename("http.method", out var live) ? live : "(none)");
Console.WriteLine(AttributeMapping.IsObsoleted("db.connection_string"));  // True
Console.WriteLine(AttributeMapping.NamespaceOf("qyl.session.id"));        // qyl

Both namespaces are in scope at once and no call is ambiguous, because the two packages are disjoint for every kind but attributes. The program also publishes NativeAOT clean — dotnet publish -c Release -r <rid> -p:PublishAot=true emits no trim or AOT warning, because nothing here reflects: the keys are const string, the definitions are constructed objects, the starters are static methods with TagList creation tags, and AttributeMapping is switch statements over string literals.

The registry

registry/manifest.yaml is the single source of truth for the upstream pins. It names two dependencies:

  • core — open-telemetry/semantic-conventions at the tag its schema_url carries, materialised as a local filtered copy under .build/core-filtered/model by scripts/fetch-core.sh. The filter removes gen-ai/, mcp/, openai/ and the registry.aws.bedrock group, exactly the way upstream semantic-conventions-genai filters core in its own Makefile: an unfiltered core dependency collides with the genai one on gen_ai.agent.description.
  • genai — open-telemetry/semantic-conventions-genai, a git URL pinned to a commit.

qyl's own YAML sits next to them under registry/. registry/qyl carries the 17 qyl.* attributes, the two qyl-owned instruments, the two qyl-owned event names and the six scope names. registry/vendor carries one file per pinned third-party library: the keys that library puts on its own ActivitySource or Meter and that upstream semantic conventions do not define. Weaver resolves all three layers itself; there is no merge step.

Vendor keys retain their native spelling. Registry membership is not an allowlist for application attributes; persistence follows the generated policy's explicit exclusions and capture rules. This registry carries telemetry vocabulary only; qyl's client-visible product requests, responses, stream events and errors are owned by qyl-api-schema.

The Anthropic coverage notes distinguish Claude Code, Cowork, Desktop, the Claude API/SDK and Chrome instrumentation, including beta and dynamic paths. The browser extension audit also covers ChatGPT 1.26.1005.11759, separating its inactive tracing hooks from its Statsig product analytics. The OpenAI reference covers the pinned Codex runtime, ChatGPT desktop, public cloud schemas and Chrome audit. The complete registry reference includes qyl, vendor and resolved upstream definitions at every stability. Both are rendered directly by Weaver 0.28.0 from templates/registry/markdown/*.j2; run mise exec -- scripts/generate-reference.sh to regenerate them. The qyl agent-observability profile defines development adapter obligations; the catalog does not assert deployed ingestion. Operation IDs belong on span/event attributes. The ordinary lifecycle signals do not associate an operation entity with the OpenTelemetry resource: that would require a new resource identity for each operation. The standalone entity vocabulary does not impose that resource layout.

Policies

Two Rego sets, because two registry shapes answer different questions. registry/policies runs against the plain resolved shape, the only one that keeps group annotations: every attribute qyl declares is in qyl.*, and every vendor group has its metadata sibling naming the library, version, repository, ref, licence and ActivitySources, with a file-and-line note on every attribute. Weaver loads it from the registry directory itself. registry/policies-v2 is passed explicitly and runs against the materialized v2 registry, the only shape that carries the full dependency catalog: qyl.attribute.namespace enumerates every root namespace plus other, there is at least one GenAI token histogram (unit {token}) for QYL0402 to name and exactly one unconstrained execute_tool span, and every GenAI any attribute carries a JSON Schema annotation.

Live-check judgement

registry/.weaver.toml and registry/policies/live_check_advice are one judgement in two halves, and both are needed to state it. --advice-policies replaces the rego advisors only: Weaver's deprecated, stability, type and enum advisors are compiled into the binary, always run, and emit at a level no policy can change. .weaver.toml drops the three findings the qyl rules replace, by finding id and never by attribute name. Run with --advice-policies alone and each of those findings is reported twice, once at Weaver's level and once at qyl's, and --fail-on violation fails on telemetry qyl handles correctly.

The judgement itself is decided from the registry entry alone — there is no attribute-key list anywhere in the set:

finding level why
open_enum_value information The enum carries the member _OTHER, so it is open and a value outside the member list is what the specification prescribes. error.type is the case that matters: exception type names and status codes are all undocumented values of an open enum.
undocumented_enum_value information The same on a closed enum. Weaver's default level, kept.
deprecated_renamed improvement AttributeMapping.TryGetRename rewrites the key to its final live replacement.
deprecated_obsoleted improvement AttributeMapping.IsObsoleted tells the collector to drop the key and count the drop.
deprecated_uncategorized violation The registry names no replacement and the collector has no rule. Weaver's default level, kept.
type_coercible improvement The value parses as the declared type, so the collector coerces it.
type_not_coercible violation It does not. Weaver's default level, kept.
enum_type_invalid violation An enum sample that is neither string nor int. Weaver's default level, kept.

Everything else is Weaver's, unchanged, including missing_attribute, not_stable, the required/recommended/opt-in findings and the four name and namespace rules that otel.rego — Weaver's own default policy, copied verbatim into the set — carries.

Pin freshness

The pins are exact by design: a moving input must not change generated constants without a commit here. scripts/check-pin-freshness.py reports when one falls behind upstream and does not decide whether to move it; run it by hand, and weekly.yml reports it every Monday in its job summary. The genai pin is a branch commit, and the manifest pins that repository's model/ sub-folder only, so the check counts a commit against the pin only when it changes a file under model/; a lock-file or tooling commit on main leaves the pin current and is reported as distance without staleness.

uv run scripts/check-pin-freshness.py   # 0 = every pin current, 10 = a pin is behind, 2 = unknown

Moving a pin is a deliberate change: edit registry/manifest.yaml, re-run scripts/generate.sh, review the regenerated surface, and record the delta in CHANGELOG.md.

How generation works

scripts/generate.sh checks the registry against both policy sets and then runs the same weaver registry generate --v2 --include-unreferenced for each target under templates/registry: snapshot, csharp and typescript. There is no compile-time generator: the output is committed, the same shape as the check-generated target in opentelemetry-weaver-examples/basic. The snapshot target writes the resolved registry to .build/registry.json. Its SHA-256, without the local paths of fetched dependencies, reaches both language targets as registry_snapshot_sha256 (C# RegistrySnapshot.Sha256, TypeScript REGISTRY_SNAPSHOT_SHA256), and the live-check matchers are generated from the file.

registry/  (manifest.yaml: core + genai dependencies; qyl/ and vendor/ next to them)
        |
        v
scripts/generate.sh -> weaver registry generate --v2 --include-unreferenced <target>
        |
        +-- snapshot ---> .build/registry.json  (the fingerprint, the live-check matchers)
        +-- csharp -----> src/Qyl.Telemetry.SemanticConventions/Generated/            (stable tier)
        |                 src/Qyl.Telemetry.SemanticConventions.Incubating/Generated/ (the other tiers)
        |                 src/…Analyzers/SemconvRegistryFacts.g.cs, SemconvDeprecations.g.cs, SemconvTypedSurface.g.cs
        |                 generated/pins.props  (the pins, for MSBuild)
        +-- typescript -> typescript/src/, typescript/dist/, typescript/package.json

A target's weaver.yaml, such as templates/registry/csharp/weaver.yaml, declares its files: one JQ filter shaping the registry into a context, one .j2 template rendering it. No template parameter has a default. The three schema URLs are registry facts the filters read off the materialized registry itself; the Weaver version, the core tag, the core commit and the genai commit reach the templates only from Version.props and registry/manifest.yaml, through scripts/generate.sh. A bare weaver registry generate therefore writes error: missing param <name> into the header instead of a stale pin.

Because the resolved v2 registry flattens registry.* groups into the catalog and drops their group annotations, scripts/generate.sh reads the two lists that live only in annotations — the scope names qyl constructs and the vendor ActivitySource names — out of the same YAML and hands them to the templates as parameters. The same holds for qyl's policy over the registry, registry/qyl/policy.yaml: which keys the collector does not persist (qyl.persisted: false, the denied-key tokens, qyl.token_exempt), and the finite captured HTTP-header / textual RPC-metadata field-name and credential rule, which attributes are known only when an operation ends (qyl.phase: end), and the span-name chains and starters keyed by span refinement id (qyl.span_name, starter, domain, fixed). scripts/generate.sh reads it into one parameter file, --params .build/generate-params.json, which the templates receive as params.policy. After the snapshot it adds every registry enum attribute whose key contains a denied token to token_exempt: the tokens guard free text, and an enum attribute such as cicd.pipeline.result carries one of its registry members. registry/ stays the only source. Weaver writes files and never deletes one, so scripts/generate.sh clears both Generated/ trees and typescript/src and typescript/dist first.

scripts/check-generated.sh is the gate: it regenerates everything and fails on git diff HEAD. GeneratedSurfaceTests checks that both packages provide the required signal families. Compilation and focused contract tests validate their behavior without a second inventory of generated-file hashes.

weaver must be the version Version.props pins — scripts/generate.sh refuses to run against any other. mise run check runs the whole sequence; commit and push regenerated outputs first so its drift check compares the intended commit:

uv run --no-project --with pyyaml python3 -m unittest discover --start-directory tests/scripts --pattern 'test_*.py'
./scripts/check-generated.sh
npm test --prefix typescript
./scripts/check-live-check-policies.sh
dotnet restore Qyl.Telemetry.SemanticConventions.slnx
dotnet build Qyl.Telemetry.SemanticConventions.slnx --no-restore --configuration Release
dotnet run --project tests/Qyl.Telemetry.SemanticConventions.Pipeline.Tests/ --configuration Release --no-build

The test project is a Microsoft.Testing.Platform project: mise run check runs it with dotnet run --project, and global.json selects the native runner, so dotnet test drives it too.

The generated surface

Each package's Generated/ directory holds one file per registry root for six kinds.

Kind Stable package Incubating package
Attributes Attributes.{Root}.{Root}Attributes Incubating.Attributes.{Root}.{Root}Attributes
Activities Activities.{Root}ActivityExtensions and a value-set struct per enum attribute (Activities.DbSystemName) Incubating.Activities.{Root}IncubatingActivityExtensions and its structs (Incubating.Activities.MessagingSystem)
Metrics Metrics.{Root}MetricDefinitions Incubating.Metrics.{Root}IncubatingMetricDefinitions
Spans Spans.{Root}SpanDefinitions Incubating.Spans.{Root}IncubatingSpanDefinitions
Events Events.{Root}EventDefinitions Incubating.Events.{Root}IncubatingEventDefinitions
Entities Entities.{Root}EntityDefinitions Incubating.Entities.{Root}IncubatingEntityDefinitions
Starters Qyl.Spans, Qyl.SpanNames Qyl.IncubatingSpans
Views Views.SpanViews, Views.DbClientSpan Incubating.Views.IncubatingSpanViews and its view structs

Attributes is the one kind both packages carry in full — the stable package the stable rows plus the deprecated migration symbols, the incubating package every key, at the byte-identical shape the collector reads by reflection. Every enum-value class carries AllValues and Contains; those const string classes live under Attributes only, for switch and pattern matching. Under Activities an enum attribute is an open value-set struct (DbSystemName): one static member per registry value, Custom(...) for a value the registry does not list (QYL0121 reports one a member carries), Value, Key, value equality, and no conversion from string. The typed setter of an enum attribute takes the struct. Resource-only attributes (service.name, deployment.environment.name, host.*: referenced by entities and by no span, event or metric) get no Activity setter.

One shared Weaver JQ symbol map distinguishes wire keys before filtering by stability or generating constants, setters and value sets. For example, error.type uses SetErrorType, while native error_type uses SetErrorTypeNative. A collision with the Native suffix gets a deterministic code-point suffix. The emitted wire names remain unchanged. Analyzer lookup uses the declaring type and complete parameter signature. TypeScript enum constants use the same collision-aware attribute naming as their attribute constants.

Span rows are keyed by the registry's span refinement id and grouped by type, so a specialization (db.postgresql.client, the gen_ai skill spans) is a row of its own with Type naming its base; each row carries its SpanName chain, and each AttributeRef its condition text, Phase, SamplingRelevant and IsPersisted. A reference's qyl.phase overrides the global phase policy for metadata, starter parameters and analyzer parameter maps. Sampling relevance is independent of that availability decision.

Metric, span, event and entity definitions expose a separate SignalRequirementLevel: Recommended by default, or OptIn. This describes collection of the signal, independently of whether an attribute is required on it. Metrics, spans and events carry EntityAssociations: an outer implicit one-of list of EntityRef, EntityAssociation.OneOf and EntityAssociation.AllOf expressions. Every reference includes its defining registry's schema URL and any source path provided by Weaver; nested alternatives are not flattened.

Typed views validate required values as well as span kind and fixed discriminators. TryCreate returns false with a null view for an unsuccessful projection; direct construction rejects a nonmatching record. Required getters never substitute an empty string or zero for malformed telemetry. Optional getters return null when a value cannot be projected.

The starters (ADR D2) exist for the span groups no library qyl subscribes to already emits: db.client on Qyl.Spans; messaging.process.consumer, gen_ai.invoke_agent.internal, gen_ai.execute_tool.internal and its three skill specializations on Qyl.IncubatingSpans. Required attributes are positional, conditionally required ones nullable with no default (an omission is CS7036), recommended ones default to null; OptIn, end-phase and group-fixed attributes take no parameter and go through the typed setters or RecordFailure. Each starter stamps its qyl.instrumentation.domain, names the span through Qyl.SpanNames, puts every supplied value into the creation tags and returns null when nothing listens. Spans.Work(name) covers business work no group describes (domain app). Use native Activity.SetTag for application attributes, preserving their names and types. RecordFailure sets status Error with a bounded description, error.type and the exception event. Qyl.SpanNames carries every naming chain of the policy in the stable package, so producers that may not read the incubating tier name their spans the same way.

For the other five the two packages are disjoint. The stable package carries the stable and deprecated rows under {Root}{Kind}; the incubating package carries only the rows the stable package does not, under {Root}Incubating{Kind}, so a consumer may using both namespaces without an ambiguous-call error. A registry root with no non-stable rows gets no incubating type at all. A definition generated in either package is an instance of the same public type, so it can be handed between libraries and applications.

These singletons round the surface out.

  • SchemaUrl.Current — the pinned core schema URL, stable package only; SchemaUrl.Qyl and SchemaUrl.QylVersion — the qyl registry's own (https://qyl.at/schemas/<version>), which qyl's ActivitySources carry.
  • RegistryPolicy — the collector's persistence rule as a table (NotPersistedKeys, NotPersistedPrefixes, DeniedKeyTokens, TokenExemptKeys, VendorKeys, IsPersisted); the views and the qyl collector's catalog read it. TokenExemptKeys includes every registry enum attribute whose key contains a denied token.
  • SpanRecord, SpanEventRecord, SpanLinkRecord, ResourceRecord, InstrumentationScopeRecord, ResourceEntityReference and StoreIdentity — the trace contract the generated views read and Qyl.Telemetry.Testing builds on. Construct a span with its trace ID, span ID and name; initialize its remaining metadata by name. Resource and scope schema URLs are independent, and typed attributes and exact nanosecond times remain available alongside the .NET timestamp projections.
  • Names.QylTelemetryNames — stable package only, because the producer packages build their ActivitySource and Meter from it and may not read the incubating tier. Scopes is the seven scope names qyl constructs (the application scope Qyl.Telemetry.Application among them); VendorActivitySources is the source names of the pinned third-party libraries, so AddSource and a span processor's source match need no literal, and define the reserved names QYL0200 checks. Custom application and library names remain valid; Events is the one qyl-owned event name.
  • Incubating.Attributes.{Root}.{Root}MetricAttributes — the qyl-owned instrument names and their units, for the roots that declare one.
  • Incubating.Mapping.AttributeMapping — the collector's normalize table, four switch statements over string literals so it stays netstandard2.0-compatible and NativeAOT-clean. TryGetRename resolves every renamed key transitively to its final live replacement; IsObsoleted is every key the registry deprecates without naming one, so the collector can drop it and count the drop; IsVendorPassThrough is every key that reached the registry through registry/vendor/; NamespaceOf clamps a key to the closed value set of qyl.attribute.namespace, returning other for anything the registry does not know. The four pins ride along as constants.

RegistryCatalog.TryGetAttribute and TryGetMetric expose complete stable and incubating facts without reflecting over constants: value kinds, template keys, metric instruments and units, stability and structured deprecations. The analyzers embed the same generated catalog privately, with no runtime dependency on the consumer library. TypeScript's separate ./catalog export provides findAttribute and findMetric from the same normalized facts. An unknown custom name is not an OpenTelemetry error. Requirements belong to a selected signal definition, and the catalog does not assign instruments to scopes.

Checking live telemetry against this registry

weaver registry live-check compares emitted spans, metrics and logs against the registry and reports a finding per attribute, judged by the two halves above.

registry/.weaver.toml is generated from the resolved span definitions and the naming contracts in registry/qyl/policy.yaml. It selects Weaver v2 and routes spans by their wire name and kind, so client/server spans sharing a name retain distinct requirements. The registry type is also accepted as an explicit name. Free-form names without a recognizable literal or enum are not guessed; they need an explicit type or a producer-specific discriminator before their required fields can be checked. Unmatched telemetry still receives global attribute validation. scripts/check-signal-validation.py proves that an ordinary operation passes, each required field fails when removed, and a shared native name routes separately for client and server.

Generation also runs scripts/check-future.py. It rejects owned registry defects and unknown dependency diagnostics, while reporting the pinned dependencies' known forward-compatibility warnings separately in artifacts/logs/future.json. Weaver 0.28's definition/2 experimental format notice is reported separately even for valid local files. This gate does not claim that the raw weaver registry check --future command exits zero for those pinned upstream inputs.

All three of -r, --config and --advice-policies need a local path. Weaver's <url>[sub-folder] archive syntax does not work for this registry: the core dependency is a local filtered copy at .build/core-filtered/model that only scripts/fetch-core.sh materialises, so pointing -r at …/archive/refs/tags/<tag>.zip[registry] fails with IO error for operation on .build/core-filtered/model. --advice-policies and --config do not accept a URL at all — and --advice-policies fails silently on a path it cannot read, loading no policies and leaving only the built-in advisors, so a typo there looks like a registry that suddenly disagrees with itself.

Fetch the tag and materialise the core copy once:

tag="$(git ls-remote --tags --refs --sort=-version:refname \
  https://github.com/ANcpLua/Qyl.Telemetry.SemanticConventions 'v*' | head -n1 | cut -f2)"
tag="${tag#refs/tags/}"                    # the latest tag, v<x.y.z>
curl -sSLo semconv.zip \
  "https://github.com/ANcpLua/Qyl.Telemetry.SemanticConventions/archive/refs/tags/${tag}.zip"
unzip -q semconv.zip                       # -> Qyl.Telemetry.SemanticConventions-<version>/
cd Qyl.Telemetry.SemanticConventions-"${tag#v}"
./scripts/fetch-core.sh                    # writes .build/core-filtered/model
weaver registry live-check \
  -r registry --include-unreferenced \
  --config registry/.weaver.toml \
  --advice-policies registry/policies/live_check_advice \
  --input-source ../spans.json --fail-on violation

Weaver 0.28.0 answers that --include-unreferenced with ⚠ The flag include_unreferenced is deprecated. Please prefer manually adding the required imports; the warning is expected and the flag stays, because a registry's imports block declares metrics, events and entities only and this registry generates every attribute key its dependencies carry.

git clone --depth 1 --branch "${tag}" followed by ./scripts/fetch-core.sh is equivalent and is what Qyl.Telemetry.AutoInstrumentation's live check (./eng/build.sh LiveCheck) needs in .semconv. scripts/check-live-check-policies.sh pins the exact findings the set produces over a sample covering every rule, and asserts that dropping --config brings the built-in violations back.

Adding a term, a vendor model, or a policy

Every change ends the same way: run ./scripts/generate.sh and commit the regenerated files alongside the YAML, because scripts/check-generated.sh compares them against HEAD.

A term. A qyl-owned attribute goes in registry/qyl/attributes.yaml and must be in the qyl.* namespace — qyl_namespace.rego refuses anything else in that group. An instrument goes in metrics.yaml, an event name in events.yaml, a scope name in the annotations of names.yaml. A term that belongs to upstream is not added here at all: it arrives by moving the core or genai pin.

A vendor model. One file per library under registry/vendor/, with two groups. registry.vendor.<library> carries the attributes into the resolved catalog; every attribute needs the file and line of the library release that declares and sets the key. vendor.<library> carries the metadata annotation — library, exact pinned version, repository, ref, licence, and the ActivitySource names it emits on, each with its own file-and-line note. vendor_metadata.rego fails weaver registry check on a file that cannot answer those questions. Vendor rows are development stability and are identified by their registry provenance path, so no prefix allowlist exists anywhere. A vendor key whose root namespace is new must also gain its member in qyl.attribute.namespace, or policies-v2 fails and the dropped-attribute counter would bucket the whole namespace as other.

A policy. A rule about qyl's own groups and their annotations goes in registry/policies/ — Weaver loads that directory from the registry itself. A rule that needs the resolved catalog of every dependency goes in registry/policies-v2/, which scripts/generate.sh passes with -p against --v2 --include-unreferenced. A rule about ingested telemetry rather than the registry is not either of these: it is an advice policy under registry/policies/live_check_advice/, it must decide from the registry entry rather than from a key list, and its finding has to be added to the sample and the expected table in scripts/check-live-check-policies.sh.

Releasing

Bump VersionPrefix in Directory.Build.props, set the registry/manifest.yaml schema_url to https://qyl.at/schemas/<version>, add the CHANGELOG.md entry, run scripts/generate.sh (it writes the same version into typescript/package.json) and commit. Then run ./release.sh <version>: it packs the three NuGet packages and the npm tarball, publishes them with the keys registered in Vitals, tags v<version> and creates the GitHub release. --dry-run packs and runs npm's publish dry run only.

License

Apache-2.0

TypeScript bindings

Weaver's typescript target (templates/registry/typescript) renders the typescript/ package's stable and incubating constants, its lookup catalog and its captured-field policy from the same resolved registry and parameters as the C# bindings. See the TypeScript package documentation. The package has no runtime dependencies. Run npm test --prefix typescript to verify the packed exports.

About

qyl's OpenTelemetry semantic-conventions registry: one Weaver registry generates the stable and incubating C# bindings and analyzers (NuGet) and the TypeScript constants (npm @ancplua/qyl-semantic-conventions)

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages