11

Loki: Logs by Stream, Not Full-Text Index

Source: Observability Platform source — section 11, “Logs: Loki as the Log Backend”

The enterprise problem and today’s slice

Enterprise problem: Engineers need detailed event evidence during incidents, but indexing every word and every unique identifier can make log cost and query latency grow faster than the systems being observed.

Whole-course context: The incoming evidence proves metrics can move from local Prometheus through remote write to shared storage and alert routing; logs now add the forensic record behind a metric symptom.

Today’s slice: Design Loki streams, structured metadata, object-storage flow, and LogQL queries while keeping source-owned business data and high-cardinality request identifiers out of the stream-label index.

End-of-day evidence: Produce a reviewed label contract plus one bounded LogQL investigation that finds a known checkout failure without creating an unbounded stream dimension.

Still unsolved: Trace causality, code-level profiles, Grafana navigation, and the complete cross-signal architecture remain deferred.

Customer use cases

If the label contract mirrors every JSON field, customers pay for an explosive stream index and still receive slow investigations. These use cases separate repeatable service filtering from request-specific forensic search.

Use case IDActorCustomer jobSuccess outcomeDenial or recovery evidence
D11-UC-01Incident responderFind payment timeout logs for checkout in production during a bounded windowA stream selector plus filters returns the known event and preserves its trace/request metadataAn over-broad or unauthorized tenant query is rejected or bounded, while a precise control query completes
D11-UC-02Observability platform operatorKeep log streams queryable without cardinality growth from unique identifiersLabel-analysis evidence shows bounded streams and high-cardinality fields stored as structured metadataA policy test rejects a forbidden label and confirms the same field remains searchable as metadata

Actor-centred user stories

“Centralize logs” is not testable until query scope and index policy are observable. These stories require both a useful investigation and a rejected unsafe schema.

Story IDUse case IDsUser storyObservable acceptance conditions
D11-US-01D11-UC-01As an incident responder, I want to narrow checkout logs by service and environment before filtering details, so that I can find the failure without scanning unrelated tenantsA fixed-window LogQL query returns the seeded timeout with trace ID and excludes staging data
D11-US-02D11-UC-02As a platform operator, I want request IDs and pod-instance IDs outside the stream-label index, so that stream count and query cost stay boundedIngestion policy rejects or demotes the forbidden label, stream cardinality stays within budget, and metadata search still finds the event

End-to-end product flows

A log line is useful only if it remains attributable, retained, and queryable without violating tenant or cardinality policy. The flow begins with an application event and ends with a bounded result or explicit policy denial.

Flow IDUse case IDsPathTriggerNumbered stepsTerminal evidence
D11-FLOW-01D11-UC-01, D11-UC-02HappyResponder opens logs for checkout production and a fixed incident window1. Application emits structured stdout.<br>2. Alloy or an OpenTelemetry Collector parses and enriches it.<br>3. Policy promotes bounded resource attributes to labels and keeps unique IDs as metadata.<br>4. Loki ingests the stream and persists index/chunks.<br>5. LogQL selects streams, filters lines, and parses the reduced set.Tenant, selector, time bounds, matching line, trace/request metadata, environment, timestamp, and query ID
D11-FLOW-02D11-UC-01, D11-UC-02DeniedOperator submits a pipeline revision that promotes trace_id to an indexed label1. Schema policy detects an unbounded field.<br>2. Deployment is denied or the field is demoted to structured metadata.<br>3. Existing streams remain queryable.<br>4. A metadata query finds the seeded trace ID.Denial reason, rejected revision, unchanged stream count, successful metadata lookup, and policy-run ID

System design derived from the flows

Calling Loki a full-text search database leads teams to index the wrong data and overload ingestion. The system instead indexes stream label sets and stores compressed log content for parallel filtering at query time.

Use case IDEntry pointResponsible servicesAuthoritative storeFailure evidence
D11-UC-01Grafana Explore or Loki query APIQuery frontend, query scheduler, queriers, index gateway/store, object-store readersLoki index and compressed chunks in object storageEmpty selector, query limit, chunk read error, parser error, tenant denial, or timed-out query
D11-UC-02Collector pipeline and Loki push endpointAlloy/OTel processors, Loki distributor, stream policy, ingesters, compactorVersioned pipeline policy plus Loki index/chunksRejected label, active-stream limit, distributor rejection, ingester error, or policy-test failure

Data model and ownership

When a stream label becomes an accidental identity database, deletion, authorization, and cost controls become unreliable. Loki owns telemetry copies and indexes; the source application remains authoritative for orders, users, and payments.

Generated-application database: Not created in this slice — Loki stores operational log evidence, not generated-application business records or user authorization state.

Record or entityStore and ownerPrimary keyForeign key or opaque referenceTenant keyMaterial constraintLifecycle and deletionUse case IDs
Label-policy revisionGit/configuration store owned by platform operationspolicy_revision_shaNone — root policy revision has no parentorganization_idOnly approved bounded attributes become index labels; secrets are redactedReview, deploy, roll back, retain history, then expire by audit policyD11-UC-02
Log streamLoki index owned by logging platformtenant_id/label_fingerprintReferences policy revision and chunk settenant_idExact label set identifies one stream; tenant isolation and active-stream limits applyCreate on first entry, age out index records after retention, delete with tenant dataD11-UC-01, D11-UC-02
Log chunkObject store owned by Lokitenant_id/chunk_idForeign reference to stream fingerprinttenant_idCompressed immutable time range; retention and legal-hold policy applyFlush, compact where supported, retain, tombstone, and deleteD11-UC-01
Query evidencePlatform evidence store owned by operationslog_query_run_idOpaque reference to policy revision, selector, and result digestorganization_idTime-bounded query and denial result are immutableCreate per test, retain for audit objective, then expireD11-UC-01, D11-UC-02

Streams, labels, and structured metadata

High-cardinality labels create many tiny streams and damage cost and performance, so labels should describe stable sources rather than unique events. A Loki stream is every log entry sharing one exact label set.

Indexed labels: service_name, deployment_environment, cluster, namespace
Structured metadata: trace_id, request_id, user_id, deployment identifier
Log body: message, exception, business-safe details

The current Loki documentation explicitly recommends structured metadata for frequently searched high-cardinality data and advises new users to demote k8s.pod.name and service.instance.id from default index labels. A field may still be sensitive even when it is not indexed; redact credentials, tokens, and unnecessary personal data before export.

LogQL investigation method

Parsing every record before narrowing streams wastes compute and can time out during an incident. Start with tenant, time, and precise labels; then apply cheap line filters before parsers and field comparisons.

{service_name="checkout", deployment_environment="production"}
  |= "payment authorization failed"
  | json
  | duration_ms > 1000

For a trace identifier stored as structured metadata, use the syntax supported by your Loki/collector version or filter the reduced log body. Validate the query plan and bytes scanned; a correct result with an unbounded selector is still an unsafe operational query.

Logs can also produce metrics:

sum by (service_name) (
  rate({deployment_environment="production"} |= "payment authorization failed" [5m])
)

Instrument recurring aggregate questions as native metrics when practical; reserve repeated log scans for irregular forensic questions.

Deployment and practical checks

A laptop monolith proves syntax but not distributed availability, so deployment evidence must match the promised scale. Current Loki supports monolithic and distributed modes; Simple Scalable is deprecated and documented for removal in Loki 4.0, so new production designs should not treat it as the durable destination.

Run these checks in a test tenant:

  1. Seed two production timeout records and one staging control with a unique trace_id in structured metadata.
  2. Query a 15-minute production window and prove the two records match while staging does not.
  3. Submit a policy that promotes trace_id; verify policy denial or controlled demotion.
  4. Compare stream cardinality before and after the rejected revision.
  5. Delete the test tenant or wait through its retention policy and prove index/chunk expiration.

Key takeaways

Loki’s value depends on disciplined indexing rather than collecting every possible field. Preserve these rules:

  • Loki indexes label sets for streams, not every word in each log line.
  • Stable, bounded source attributes belong in labels; unique identifiers usually belong in structured metadata.
  • Query from narrow time and labels toward expensive parsing.
  • Object storage owns retained chunks, while the source system still owns business truth.
  • Monolithic is suitable for getting started; distributed mode is the production-scale topology to evaluate.

Checklist

Log search can appear functional while the schema is already unsustainable. Complete these checks before onboarding a service:

  • [ ] Published an approved label, metadata, body, and redaction contract.
  • [ ] Measured active streams and cardinality by tenant and service.
  • [ ] Proved a bounded LogQL query finds a seeded failure and excludes a control.
  • [ ] Rejected or demoted one high-cardinality label.
  • [ ] Verified tenant denial and retention/deletion behaviour.
  • [ ] Selected a current supported deployment mode from measured scale.

Sources

Loki label defaults and deployment guidance change, so use current primary documentation for implementation. These sources support this lesson: