Portal Community

Loki's Core Design Philosophy

Loki was designed around a key observation: for modern structured logs, you almost always know which service, environment, and tenant you're looking for before you start searching. Indexing that metadata (labels) is sufficient for fast filtering. The log content itself can remain unindexed and compressed — dramatically reducing storage cost compared to Elasticsearch.

How Loki Differs from Elasticsearch

FeatureLokiElasticsearch
Index log contentNo — grep-style filtering after label matchYes — inverted index on all fields
Storage costLow — compression only, no content indexHigh — index is 1–3x raw data
Ingest overheadVery low — write log lines to streamHigh — tokenize and index every word
Query for "all errors in service X"Fast — stream selector + level labelFast — index lookup
Query for "find all logs containing IP 1.2.3.4"Slower — full scan of matched streamsFast — inverted index
Best forStructured logs with known label dimensionsArbitrary unstructured text search

Loki's Data Model

Loki organizes logs hierarchically:

BizFirstGO Stream Labels

BizFirstGO services use the following standard labels when pushing logs to Loki:

# Example stream selector for production errors in ProcessEngine
{
  "job": "processengine",
  "service": "flow-studio-api",
  "tenant_id": "tenant-abc-123",
  "environment": "production",
  "level": "error"
}

# All log lines with this exact label set form one stream.
# LogQL stream selector syntax:
{job="processengine", environment="production", level="error"}

High-Cardinality Values — Keep Them Out of Labels

The following values are not used as labels — they go into the log line body:

ValueWhy Not a LabelHow to Filter
execution_idUnique per execution — millions of unique values|= "execution_id=exec-abc"
node_keyVaries per workflow design|= "node_key=approval-01"
trace_idUnique per request — very high cardinality|= "traceId=4bf92f..."
user_idMillions of unique user IDs possible|= "userId=user-xyz" (avoid for privacy)
High-Cardinality Labels Cause Serious Performance Issues

Each unique label combination creates a new stream. If you use execution_id as a label, Loki creates a new stream for every workflow execution — potentially millions of streams. This overwhelms Loki's index and causes memory exhaustion and slow queries. Keep label count low and label cardinality even lower.

LogQL in 60 Seconds

LogQL has two parts: the stream selector (mandatory, uses {}) and filter/parse expressions (optional pipeline):

# Minimum valid LogQL — all logs from processengine in production
{job="processengine", environment="production"}

# Add a filter — find lines containing "error"
{job="processengine"} |= "error"

# Parse JSON and filter a field
{job="processengine"} | json | level="error"

# Count error rate per minute (metric query, used in alerts + panels)
rate({job="processengine"} | json | level="error" [1m])

Grafana Integration

In Grafana, Loki logs appear in: