OpenTelemetry

Semantic conventions are the platform part of OpenTelemetry

A direct, engineer-to-engineer guide to why OpenTelemetry semantic conventions matter for reusable dashboards, cleaner incidents, safer upgrades, and GenAI telemetry.

Everybody says they are OpenTelemetry-native now. Every vendor accepts OTLP. Every deck has a Collector box in it. Every platform claims logs, metrics, and traces finally live in one place.

Fine. But here is the test: when your data lands, does service.name still mean service.name? Does http.response.status_code survive as a queryable field? Can an engineer jump from a trace to logs to metrics without carrying a private dictionary in their head?

If the answer is no, you do not have an OpenTelemetry platform. You have an OTLP intake pipe with a costume on.

Semantic conventions are the boring part people skip when they are excited about instrumentation. That is a mistake. Instrumentation lets teams emit telemetry. Semantic conventions make that telemetry usable after the fifth team, the fiftieth service, and the first production incident where nobody has spare brain capacity for field archaeology.

Instrumentation was the first win

Before OpenTelemetry, instrumentation was often something the observability vendor did to you. Install the agent, accept the fields, hope your framework was supported, and then try to explain your application in whatever vocabulary showed up.

OpenTelemetry changed the ownership model. Application teams can put spans where intent exists. They can use SDKs, auto-instrumentation, and OTLP. Platform teams can route, enrich, filter, redact, and sample through the Collector without wiring every service directly to one backend.

That part matters. It moved observability closer to the engineers who understand the code.

But ownership without a shared language has a short half-life. One team emits status_code, another emits http_status, another emits responseCode, and another is on old HTTP conventions. Everyone was locally reasonable. The global result is a mess.

Semantic conventions are how you keep team ownership from becoming platform chaos.

The contract is the product

The OpenTelemetry docs describe semantic conventions as a common set of attributes that give meaning to telemetry data: span names, metric instruments, units, attribute names, types, meanings, and valid values. That sounds dry because standards always sound dry until you need them at 02:13.

Here is the useful version: semantic conventions are the contract between emitters, the Collector, storage, dashboards, alerts, and the engineer holding the pager.

They answer questions like:

Concept Canonical shape you want to preserve
Which service emitted this? service.name
Which version is running? service.version
Which environment is this? deployment.environment.name
Which Kubernetes namespace? k8s.namespace.name
Which HTTP method? http.request.method
Which HTTP response code? http.response.status_code
Which database system? db.system.name

Those names are not decoration. They are the handles your platform gives engineers during real work. If the backend rewrites them into private names, or if every team invents its own local spelling, you pay the translation cost forever.

Several local HTTP status field names flowing into the canonical OpenTelemetry http.response.status_code attribute

Schema drift is operational debt

The fastest way to spot an immature observability platform is to look at the queries. If the error-rate query looks like this, the platform is already billing you in human attention:

status_code >= 500
OR http_status >= 500
OR responseCode >= 500
OR http.status_code >= 500
OR http.response.status_code >= 500

That is not flexibility. That is schema debt.

The debt compounds because telemetry names escape everywhere. Dashboards depend on them. Alerts depend on them. Runbooks mention them. Incident reviews quote them. Internal libraries accidentally freeze them. Once five teams and three dashboards depend on five names for one thing, cleaning it up becomes a migration project instead of a naming fix.

Platform engineering exists for exactly this kind of problem. When the same pain shows up across teams, solving it service by service is negligent. A platform should make the good path easier than the weird path.

For observability, that means a default SDK bootstrap, auto-instrumentation where it helps, a Collector pipeline with sane enrichment and redaction, resource attributes applied consistently, and a clear rule for domain-specific attributes. Semantic conventions keep that whole system honest.

Without them, every team becomes a tiny schema committee. Nobody joined your company to attend that meeting.

A checkout incident makes the point

Imagine checkout is broken. The gateway is throwing 502s. Payment spans are slow. Logs are full of retries. A dashboard says error rate is up, but the graph alone does not tell you which service changed, which environment is affected, or whether this is one bad deploy or a dependency problem.

If your signals share the same resource model, the first moves are boring in the best way:

  • Filter to deployment.environment.name = "production".
  • Group by service.name.
  • Compare versions with service.version.
  • Look at HTTP failures through http.response.status_code.
  • Pivot from a trace to logs and metrics for the same service.

This is what engineers actually need during an incident. Not a theory of observability. Not a vendor diagram. Just one language across the signals that describe the same production system.

Now run the same incident with drift. Logs have status. Metrics have code. Traces have http.status_code. The gateway has upstream_status. The AI service has model_status because someone shipped in a hurry and it seemed obvious at the time.

The incident is no longer only about checkout. It is also about remembering which field means what in which place.

That is brutal, because attention is the scarce resource in incidents. Semantic conventions protect attention by deleting an entire class of arguments: what did this field mean again?

The platform should standardize the boring parts

Do not write a forty-page naming document and call it done. Engineers do not need more PDFs to feel guilty about. They need defaults.

Start with resource identity. If service.name, service.version, environment, namespace, cluster, cloud, and region are inconsistent, every higher-level dashboard is built on mud.

Then standardize the common protocols before inventing local names. HTTP, database, messaging, RPC, runtime, Kubernetes, and cloud telemetry already have conventions. A company-specific name for a concept OpenTelemetry already covers is not customization. It is future migration work wearing a nice shirt.

Give product teams a safe place for domain context. Fields like tenant.id, checkout.step, plan.tier, or experiment.name can make telemetry much more useful. But they need an owner, an expected cardinality, allowed values when possible, and a decision about whether they belong in logs, spans, metrics, or all three.

Here is the platform contract I would want written down:

Area Platform default Team freedom
Resource identity Standard attributes are required Teams can add owned domain context
Protocol telemetry Use OpenTelemetry conventions first Add custom fields only when the spec has no concept
Cardinality High-cardinality fields are reviewed Useful high-cardinality fields can exist with retention controls
Sensitive data Redaction and sampling are default Content capture requires explicit opt-in
Migrations Old and new names coexist for a window Teams get a published upgrade path

The standard does not remove engineering judgment. It gives judgment a place to stand.

Convention changes are production changes

Semantic conventions evolve. Some groups are stable. Some are still changing. That is not a flaw. Production systems evolve too.

The operational mistake is treating a semantic-convention upgrade like a harmless dependency bump. It is not harmless if dashboards, alerts, and backend views depend on the old names.

HTTP is the example most teams recognize. Older instrumentation may emit fields like http.status_code, while stable HTTP conventions use fields like http.response.status_code. During migration, you need a period where old and new shapes can coexist, dashboards can be updated, and backend views can handle both.

That is unglamorous platform work. It is also the work that keeps one SDK rollout from surprising every team at once.

A healthy process looks like this:

  1. Pin SDK and auto-instrumentation versions.
  2. Publish the convention version your golden path expects.
  3. Test a sample service through the Collector and backend.
  4. Verify the emitted attribute names are still queryable.
  5. Run old and new names side by side during migration when needed.
  6. Remove compatibility only after dashboards and alerts have moved.

If that sounds like release engineering, good. Observability schemas are production interfaces.

GenAI makes the naming problem louder

Agents make this whole topic less optional.

A normal service receives a request, calls a database, maybe calls another service, and returns a response. Agents do not stay inside that tidy box. They read instructions, retrieve context, call tools, ask models for output, inspect results, retry, branch, and keep going.

When an agent gets expensive or weird, a transcript is not enough. You need to know:

  • which agent ran
  • which operation it performed
  • which model was requested
  • which model actually responded
  • how many input and output tokens it used
  • which tool it called
  • what service or database sat behind that tool
  • whether the failure was in the model, the tool, the dependency, or the workflow

Without a shared vocabulary, every AI team invents its own labels. model, llm.model, gen_model, prompt_tokens, input, completion_tokens, tool, function, workflow_step. Then the support team sees slow tickets, the AI team sees token growth, the platform team sees database latency, and nobody can stitch the story together without a translation exercise.

OpenTelemetry’s GenAI conventions are trying to give this a common shape. The useful direction is attributes such as gen_ai.operation.name, gen_ai.provider.name, gen_ai.request.model, gen_ai.response.model, gen_ai.agent.name, and gen_ai.tool.name, plus token usage fields like gen_ai.usage.input_tokens and gen_ai.usage.output_tokens.

The point is not that every field is forever. The point is that agent telemetry cannot live in a special corner. If the agent calls a tool, and that tool calls a service, and that service calls a database, the whole path has to be debuggable as one production system.

Agent, model, tool, database, and service telemetry connected by shared OpenTelemetry attributes

There is one hard rule here: GenAI telemetry can contain sensitive data. Prompts, messages, retrieved documents, tool arguments, tool results, and system instructions should be captured deliberately, with redaction, truncation, sampling, and opt-in content capture. Semantic conventions give you names. Platform policy decides what is safe to store.

OpenTelemetry-native should mean preserving meaning

Accepting OTLP is table stakes. It is the first checkbox, not the finish line.

If a backend accepts OTLP and immediately translates everything into a private schema, the protocol is open but the operational model is not. Engineers still have to learn a vendor-specific field language. Dashboards still become sticky. Exports still carry translated meaning. Migration still hurts.

When someone says a tool is OpenTelemetry-native, ask harder questions:

Question What a good answer sounds like
Are emitted attributes preserved? service.name and http.response.status_code remain visible and queryable
Do signals share resource identity? Logs, metrics, and traces agree on service, version, environment, namespace, and region
Are migrations explicit? Old and new convention names have a documented compatibility window
Can any Collector send data? The platform does not require a private Collector fork to keep semantics intact
Can teams inspect drift? You can see which services emit which convention versions and fields

This is the real bar. Not “does it ingest the bytes?” but “does it preserve the meaning?”

What I would standardize first

If I were building the platform path from scratch, I would start small and be strict.

First, resource identity. Make the service, version, environment, Kubernetes, cloud, and region attributes boringly consistent. This is the join surface for everything else.

Second, HTTP and database conventions. These appear in almost every incident, and inconsistent status, route, method, and database names create a shocking amount of operational noise.

Third, Collector policy. Enrich at the edge. Redact known risky fields. Drop known useless fields. Set retention rules by signal and cardinality, not by vibes.

Fourth, domain context. Give product engineers blessed helpers for attributes like tenant.id, checkout.step, and plan.tier, with ownership and cardinality expectations built in.

Fifth, GenAI conventions. If agents touch production workflows, standardize model, provider, operation, agent, token usage, tool, retrieval, and prompt-version attributes early. Retroactive cleanup will be worse.

Finally, test the round trip. Emit a known span, metric, and log from a sample service. Send them through the Collector. Query them in the backend. Confirm the names that came out are the names that went in.

That one test tells you whether your platform is preserving OpenTelemetry or merely accepting OTLP.

Conclusion

OpenTelemetry gave engineers a better way to emit telemetry. Semantic conventions are what keep that freedom useful after the organization grows.

Without a shared vocabulary, freedom at emit time becomes translation work at query time. With one, logs, metrics, traces, and agent telemetry can describe the same system in the same language.

That is why semantic conventions are not a side quest. They are the platform part of OpenTelemetry. They are how you get reusable dashboards, safer upgrades, cleaner alerts, real correlation, and fewer incident calls where half the room is trying to remember what a field name means.