Loki doesn’t actually store metrics about log volume; it exposes metrics that other systems can use to track log volume.
Let’s see this in action. Imagine you have Loki running, and you’re sending logs from a few different services. You can hit Loki’s metrics endpoint, typically /metrics, and you’ll see a ton of output. Among that output, you’ll find metrics like loki_log_bytes_total and loki_log_lines_total.
# HELP loki_log_bytes_total Total number of bytes read from logs.
# TYPE loki_log_bytes_total counter
loki_log_bytes_total{app="my-app",namespace="default",pod="my-app-12345"} 1500000
loki_log_bytes_total{app="another-service",namespace="production",pod="another-service-abcde"} 750000
# HELP loki_log_lines_total Total number of lines read from logs.
# TYPE loki_log_lines_total counter
loki_log_lines_total{app="my-app",namespace="default",pod="my-app-12345"} 15000
loki_log_lines_total{app="another-service",namespace="production",pod="another-service-abcde"} 7500
These aren’t just numbers; they’re Prometheus-formatted metrics that Loki itself is generating. The loki_log_bytes_total metric, for example, is a counter that increments every time Loki ingests a chunk of log data. It’s tagged with labels that identify the source of those logs – app, namespace, pod, and any other labels you’ve configured Loki to capture.
The problem Loki solves here is making the volume of data you’re sending to it visible. Before this, you might only know you’re sending logs, but not how much. This is critical for cost management, capacity planning, and identifying runaway log generation. Loki exposes these metrics so you can pipe them into a system like Prometheus, which can then scrape, store, and alert on them.
The core components involved are Loki’s ingester and its metrics exposition. When the ingester processes incoming logs, it keeps an internal tally of the bytes and lines. This tally is then exposed via the /metrics endpoint as Prometheus metrics. You can then query these metrics in Prometheus using PromQL. For instance, to see the total bytes ingested by my-app in the last hour, you’d use something like rate(loki_log_bytes_total{app="my-app"}[1h]) * 3600. The rate function calculates the per-second average, and multiplying by 3600 gives you the total for the hour.
The exact labels available on these metrics are determined by your Loki configuration, specifically the common_labels and extra_labels settings in your loki.yaml file. If you want to track log volume by a specific Kubernetes deployment or a custom application identifier, you need to ensure that label is being passed to Loki and is either a common label or an extra label that Loki captures. Without that label being present on the logs themselves when they arrive at Loki, it won’t be available on the metrics.
What most people don’t realize is that these metrics are per-ingester instance. If you have multiple Loki ingesters running in your cluster, each one will independently report its own loki_log_bytes_total. To get a cluster-wide view, Prometheus will automatically aggregate these based on the metric name and common labels. However, if you’re debugging a specific ingester’s performance or trying to isolate a problem, you might need to look at the metrics from individual ingester pods.
Once you’ve got Prometheus scraping Loki’s metrics, the next logical step is to set up Grafana dashboards to visualize this data and create alerts for unexpected spikes or sustained high volumes.