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
- Integrated: Part of Grafana Cloud (unified with Mimir + Tempo)
- Prometheus-Like: LogQL query language similar to PromQL
- Efficient: Indexes labels only (not full-text)
- 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