Documentation
Schema Management
How Telemetry Machine stores fields and evolves schemas for OpenTelemetry data.
Telemetry Machine gives each traces, logs, or metrics dataset a schema for that signal type. You do not need to submit a schema before sending OTLP data.
This page describes OpenTelemetry datasets. Custom event datasets use a different ingestion and schema path.
How schemas evolve
Each signal type starts with fixed columns. Telemetry Machine then adds columns for resource and record attributes as it sees them.
For normal OTLP ingestion, schema growth is additive:
- A new attribute name creates a new nullable column.
- Rows that contain the attribute store its value.
- Rows that do not contain the attribute return
nullfor that column. - Existing rows are not rewritten when a new column appears.
For example:
Day 1: service.name, http.request.method
Day 2: service.name, http.request.method, user.id
Queries covering both days return null for user.id in the Day 1 rows.
Fixed columns
Fixed columns depend on the dataset's signal type. The schema reports native query types such as Utf8, Int64, Double, Boolean, and Timestamp(ns, UTC).
Common trace columns include:
| Column | Query type | Meaning |
|---|---|---|
_time | Timestamp(ns, UTC) | Span start time used for time filtering |
trace.trace_id | Utf8 | Trace ID |
trace.span_id | Utf8 | Span ID |
trace.parent_span_id | Utf8 | Parent span ID |
name | Utf8 | Span name |
kind | Int64 | OpenTelemetry span-kind enum, from 0 to 5 |
duration_ns | Int64 | Span duration in nanoseconds |
status_code | Int64 | 0=UNSET, 1=OK, 2=ERROR |
resource.service.name | Utf8 | Service name |
Use name, kind, duration_ns, and status_code in trace queries. They do not have a span. prefix.
Logs have fixed columns such as log.body, log.severity_number, and log.severity_text. Metrics have fixed columns such as metric.name, metric.type, value_int, and value_double.
Attribute columns
Telemetry Machine flattens OpenTelemetry attributes into columns:
| Prefix | Source |
|---|---|
attr.* | Span, log-record, or metric-data-point attributes |
resource.* | Resource attributes |
For example, an OpenTelemetry span attribute named http.request.method becomes attr.http.request.method.
Dynamic attr.* and resource.* values are stored as strings. This applies even when the OTLP AnyValue contains an integer, double, or boolean.
OTLP AnyValue field | Stored text |
|---|---|
string_value: "GET" | GET |
int_value: 200 | 200 |
double_value: 3.14 | 3.14 |
bool_value: true | true |
array_value | JSON array |
kvlist_value | JSON object |
bytes_value | Base64 text |
The original scalar type is therefore not preserved for a dynamic attribute. Compare these fields as strings, or cast them explicitly when a query needs a numeric or boolean type.
Attribute names
Use current OpenTelemetry semantic conventions where possible. Current HTTP span attributes include:
attr.http.request.method
attr.url.path
attr.http.response.status_code
Resource attributes describe the observed service, process, container, host, or other entity. They should remain stable for the lifetime of that resource.
resource.service.name
resource.deployment.environment.name
resource.cloud.region
Put values that change for each operation, such as request IDs and user IDs, on the span or log record instead of the resource.
Telemetry Machine stores attribute names as they arrive. It does not translate old and new semantic-convention names into aliases. For example, attr.http.status_code and attr.http.response.status_code are separate columns. Update queries when your instrumentation changes names.
View a dataset's schema
Use tm-cli to get the authoritative field names and query types:
If you do not have tm-cli yet, follow Getting Started first.
tm-cli schema get <dataset> --type traces
Use --type logs or --type metrics for those signal types. When you need a non-current context, put -c <context> immediately after tm-cli.
The schema output is the source of truth for that dataset. Check it before writing a query if you are unsure about a field name or type.
Troubleshooting
A new field is null in older rows
This is expected. Adding a column does not rewrite data that arrived before the field existed.
A numeric attribute is shown as a string
This is expected for dynamic OpenTelemetry attributes. Telemetry Machine stores attr.* and resource.* values as strings. Cast the field in the query if you need numeric operations.
A query stopped matching after an instrumentation upgrade
Check the dataset schema for both the old and new attribute names. Semantic-convention migrations can change a name, and Telemetry Machine keeps the names as separate columns.
An array or object needs to be queried
Arrays and key-value lists are stored as JSON text. Query them with the typed JSON functions, such as json_get_str, json_get_int, json_get_float, or json_get_bool.
OTLP support
Telemetry Machine accepts OTLP over HTTP with Protocol Buffers for traces, logs, and metrics:
| Signal | Path |
|---|---|
| Traces | /v1/traces |
| Logs | /v1/logs |
| Metrics | /v1/metrics |
The public ingestion endpoint does not expose OTLP over gRPC.
FAQ
Can I change a field's type?
Fixed column types come from the signal schema. Dynamic OpenTelemetry attributes are stored as strings. There is no field-type editor in the public product.
Can I rename or delete a field?
There is no per-field rename or delete control in the public product. Send the new attribute name from your instrumentation. The old column remains in the dataset and returns null for rows that no longer send it.
What happens to old data when I add a field?
Old rows return null for the new field. New rows contain the value when the producer sends it.
Can I send arrays or nested objects in attributes?
Yes. OTLP arrays and key-value lists are serialized as JSON strings. Byte arrays are stored as Base64 text.
Next steps
- Getting Started - Create a dataset and send your first telemetry