ADR-0019: Observability - Field Naming: snake_case + dot Convention
Context and Problem
Section titled “Context and Problem”During incident response, searching Logs for the requestCategory field value found in a Trace Span returned 0 results. Logs recorded the same data as request_category. Metrics used yet a third name, request-category. The same business event was recorded with different field names across each Pillar, making Trace-to-Logs transitions impossible in Grafana, and correlation queries on dashboards required manual field name mapping every time.
A single field naming convention enforced across all 3-Pillars was needed to enable immediate correlation analysis, while being naturally compatible with OpenTelemetry Semantic Conventions.
Considered Options
Section titled “Considered Options”- Option 1: camelCase (
requestCategoryName,responseStatus) - Option 2: kebab-case (
request-category-name,response-status) - Option 3: snake_case + dot (
request.category.name,response.status) - Option 4: underscore only (
request_category_name,response_status)
Decision
Section titled “Decision”Option 3: Adopt the snake_case + dot convention.
Rather than inventing a proprietary convention, the pattern already used by OpenTelemetry Semantic Conventions (http.request.method, db.system, rpc.service) is adopted as-is. When aligned with industry standards, there is no need to separately verify tool compatibility.
The following naming rules apply to all observability fields:
- Namespace separation: dot (
.) expresses hierarchy (e.g.,request.category.name,response.status,error.code) - Word separation: snake_case (e.g.,
error.stack_trace,request.content_type) - Count field rule: Metric names use
.count(dot separator); attributes use_count(underscore separator)
Consequences
Section titled “Consequences”- Positive: Searching Logs with the
error.codefield name found in a Trace Span returns results immediately. Grafana’s Trace-to-Logs transitions work without field name mapping. The dot hierarchy enables tree-structured field exploration likerequest.*anderror.*in Grafana and Kibana. The entire team only needs to remember one principle: “follow the OpenTelemetry convention.” - Negative: All fields previously recorded in camelCase or underscore must be migrated, and existing dashboard queries must be updated as well. Some tools that do not interpret dot separators as namespaces may require escaping.
Confirmation
Section titled “Confirmation”- Verify that all Trace Attributes, Metric Attributes, and Structured Log Properties generated by the framework follow the snake_case + dot convention.
- Verify that the same field name can be used to correlate Traces and Logs on Grafana dashboards.
- Verify that count fields correctly use
.countfor metrics and_countfor attributes.
Pros and Cons of the Options
Section titled “Pros and Cons of the Options”Option 1: camelCase
Section titled “Option 1: camelCase”- Pros: Familiar to C#/JavaScript developers. Matches
System.Text.Json’s default policy (JsonNamingPolicy.CamelCase), making serialization convenient. - Cons: Misaligned with OpenTelemetry Semantic Conventions (
http.request.method), causing framework-generated fields and user-defined fields to have mixed naming. Cannot express hierarchy, so names become long likerequestCategoryName, and tree exploration likerequest.*is impossible in Grafana.
Option 2: kebab-case
Section titled “Option 2: kebab-case”- Pros: Consistent with URLs and HTTP headers (
Content-Type,Accept-Language), offering good readability. - Cons: Serilog’s
LogContext.PushPropertydoes not support property names containing hyphens. Cannot be used as C# identifiers, requiring all field names to be managed as string literals with high typo risk. Misaligned with OpenTelemetry conventions.
Option 3: snake_case + dot
Section titled “Option 3: snake_case + dot”- Pros: Fully aligned with OpenTelemetry Semantic Conventions like
http.request.methodanddb.system, so framework fields and user-defined fields follow the same pattern. Dot separation enablesrequest.*anderror.*tree exploration in Grafana and automatic field grouping in Elasticsearch. Guarantees identical field names across all 3-Pillars (Trace, Metrics, Logs), enabling immediate correlation query operation. - Cons: Dot-containing string constants like
"error.stack_trace"must be managed in C# code. Some Elasticsearch configurations may interpret dots as object nesting, causing unintended mappings.
Option 4: Underscore Only
Section titled “Option 4: Underscore Only”- Pros: Matches Prometheus naming conventions (
http_request_duration_seconds). No dot-related issues. Easy to express as C#const string. - Cons: In
request_category_name, it is impossible to tell whetherrequestis a namespace or part of a word, losing hierarchical structure. Misaligned with OpenTelemetry Semantic Conventions’ dot hierarchy, mixing framework fields (http.request.method) and user-defined fields (request_category_name). Readability degrades sharply as field names lengthen with consecutive_characters.
Related Information
Section titled “Related Information”- Commits: a5027a78, 419659df
- Related ADR: ADR-0009 3-Type Error Classification