Data model
Fixter stores three signals (logs, spans, metrics), built for fast time-scoped search and aggregation. Everything you send over OTLP is normalized into these shapes, and everything you query reads them. All access is scoped to your workspace.
| Field | Notes |
|---|---|
timestamp | nanosecond precision |
log_id | server-generated ULID, a stable identity for permalinks and lookups |
service | from service.name, the primary grouping dimension |
level | upper-cased, but not normalized to a fixed set. See the note below |
message | the log body, indexed for token and substring search |
trace_id / span_id / parent_span_id | correlation to traces, when present |
source_instance_id | which instance of the service emitted the record |
resource | resource attributes (host, deployment, …), queryable as resource.<key> |
payload | log attributes, queryable directly by attribute name |
Your own attributes are first-class: whatever keys your instrumentation emits become queryable fields by name, no schema registration needed (see dynamic attributes).
A note on level, because it affects the queries you write. When your SDK sends a
severity text, Fixter stores that text upper-cased, exactly as sent. Only when severity
text is absent does it derive a level from the OTLP severity number, and then you get
one of TRACE, DEBUG, INFO, WARN, ERROR, or FATAL. So level holds whatever
your logging library calls things: CRITICAL, SEVERE, PANIC, ERR, WARNING and
others all occur in practice. Filtering on level = 'ERROR' alone can therefore miss
real errors. Prefer a set: level IN ('ERROR', 'FATAL', 'CRITICAL', 'PANIC'), matched
to what your services actually emit.
Where to see logs: the Logs page (filter bar, live
tail, SQL mode), the logs, get_log, and get_log_neighbors
MCP tools, or SELECT ... FROM logs in
QuerySQL.
| Field | Notes |
|---|---|
timestamp | when the span started |
duration_ms | how long it took (fractional milliseconds) |
trace_id / span_id / parent_span_id | the trace identity triple |
service | the emitting service |
name | the operation (an endpoint, a query, a job step) |
kind | SERVER, CLIENT, PRODUCER, CONSUMER, INTERNAL, or UNSPECIFIED |
status_code | UNSET, OK, or ERROR, with status_message for detail |
source_instance_id | which instance emitted the span |
resource / payload | resource and span attributes, queryable by name |
end_customer | the customer_id span attribute, lifted to a field so you can slice by your own tenant |
Span events and links are stored on the span (exception events land there) but are not
queryable fields in QuerySQL. Read them through the trace view
or the get_trace MCP tool.
Fixter continuously maintains rollups on top of spans, so service-level RED metrics (request rate, error rate, latency percentiles) answer fast without scanning raw data.
Where to see spans: any log with a trace opens its full trace view in the app
(/traces/<trace-id>), and the spans, get_trace, and aggregate_spans
MCP tools or SELECT ... FROM spans in
QuerySQL query them directly.
Metrics
Section titled “Metrics”| Field | Notes |
|---|---|
timestamp | sample time |
metric_name | the metric’s name |
service | the emitting service |
source_instance_id | which instance emitted the sample |
value | the scalar value, for gauges and counters |
attributes | metric attributes, queryable by name |
Metrics are stored with the full OTLP model behind these fields: the metric type
(GAUGE, SUM, HISTOGRAM, EXPONENTIAL_HISTOGRAM, SUMMARY), delta and cumulative
temporality, histogram buckets, summary quantiles, count/sum/min/max, and exemplars
that link a sample back to the trace that produced it.
Those are not queryable columns in QuerySQL, which exposes
the five fields above plus your own attributes. Reach the rest through the
Metrics page or the list_metrics and metrics
MCP tools. A query like WHERE type = 'HISTOGRAM' will not
compile.
Where to see metrics: the Metrics page in the
app, the list_metrics and metrics MCP tools, or
SELECT ... FROM metrics in QuerySQL.
Retention
Section titled “Retention”Retention means how far back your data stays accessible for query. It is two years across all three signals. Nothing expires sooner, and you do not manage or tune it.
Querying beyond the app
Section titled “Querying beyond the app”When point-and-click runs out, QuerySQL (Fixter’s read-only
SQL dialect) queries logs, spans, and metrics directly, with monitoring-flavored
functions (latency percentiles, time bucketing, full-text matching) on top of familiar
SQL. It is the same language the Logs page’s SQL mode, the run_sql
MCP tool, and alert rule conditions all speak.