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:

  1. Detect new fields and add them to your dataset's schema
  2. Preserve existing field types to maintain query compatibility
  3. 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:

TypeDescriptionExample Values
StringText data"GET", "/api/users", "error message"
IntegerWhole numbers200, 404, 1024
FloatDecimal numbers0.5, 3.14159, 99.9
BooleanTrue/falsetrue, false
TimestampDate and time2025-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 ValueInferred Type
StringValueString
IntValueInteger
DoubleValueFloat
BoolValueBoolean
ArrayValueString (JSON)
KeyValueListString (JSON)
BytesValueString (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:

ExpectedReceivedResult
Integer"200"200 (parsed successfully)
Integer"error"null (cannot parse)
Float100100.0 (lossless)
Float"3.14"3.14 (parsed successfully)
Boolean"true"true
Boolean"yes"true
Boolean"1"true
StringAnyAlways 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:

FieldTypeDescription
_timeTimestampEvent timestamp
trace.trace_idStringTrace identifier
trace.span_idStringSpan identifier
span.nameStringSpan operation name
span.kindStringSpan kind (SERVER, CLIENT, etc.)
span.duration_nsIntegerDuration in nanoseconds
span.status_codeIntegerStatus 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:

  1. Go to your dataset
  2. Click "Schema" in the sidebar
  3. View all fields, their types, and cardinality

Type Conflicts

If you see unexpected null values, check for type mismatches:

  1. Look at the field in the Schema view
  2. Check if the expected type matches what you're sending
  3. Update your instrumentation to send the correct type

Common Issues

SymptomLikely CauseSolution
Field shows all nullType mismatchCheck field type in schema, update instrumentation
Queries return no resultsField name typoCheck exact field names in schema
Aggregations failField is string, expected numberEnsure 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_codehttp.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