Documentation
Schema Management
How Telemetry Machine handles schema evolution and type management for your telemetry data.
Telemetry Machine automatically manages schemas for your telemetry data. This guide explains how schema evolution works and what to expect when your instrumentation changes.
How Schema Works
When you send OpenTelemetry data to Telemetry Machine, we automatically:
- Detect new fields and add them to your dataset's schema
- Preserve existing field types to maintain query compatibility
- Handle missing fields gracefully with null values
You don't need to define schemas upfront. Just send data and we handle the rest.
Field Types
Telemetry Machine supports the following field types:
| Type | Description | Example Values |
|---|---|---|
| String | Text data | "GET", "/api/users", "error message" |
| Integer | Whole numbers | 200, 404, 1024 |
| Float | Decimal numbers | 0.5, 3.14159, 99.9 |
| Boolean | True/false | true, false |
| Timestamp | Date and time | 2025-01-16T12:00:00Z |
Type Inference
When we see a new field for the first time, we infer its type from the first value:
| OpenTelemetry Value | Inferred Type |
|---|---|
StringValue | String |
IntValue | Integer |
DoubleValue | Float |
BoolValue | Boolean |
ArrayValue | String (JSON) |
KeyValueList | String (JSON) |
BytesValue | String (Base64) |
Schema Evolution
Adding New Fields
New fields are automatically added to your schema. No action required.
Day 1: You send { service.name, status_code }
Day 2: You add { service.name, status_code, user_id } <-- user_id automatically added
All existing data will show null for the new field. New data will have the actual value.
Type Consistency
Once a field's type is established, we maintain it for query consistency:
Field "status_code" is Integer
✓ Sending 200 → stored as 200
✓ Sending "404" → parsed to 404
✗ Sending "success" → stored as null (cannot parse to integer)
Why we don't change types: If status_code became a string, your existing dashboards with queries like WHERE status_code > 400 would break.
Type Coercion
When data doesn't match the expected type, we attempt automatic conversion:
| Expected | Received | Result |
|---|---|---|
| Integer | "200" | 200 (parsed successfully) |
| Integer | "error" | null (cannot parse) |
| Float | 100 | 100.0 (lossless) |
| Float | "3.14" | 3.14 (parsed successfully) |
| Boolean | "true" | true |
| Boolean | "yes" | true |
| Boolean | "1" | true |
| String | Any | Always succeeds |
Important: When coercion fails, the value becomes null rather than dropping the entire record. Your data is preserved.
Field Namespaces
Telemetry Machine organizes fields into namespaces:
Core Fields (Fixed)
These fields are always present with fixed types:
| Field | Type | Description |
|---|---|---|
_time | Timestamp | Event timestamp |
trace.trace_id | String | Trace identifier |
trace.span_id | String | Span identifier |
span.name | String | Span operation name |
span.kind | String | Span kind (SERVER, CLIENT, etc.) |
span.duration_ns | Integer | Duration in nanoseconds |
span.status_code | Integer | Status code (0=UNSET, 1=OK, 2=ERROR) |
Resource Attributes (resource.*)
Resource attributes describe the entity producing telemetry:
resource.service.name = "api-gateway"
resource.deployment.environment = "production"
resource.cloud.region = "us-east-1"
These are typed columns optimized for filtering and grouping.
Span/Log Attributes (attr.*)
Attributes on individual spans or logs:
attr.http.method = "GET"
attr.http.url = "/api/users"
attr.http.status_code = 200
These are stored flexibly to support high-cardinality data.
Best Practices
1. Use Consistent Types
Send the same field with the same type across all your services:
// Good: All services send status_code as integer
Service A: status_code = 200
Service B: status_code = 404
// Bad: Mixed types cause coercion
Service A: status_code = 200
Service B: status_code = "OK" // Will be stored as null
2. Use Semantic Conventions
Follow OpenTelemetry Semantic Conventions for attribute names:
// Good: Standard names, well-typed
http.request.method = "GET"
http.response.status_code = 200
// Avoid: Custom names make querying harder
my_http_method = "GET"
status = "200"
3. Keep Cardinality in Mind
High-cardinality fields (unique values per request) work best as attributes:
// Good: High-cardinality as attributes
attr.user.id = "user_12345"
attr.request.id = "req_abc123"
// Avoid: High-cardinality as resource attributes
resource.request.id = "req_abc123" // Creates too many unique combinations
Handling Schema Issues
Viewing Your Schema
You can see your dataset's schema in the Telemetry Machine UI:
- Go to your dataset
- Click "Schema" in the sidebar
- View all fields, their types, and cardinality
Type Conflicts
If you see unexpected null values, check for type mismatches:
- Look at the field in the Schema view
- Check if the expected type matches what you're sending
- Update your instrumentation to send the correct type
Common Issues
| Symptom | Likely Cause | Solution |
|---|---|---|
Field shows all null | Type mismatch | Check field type in schema, update instrumentation |
| Queries return no results | Field name typo | Check exact field names in schema |
| Aggregations fail | Field is string, expected number | Ensure numeric fields are sent as numbers |
OpenTelemetry Compatibility
Telemetry Machine is fully compatible with OpenTelemetry. We support:
- OTLP/gRPC and OTLP/HTTP protocols
- All three signal types: Traces, Logs, Metrics
- Semantic conventions (we follow the latest stable version)
Semantic Convention Versions
OpenTelemetry semantic conventions evolve over time (e.g., http.status_code → http.response.status_code). Telemetry Machine handles both old and new convention names.
If you're migrating to newer conventions, both versions will work during the transition period.
FAQ
Can I change a field's type?
Currently, field types cannot be changed after creation. If you need a different type, use a new field name.
What happens to old data when I add a new field?
Old data shows null for the new field. New data has the actual value. Queries work across both.
Is there a limit on number of fields?
There's no hard limit, but we recommend keeping datasets under 1,000 unique fields for optimal query performance.
How do I delete a field?
Fields cannot be deleted, but unused fields don't affect query performance. Contact support if you need to clean up a dataset.
Can I use nested JSON in attributes?
Yes. Arrays and nested objects are serialized as JSON strings. You can use JSON functions in queries to extract values.
-- Extract from JSON attribute
SELECT json_extract(attr.request.headers, '$.content-type') as content_type
FROM traces
Next Steps
- Getting Started - Send your first traces