Metrics & Attribute Naming Conventions
This page is the team reference for naming metrics, attributes and events. It follows the OpenTelemetry Semantic Conventions - Naming spec, so use that as the source of truth if anything here is ambiguous.
1. Before you name anything: check what already exists
Creating a brand new name is a last resort, not a first step. In order:
- Attribute/metric registry - check the OpenTelemetry Semantic Convention Registry for an existing attribute/metric that already matches your semantics.
- Splunk/Splunk-O11y OTel receivers - check the Splunk OTel component receivers for metrics already produced by a receiver for the technology you're instrumenting (e.g. JVM, Kafka, Flink).
- Only if neither exists - it's a custom metric, and naming must be agreed by the tribes (this doc + O11y standards), not invented ad hoc per team.
Rule of thumb: if a semantic convention or receiver metric already covers your case, use it exactly as-is - even if you'd have picked a different name yourself. Matching semantics beats naming preference every time.
2. Core OTel naming rules
These apply to metric names, attribute names and event names alike:
- Lowercase only.
- Namespace with dots.
service.version= theservicenamespace,versionattribute. Namespaces can nest:telemetry.sdkis a namespace insidetelemetry, andtelemetry.sdk.nameis an attribute inside it. - Snake_case within a component. Each dot-delimited segment that is multi-word uses underscores:
http.response.status_code(thehttpnamespace,responsenamespace,status_codeattribute). - Underscore instead of dot only when namespacing doesn't make sense, or would change the meaning. E.g.
rate_limiting, notrate.limiting. - Be precise, not generic. Name the property, not just the object:
file.owner.namenotfile.owner;system.network.packet.droppednotsystem.network.dropped. - Avoid ambiguous, generic terms that could clash with other conventions:
security_rulenotrule. - Prefer shorter names when clarity isn't lost. Drop redundant namespace segments:
vcs.change.idis precise enough - you don't needvcs.repository.change.id.
3. The golden rule: domain/technology first, never app/company first
This is the rule most likely to trip people up, and the one to focus on.
When you need a custom attribute or metric beyond the semantic conventions, name it after the domain or technology it describes, never after your application, service, or TLA.
| Bad (app/TLA-first) | Why it's wrong |
|---|---|
bcg.request.count |
Locks the metric to one service; can't compare/aggregate across TLAs |
process.tla.threads |
tla is not a real concept outside our org - meaningless namespace |
myapp.request.size |
Not reusable, not comparable, vendor/app-locked |
osg.user.id |
Company/app prefix pollutes the namespace |
| Good (domain/technology-first) | Why it works |
|---|---|
http.server.request.duration |
Standard convention - works for every service |
jvm.memory.used |
Root namespace matches the existing JVM receiver/convention |
messaging.kafka.message.key |
Technology-scoped, reusable across any Kafka producer/consumer |
inventory.item.count |
Business-domain concept, reusable outside our stack |
Why this matters for us specifically: if every TLA invents its own prefixed metrics (bcg.*, dec.*,
tim.*...) we end up with dozens of metrics that mean the same thing but can't be queried, aggregated or alerted on
together. Abstracting the technology/domain into the metric name and pushing the "which service/TLA is this" question
into an attribute lets us build one dashboard/detector/alert that works across every TLA, filtered by attribute.
4. Do we need a namespace per application/TLA?
No. Do not create a root (or nested) namespace per application, service, or TLA (e.g. no bcg.*, dec.*,
tim.*, myapp.*). This is the single biggest anti-pattern we're moving away from (the old process.tla.* values).
Instead:
-
If the metric is technology-specific (JVM, Kafka, Flink, HTTP, etc.), use the existing root namespace already defined by the relevant OTel semantic convention or Splunk receiver - e.g.
jvm.*for anything JVM-related,kafka.*/messaging.kafka.*for Kafka,http.*for HTTP. -
If the metric is a business/domain concept (not tied to one technology), name it after the domain, not the app:
payment.method,order.status,workflow.step.name. -
To identify which service/TLA/instance produced a data point, use resource attributes, not the metric name or a custom namespace:
service.name,service.instance.id, and (if genuinely needed) a custom attribute liketlaorservice.namespace. This is what lets us filter/slice a shared metric per application in SignalFx instead of maintaining N near-identical metrics.
5. Defining metrics (e.g. Micrometer)
When defining metrics in our application, we still need to check if an appropriate metric already exists. If so, use the exact same name, with the same naming conventions described above in terms of dots and underscores, for two reasons:
- Semantic match rule still applies. If an existing metric/semantic convention already describes what you're measuring, you must use that name rather than inventing a new one. Reusing the name is what makes it comparable/aggregatable with everyone else's data for that concept.
- Micrometer expects dot notation and handles export-time transformation for you. Micrometer's own convention is
lowercase.dot.separated, and eachMeterRegistryapplies its ownNamingConventionwhen exporting — e.g. the Prometheus registry automatically converts dots to underscores (http_server_requests_seconds) and appends unit suffixes. So: ┃
Note: heck whether Spring Boot/Micrometer is already auto-generating that metric (e.g. http.server.requests is emitted automatically for Spring MVC). If so, don't create a duplicate ┃
custom one at all — just add the attributes/tags you need to the existing metric.
6. Reserved namespaces - never use these
otel.*is exclusively reserved for the OpenTelemetry spec itself (otel.scope.name,otel.status_code,otel.span.sampling_result, ...). Never create attributes underotel.*.- Other reserved names you must not repurpose:
error.type,exception.message,exception.stacktrace,exception.type,server.address,server.port,service.name,telemetry.sdk.language,telemetry.sdk.name,telemetry.sdk.version,url.scheme.
7. Grouping and hierarchy
- Related metrics should be nested together in a hierarchy based on usage (e.g. all JVM GC metrics under
jvm.gc.*), so they're discoverable and comparable as a group. - Where similar metrics differ significantly in implementation across technologies (e.g. garbage collection), prefer
a prefixed, technology-scoped name over one shared generic name:
jvm.gc.*rather than a single ambiguousgc.*that mixes runtimes with different semantics. - Common attributes should be named consistently across metrics so they aid discoverability (e.g. always
service.name, notsvc/service_id/serviceNameinterchangeably).
8. Units
- Don't put the unit in the metric name if it's already carried in metadata - only add it to the name when it adds real meaning.
- Units follow UCUM: use base units (
BynotMiBy,sfor durations), case-sensitive form. - Dimensionless/utilization metrics use unit
1. - Counts of things use curly-brace annotations matching grammatical number, e.g.
{request},{error},{packet}(not{requests}).
9. Quick checklist before you ship a new metric/attribute
- Does a semantic convention already cover this? Use it as-is.
- Does a Splunk OTel receiver already emit this for the technology? Use its name/namespace.
- Is this genuinely custom? Name it after the domain/technology, not the app or TLA.
- Does it belong under an existing root namespace (
jvm.*,http.*,messaging.*...)? Nest it there. - Lowercase, dot-namespaced, snake_case within each segment?
- Precise (
{object}.{property}, not a bare object)? - Not colliding with a reserved namespace (
otel.*etc.)? - Can the app/TLA/service be identified via an attribute (
service.name,tla,service.instance.id) instead of baking it into the metric name?