Skip to main content

ADR-0045: Loki for Log Aggregation

Status

Accepted - 2025-01-26


Context

Application logs need centralized aggregation for debugging, compliance, and correlation with traces/metrics.


Decision

Grafana Loki for log aggregation (part of Grafana Cloud stack).

Rationale

  1. Integrated: Part of Grafana Cloud (unified with Mimir + Tempo)
  2. Prometheus-Like: LogQL query language similar to PromQL
  3. Efficient: Indexes labels only (not full-text)
  4. Free Tier: 50GB/month

Implementation

See ADR-0044 for Grafana Cloud setup

Winston Loki Transport

// src/logging/logger.ts
import winston from 'winston';
import LokiTransport from 'winston-loki';

const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new LokiTransport({
host: process.env.GRAFANA_LOKI_URL,
basicAuth: `${process.env.GRAFANA_LOKI_USERNAME}:${process.env.GRAFANA_LOKI_API_KEY}`,
labels: {
service: 'tvl-api',
environment: process.env.NODE_ENV,
},
json: true,
batching: true,
interval: 5, // Batch every 5s
}),
],
});

export default logger;

Log with Labels

logger.info({
message: 'Booking created',
org_id: 'org-123',
booking_id: 'book-456',
channel: 'hostaway',
trace_id: span.spanContext().traceId,
});

LogQL Queries

# All errors
{service="tvl-api", level="error"}

# Errors for specific org
{service="tvl-api", org_id="org-123", level="error"}

# Slow requests
{service="tvl-api"} | json | duration > 1000

# Booking creation failures
{service="tvl-api"} | json | message="booking.created" | level="error"

Free Tier

  • 50GB logs/month - Sufficient for MVP.0-MVP.2 (~5M log lines)
  • 13-month retention
  • Upgrade at V1.0: $50/month for 100GB

References