Skip to main content
A four-panel Grafana dashboard over the logs an OTLP drain delivers, through the collector config, into Loki. Before you begin
  • An otlp drain in state healthy, delivering into a collector.
  • A Loki datasource in Grafana, receiving from that collector.
  • Grafana 10 or later.
The queries assume Loki 3.x with OTLP ingestion, where service.name becomes the service_name stream label and the remaining resource and record attributes become structured metadata with every non-alphanumeric character replaced by _ — so severity_text, gen_ai_tool_name, lua_drain_backlog. If your pipeline promotes a different set of attributes to labels, change the stream selector in each panel; the filter stages after it stay as they are.

What you get

An $agent template variable over service_name scopes all four panels to one agent or to all of them.

Import it

1

Save the dashboard

Save the JSON below as lua-grafana-dashboard.json.
2

Import

In Grafana, Dashboards → New → Import, upload the file, pick your Loki datasource for the DS_LOKI input, and Import.The dashboard’s UID is lua-log-drain, so re-importing updates it in place rather than creating a second copy.
3

Check the heartbeat panel

It should read 3 within fifteen minutes of a healthy drain. DOWN in red means either no heartbeat reached Loki or the attribute name differs in your pipeline — run the panel’s query in Explore and confirm lua_drain_backlog is the structured-metadata name your Loki produced.

The dashboard

lua-grafana-dashboard.json

Why these selectors

Each panel keys on a field that is unique to what it measures, which avoids depending on the OTLP eventName field reaching Loki as a queryable label:
  • severity_text distinguishes the four severities directly.
  • gen_ai_tool_name is present only on tool calls, so severity_text = "ERROR" plus that attribute is exactly “a tool call failed”, across skills and MCP servers.
  • lua_drain_backlog is present only on the heartbeat record. Counting records that carry it is counting heartbeats.
The same logic gives you the other two synthetic records if you want panels for them: lua_drain_dropped_count appears only on a drop record, and lua_truncated_lines only on a truncation record.
The Datadog pack selects on @lua.eventName instead, because the Datadog encoder ships the whole record as a nested object and the event name is directly addressable there. The two packs watch the same four signals from opposite ends of the schema.

Alert on it

Grafana alert rules over the same queries, if Grafana rather than the backend owns your alerting:
Drain heartbeat missing — alert when IS BELOW 1 for 5m
Errors spiking — alert when IS ABOVE 5 for 5m
Records dropped — alert when IS ABOVE 0
Set the heartbeat rule’s No data handling to Alerting: with no heartbeats there is no series at all, and the default of No Data would leave it silent exactly when it matters.

Next steps

OpenTelemetry Collector config

The pipeline that feeds this dashboard.

OpenTelemetry

Create the drain.

Event schema

Where these attribute names come from.

Datadog monitor pack

The same signals, in Datadog.