OTEL Field Mapping
How OpenTelemetry Protocol fields map to Berserk columns for traces, logs, and metrics
Berserk converts OTLP data into rows with the following conventions:
- Field names use
snake_case - Timestamps are stored as
DateTime(nanoseconds since epoch) - Durations are stored as
Timespan(100-nanosecond ticks) - IDs (trace, span) are hex-encoded strings
- Attribute keys are stored as-is (e.g.,
"http.method"stays as a flat key underattributes). Dotted-path shortcuts let you also access them asattributes.http.method. - Flattened metadata: Scope, metric, and status metadata are top-level prefixed columns
- Empty/default fields are omitted to save space
Common Structures
Resource
Resource IS the attributes map directly. dropped_resource_attributes_count is stored as a separate top-level field.
resource: {
"service.name": "my-service",
"host.name": "host-1"
}
dropped_resource_attributes_count: 5 // only if > 0Scope
Scope metadata is flattened to top-level columns. The scope bag holds only scope attributes.
| OTEL Field | Berserk Field | Type |
|---|---|---|
InstrumentationScope.name | scope_name | String |
InstrumentationScope.version | scope_version | String |
InstrumentationScope.attributes | scope | Propertybag |
scope_name: "opentelemetry-java",
scope_version: "1.25.0",
scope: { // only if non-empty, flat keys
"custom.attr": "value"
}Attributes
All attributes (span, log, event, link) store keys as flat strings:
attributes: {
"http.method": "GET",
"http.status_code": 200
}Accessible via both attributes.['http.method'] (bracket notation) and attributes.http.method (dotted shortcut).
Traces (Spans)
Each OTLP span becomes one row.
| OTEL Field | Berserk Field | Type | Notes |
|---|---|---|---|
start_time_unix_nano | start_time | DateTime | Span start timestamp |
end_time_unix_nano | end_time | DateTime | Span end timestamp |
end_time_unix_nano | timestamp | DateTime | Primary timestamp (copy of end_time) |
| (computed) | ingest_time | DateTime | Ingestion timestamp |
| (computed) | duration | Timespan | (end_time - start_time) / 100 |
trace_id | trace_id | String | Hex-encoded |
span_id | span_id | String | Hex-encoded |
parent_span_id | parent_span_id | String | Hex-encoded, omitted if empty |
name | span_name | String | Span operation name |
kind | span_kind | String | INTERNAL, SERVER, CLIENT, PRODUCER, CONSUMER |
trace_state | trace_state | String | W3C trace state |
status.code | status_code | String | "UNSET", "OK", "ERROR" |
status.description | status_description | String | Only if non-empty |
flags (bit 0) | sampled | Boolean | Only if sampled flag is set |
flags (bits 1-31) | flags | Long | Only if uninterpreted bits remain |
attributes | attributes | Propertybag | Flat keys |
resource | resource | Propertybag | IS the attributes map directly |
scope.name | scope_name | String | Flattened to top level |
scope.version | scope_version | String | Flattened to top level |
scope.attributes | scope | Propertybag | Scope attributes only |
events | events | Array | See Events structure |
links | links | Array | See Links structure |
dropped_attributes_count | dropped_attributes_count | Long | Only if > 0 |
dropped_events_count | dropped_events_count | Long | Only if > 0 |
dropped_links_count | dropped_links_count | Long | Only if > 0 |
| (from resource) | dropped_resource_attributes_count | Long | Only if > 0 |
| (from scope) | dropped_scope_attributes_count | Long | Only if > 0 |
Events
Each event in the events array is a propertybag with timestamp, name, attributes, and optional dropped_attributes_count.
Links
Each link in the links array is a propertybag with trace_id, span_id, optional trace_state, sampled, is_remote, flags, attributes, and optional dropped_attributes_count.
Example
{
"timestamp": "2024-01-15T10:30:00.150000000Z",
"start_time": "2024-01-15T10:30:00.000000000Z",
"end_time": "2024-01-15T10:30:00.150000000Z",
"ingest_time": "2024-01-15T10:31:00.000000000Z",
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"span_id": "b7ad6b7169203331",
"parent_span_id": "00f067aa0ba902b7",
"span_name": "GET /api/users",
"span_kind": "SERVER",
"duration": "150ms",
"status_code": "OK",
"attributes": {
"http.method": "GET",
"http.status_code": 200
},
"resource": {
"service.name": "user-service",
"service.version": "1.0.0"
},
"scope_name": "opentelemetry-java",
"scope_version": "1.25.0"
}Logs
Each OTLP log record becomes one row.
| OTEL Field | Berserk Field | Type | Notes |
|---|---|---|---|
time_unix_nano | timestamp | DateTime | Falls back to current time if 0 |
observed_time_unix_nano | observed_time | DateTime | |
| (computed) | ingest_time | DateTime | Ingestion timestamp |
trace_id | trace_id | String | Hex-encoded, for log-trace correlation |
span_id | span_id | String | Hex-encoded |
severity_number | severity_number | Long | 1-24 per OTEL spec |
severity_text | severity_text | String | INFO, ERROR, etc. |
body | body | Dynamic | String, object, or array |
flags (bit 0) | sampled | Boolean | Only if sampled flag is set |
flags (bits 1-31) | flags | Long | Only if uninterpreted bits remain |
attributes | attributes | Propertybag | Flat keys |
resource | resource | Propertybag | IS the attributes map directly |
scope.name | scope_name | String | Flattened to top level |
scope.version | scope_version | String | Flattened to top level |
scope.attributes | scope | Propertybag | Scope attributes only |
dropped_attributes_count | dropped_attributes_count | Long | Only if > 0 |
| (from resource) | dropped_resource_attributes_count | Long | Only if > 0 |
| (from scope) | dropped_scope_attributes_count | Long | Only if > 0 |
Example
{
"timestamp": "2024-01-15T10:30:00.000000000Z",
"observed_time": "2024-01-15T10:30:00.001000000Z",
"ingest_time": "2024-01-15T10:31:00.000000000Z",
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"span_id": "b7ad6b7169203331",
"severity_number": 9,
"severity_text": "INFO",
"body": "User login successful",
"attributes": {
"user.id": "12345"
},
"resource": {
"service.name": "auth-service"
},
"scope_name": "com.example.auth",
"scope_version": "2.0.0"
}Metrics
Each OTLP metric data point becomes one row. The structure varies by metric type.
Common Fields
| OTEL Field | Berserk Field | Type | Notes |
|---|---|---|---|
time_unix_nano | timestamp | DateTime | Data point timestamp |
| (computed) | ingest_time | DateTime | Ingestion timestamp |
start_time_unix_nano | start_time | DateTime | Aggregation window start |
Metric.name | metric_name | String | Flattened from metric metadata |
Metric.type | metric_type | String | "gauge", "sum", "histogram", etc. |
Metric.description | metric_description | String | Flattened from metric metadata |
Metric.unit | metric_unit | String | Flattened from metric metadata |
attributes | attributes | Propertybag | Flat keys, data point attributes |
resource | resource | Propertybag | IS the attributes map directly |
scope.name | scope_name | String | Flattened to top level |
scope.version | scope_version | String | Flattened to top level |
flags (bit 0) | no_recorded_value | Boolean | Only if no_recorded_value flag is set |
flags (bits 1-31) | flags | Long | Only if uninterpreted bits remain |
| (from resource) | dropped_resource_attributes_count | Long | Only if > 0 |
| (from scope) | dropped_scope_attributes_count | Long | Only if > 0 |
Gauge / Sum
Single value field (int or double). Sum also includes aggregation_temporality and is_monotonic.
{
"timestamp": "2024-01-15T10:30:00.000000000Z",
"metric_name": "http.server.request.duration",
"metric_type": "sum",
"metric_unit": "ms",
"value": 1234.5,
"aggregation_temporality": "CUMULATIVE",
"attributes": {
"http.method": "GET",
"http.status_code": 200
},
"resource": {
"service.name": "api-gateway"
},
"scope_name": "opentelemetry-collector"
}Histogram
Fields: count, sum, min, max, bucket_counts, explicit_bounds, aggregation_temporality.
{
"timestamp": "2024-01-15T10:30:00.000000000Z",
"metric_name": "http.server.request.duration",
"metric_type": "histogram",
"metric_unit": "ms",
"count": 100,
"sum": 5432.1,
"min": 1.2,
"max": 234.5,
"bucket_counts": [10, 25, 40, 20, 5],
"explicit_bounds": [10, 50, 100, 200],
"aggregation_temporality": "DELTA",
"attributes": {
"http.method": "GET"
},
"resource": {
"service.name": "api-gateway"
}
}Exponential Histogram
Fields: count, sum, min, max, scale, zero_count, positive (offset + bucket_counts), negative (offset + bucket_counts), aggregation_temporality.
{
"timestamp": "2024-01-15T10:30:00.000000000Z",
"metric_name": "http.server.request.duration",
"metric_type": "exponential_histogram",
"count": 100,
"sum": 5432.1,
"min": 1.2,
"max": 234.5,
"scale": 3,
"zero_count": 2,
"positive": {
"offset": 5,
"bucket_counts": [10, 25, 40, 20, 5]
},
"negative": {
"offset": 0,
"bucket_counts": [1, 2]
},
"aggregation_temporality": "CUMULATIVE"
}Summary
Fields: count, sum, quantile_values (array of {quantile, value}).
{
"timestamp": "2024-01-15T10:30:00.000000000Z",
"metric_name": "http.server.request.duration",
"metric_type": "summary",
"count": 100,
"sum": 5432.1,
"quantile_values": [
{ "quantile": 0.5, "value": 45.2 },
{ "quantile": 0.9, "value": 123.4 },
{ "quantile": 0.99, "value": 198.7 }
]
}Omitted Fields
The following OTEL fields are intentionally not stored:
- Schema URLs at all levels (not needed for storage)
InstrumentationScope.dropped_attributes_count(low value)- Metric exemplars (high cardinality; can be added if needed)
Link.flagsare decomposed intosampled,is_remote, and remainingflags