Eventum Logo

Eventum

Logs vs metrics vs events

How logs and metrics differ, why each is an event in a different shape, and how to generate a log line, a metric reading, and a structured event with Eventum.

A ticket asks for the metrics for the checkout service, and that request alone could mean a log line written each time an order fails, a number sampled every few seconds, or a structured record carrying a dozen named fields — a different thing to generate, a different destination, a different volume — three problems wearing one word. Log, metric, and event get used as if they name the same kind of thing, but a log store, a time-series database, and an event index each expect a specific shape, and a feed built for one doesn't load cleanly into another.

Choosing what to generate and where to send it starts with knowing which of the three you need, before a single template gets written.

Logs, metrics, and events: three shapes of telemetry

Three terms that get used interchangeably, but name three specific things:

  • A log is a timestamped textual record of a discrete occurrence — a line of text describing something that happened, written to be read in order, top to bottom.
  • A metric is a numeric measurement of a system, sampled or aggregated over time — a single number taken at an instant or rolled up over a window, not a description of an occurrence.
  • An event is a structured record of a discrete occurrence carrying named fields — the same occurrence a log describes, kept as data instead of folded into a sentence.

Each still describes a single occurrence — what differs is the shape it's given and where that shape ends up living.

How they differ

The differences show up concretely in what each one is, where it lives, and how it gets read back:

SignalShapeTypical exampleWhere it's storedHow it's queried
LogsA timestamped line of text2026-07-11T17:44:01+00:00 ERROR order 39402 failed: payment declinedA log store — a file, or a log-oriented search backendFull-text or field search, filtered by a time range
MetricsA timestamped number, often pre-aggregatedcpu.usage{host="web-01"} 0.72A time-series databaseRange queries and aggregation over a window — average, rate, percentile
EventsA timestamped structured record with named fields{"event": "order_failed", "order_id": 39402, "reason": "payment declined"}An event stream or a document indexField-level filters and aggregations against the structured payload

Every row above starts with the same word, timestamped, because log, metric, and event aren't independent categories: a log is an event rendered as a line of text, a metric is an event whose payload is a measurement, and an event is the general shape the other two specialize from — one suited to being read on its own, the other to being measured and compared across thousands of samples.

Where events fit as the common abstraction

Eventum's three-stage pipeline produces events: the input stage decides when one occurs, the event stage decides what it contains, and the output stage decides where it goes. Nothing about that pipeline is specific to a log or a metric — the event stage renders whatever a template defines, and the same mechanism that fills in a JSON object with named fields renders one sentence of plain text or a single labeled number, depending only on what the template writes and which formatter serializes the result on the way out.

A log line and a metric reading built with Eventum are the same event, shaped differently by the template — one as a sentence, one as a labeled number. The template and the formatter make that call; nothing else in the pipeline does.

Generate each with Eventum

The pipeline underneath is identical for all three; only the template and the formatter change. Each fragment below uses the template event plugin and is illustrative rather than a full project — the linked lesson after each one builds the complete generator.

A plain log line

A one-line, human-readable sentence with the variable data folded directly into it — the shape a service typically writes to its own console or log file:

templates/order.jinja
{%- set order_id = module.rand.number.integer(10000, 99999) -%}
{%- set user = module.rand.choice(["alice", "bob", "carol"]) -%}
{%- set duration_ms = module.rand.number.integer(80, 400) -%}
{{ timestamp.isoformat() }} INFO order {{ order_id }} processed for {{ user }} in {{ duration_ms }}ms

Rendered through the file output's default plain formatter, a line reads:

2026-07-11T17:44:01+00:00 INFO order 39402 processed for bob in 205ms

order_id, user, and duration_ms above are flat, uniform picks — fine for this illustration, but not for a value meant to resemble production traffic. Generate realistic fake data covers drawing a duration like this one from a skewed distribution instead of a flat range.

A metric reading over time

A single numeric measurement, sampled on a fixed interval, carrying none of a log's descriptive text — only what's being measured, where, and the value itself. Eventum has no dedicated metrics format — a metric-shaped event is a regular JSON object built around a name and a value, validated the same way as any other event, through the json formatter:

templates/cpu-usage.jinja
{%- set previous = locals.get("value", 0.35) -%}
{%- set step = module.rand.number.floating(-0.05, 0.05) -%}
{%- set value = module.rand.number.clamp(previous + step, 0.0, 1.0) -%}
{%- do locals.set("value", value) -%}
{"metric": "cpu.usage", "host": "web-01", "value": {{ "%.2f" | format(value) }}, "timestamp": "{{ timestamp.isoformat() }}"}

Each reading adjusts the previous one by a small step instead of picking a fresh, unrelated number, so consecutive samples drift the way a real metric does:

{"metric": "cpu.usage", "host": "web-01", "value": 0.36, "timestamp": "2026-07-11T17:44:00+00:00"}
{"metric": "cpu.usage", "host": "web-01", "value": 0.33, "timestamp": "2026-07-11T17:44:05+00:00"}

IoT sensor telemetry builds this same drift technique into a full generator sampling on a timer input, across several sensors.

A structured event

A record with several named fields, each describing one part of the same occurrence — the shape an application or an API actually emits, rather than a sentence written for a person to read:

templates/login.jinja
{%- set user = module.rand.choice(["alice", "bob", "carol"]) -%}
{%- set method = module.rand.weighted_choice({"password": 80, "sso": 15, "mfa_backup": 5}) -%}
{%- set success = module.rand.chance(0.95) -%}
{"timestamp": "{{ timestamp.isoformat() }}", "event": "user_login", "user": "{{ user }}", "method": "{{ method }}", "success": {{ success | tojson }}}

Through the json formatter, each render is a validated JSON object:

{"timestamp": "2026-07-11T17:44:01+00:00", "event": "user_login", "user": "bob", "method": "password", "success": true}

Structured logging builds this same technique — named fields, a weighted pick between outcomes — into a full generator, using an order-processing example with a common and a rare case.

FAQ

On this page