Skip to content

Log formats & field mapping

HeliosLogs is permissive: it accepts the JSON shapes that common loggers already emit, and it parses several non-JSON text formats. This page documents exactly how incoming events are mapped onto HeliosLogs's schema-on-read model.

Universal-core fields and their aliases

Only three keys get special treatment; HeliosLogs recognizes common aliases for each (the first present wins). Everything else keeps its name and becomes a queryable dynamic field.

Core fieldRecognized keys
timestamptimestamp, ts, time, @timestamp, Timestamp, eventTime, datetime, @t, asctime
messagemessage, msg, Body, body, event, Msg, log.body, @m, @mt
sourcesource, log.source, _source

These cover zap, pino, logrus, bunyan, Docker, log4j2/logback (ECS / logstash encoder), Serilog (CLEF), python-json-logger, OpenTelemetry, and more. If a source isn't in the event, the ?source= query parameter is used as a fallback.

Timestamp parsing

timestamp accepts strings or numbers:

  • Strings — ISO 8601 (2026-06-14T18:00:00Z, with offsets), space-separated ISO (2026-06-14 18:00:00, assumed UTC), Apache/CLF (10/Oct/2024:13:55:36 +0000), and Python comma-millis (2024-01-15 10:30:45,123).
  • Numbers — the unit is inferred from magnitude: seconds (including fractional like 1779126000.123), milliseconds, microseconds, or nanoseconds.

If the timestamp is missing or unparseable, the event is routed to the current day rather than rejected.

Nested objects → dotted paths

Nested JSON is flattened into dotted paths at ingest, so you query nested data the same way as flat data:

json
{ "message": "upstream failed",
  "error": { "type": "Timeout", "code": 504, "detail": { "ms": 5000 } } }

becomes the fields error.type, error.code, and error.detail.ms, queryable as:

error.type:Timeout  error.code:504  error.detail.ms:5000

Objects are flattened up to 16 levels deep; anything deeper is stored as one searchable string. Field names are matched case-insensitively.

Arrays → multi-valued fields

Array elements are shredded under the same path, making the field multi-valued:

json
{ "items": [ { "sku": "A1", "qty": 1 }, { "sku": "B2", "qty": 2 } ] }

produces items.sku and items.qty with two values each. An equality query like items.qty:1 matches if any element qualifies. (Range and aggregation use the first element.) Up to 1000 array elements are shredded per array; the rest remain in the full raw event.

OpenTelemetry containers

OTel events nest attributes under Attributes / Resource containers. HeliosLogs flattens these one level into the top-level namespace before mapping (the lowercase attributes / resource are handled too):

json
{ "Timestamp": "2026-06-14T19:01:00Z", "Body": "upstream failed",
  "Attributes": { "service.name": "payment", "http.status_code": 502 },
  "Resource": { "host.name": "payment-99" } }

service.name:payment, http.status_code:502, host.name:payment-99, with Body mapped to message. (A top-level key always wins over a same-named container key.)

Index templating

The ingest index parameter may contain placeholders that resolve per event against the (pre-flatten) JSON, so one stream can fan out to many indexes:

bash
# each event routes by its service field: app-checkout, app-payment, …
curl -X POST 'http://localhost:7300/api/ingest?index=app-{{service}}' --data-binary @events.ndjson

# nested path
curl -X POST 'http://localhost:7300/api/ingest?index=logs-{{kubernetes.namespace}}' --data-binary @events.ndjson

Dotted paths address nested fields; a missing value becomes unknown. Resolved index names are lowercased and sanitized to [a-z0-9_-] (other characters fold to -) and truncated to 64 characters.

Non-JSON text formats

The /api/ingest/raw endpoint and pull sources can parse text formats directly, selected with format= (or auto-detected):

FormatWhat it parses
ndjsonOne JSON object per line.
jsonA single JSON value: an array of objects, or one object.
textPlain lines — each becomes { "message": line }.
syslogRFC 3164 / RFC 5424 (see Syslog).
logfmtkey=value key="quoted value" pairs (e.g. logrus text formatter).
csv / tsvDelimited columns with a header row (delimiter sniffed).
grokField extraction via a grok preset, %{...} pattern, or named-capture regex.
cefArcSight CEF security-appliance events.
w3cW3C extended logs (#Fields: header) — CloudFront / IIS.
autoSniffs the content and picks one of the above (falls back to text).

Unparseable input always degrades to a message field rather than being dropped, and parse failures are counted in the response's errors.

Everything else

Any other key lands in the dynamic columns under its original name and is immediately queryable as key:value — no declaration, no migration. See Fields & discovery to explore what's present in your data.