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, exemplars (see Exemplars).
{
"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"
},
"exemplars": [
{
"timestamp": "2024-01-15T10:29:59.987000000Z",
"value": 142.7,
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"span_id": "b7ad6b7169203331",
"filtered_attributes": {
"http.route": "/api/users/:id"
}
}
]
}Exponential Histogram
Fields: count, sum, min, max, scale, zero_count, positive (offset + bucket_counts), negative (offset + bucket_counts), aggregation_temporality, exemplars (see Exemplars).
{
"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 }
]
}Exemplars
Exemplars are stored as an exemplars array on gauge, sum, histogram,
and exponential_histogram rows. The OTLP Summary data point has no
exemplars in the protocol, so summary rows never emit the field. The array
is omitted entirely when the data point carries no exemplars.
| OTEL Exemplar Field | BZRK Field | Type | Notes |
|---|---|---|---|
time_unix_nano | timestamp | DateTime | Always present |
as_double / as_int (oneof) | value | Double/Long | Mirrors the parent data point's value type |
trace_id | trace_id | String | Hex-encoded; omitted if empty |
span_id | span_id | String | Hex-encoded; omitted if empty |
filtered_attributes | filtered_attributes | Propertybag | Flat keys; omitted if empty |
"exemplars": [
{
"timestamp": "2024-01-15T10:29:59.987000000Z",
"value": 142.7,
"trace_id": "0af7651916cd43dd8448eb211c80319c",
"span_id": "b7ad6b7169203331",
"filtered_attributes": {
"http.route": "/api/users/:id"
}
}
]The shape mirrors events and links on traces — an array of propertybags
with hex-encoded IDs and a nested filtered_attributes bag — so exemplars
are queryable the same way events/links/positive/negative are.
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)Link.flagsare decomposed intosampled,is_remote, and remainingflags