How do I read Postgres tables next to OpenTelemetry log records when an AI SRE investigates?
Tares puts Postgres rows and OpenTelemetry log records on one timeline when both sources carry a primary label with the same name and the same value, such as service. One read of that value then returns the rows and the log lines together, so an AI SRE sees the application data next to the logs without joining anything itself. You set this up with an otlp source and a postgres source that both mark service as primary.
How it works
Tares stores everything as events with labels. A label named in both sources is what lines them up.
- OpenTelemetry side. The OpenTelemetry connector receives OTLP/HTTP logs, traces and metrics. Resource attributes such as
service.namebecome labels, so theservicelabel is the natural key. One otlp source ingests every service. - Postgres side. The Postgres connector polls a table incrementally and stores one event per new or changed row. A cursor column that only grows, such as
updated_at, tracks progress. Aprimarylabel turns a column into the entity key. - The join. Mark
serviceprimary on the otlp source and mark a label namedserviceprimary on the postgres source. The postgres label reads a table column that holds the same service name the logs carry. When a log line and a row both carrycheckout, they sit under the same entity key on one timeline. - A second key. Keep
tenant_idas a plain, non-primary label on the postgres source when you also want to filter rows by customer. It adds a field to read, and it does not change the join.
The match is on the value. If the logs say checkout and the table column says checkout-api, the two do not meet, so the column must hold the name the logs carry.
This is the same idea the AI SRE guide describes: events from different systems correlate when they share a label value.
Set it up
- Create the otlp source. There is no required config. Add the
servicelabel and mark it primary. In the UI that is Sources, Add source, otlp. You can also ask a connected agent to create an OTLP source keyed by service.
- name: otel
connector: otlp
config:
labels:
- { name: service, field: service, primary: true }- Create the postgres source. Enter the connection URL, run Discover to list the tables, and pick one. List the column that holds the service name in
columns, and give it aservicelabel marked primary. Thedsnis stored as a secret.
- name: orders
connector: postgres
poll: 10s
config:
dsn: postgresql://user:pass@host:5432/appdb
table: orders
cursor_column: updated_at
columns: id,service,tenant_id,status,amount,updated_at # empty = every column
labels:
- { name: service, field: service, primary: true }
- { name: tenant_id, field: tenant_id }
- { name: status, field: status }- Point your exporter at Tares. Set the OTLP endpoint on the app or the OpenTelemetry Collector.
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8787 # OTLP/HTTP → /v1/{logs,traces,metrics}
# or gRPC (otlp-grpc extra): OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317The sender uses a Bearer Authorization header holding an ingest-scoped API key, needed only when Tares has auth on.
-
Check both sides. On the otlp source, Recent events shows log records as they arrive and Explore lists services under the
servicekey. On the postgres source, Recent events shows rows on the next poll and Fields reports each column’s coverage. Use Fields to confirm theservicecolumn is present on nearly every row. -
Check the values match. Compare a service name in the otlp Explore list with the values in the postgres
servicecolumn. They must be identical strings.
What you get
- One read of a service value returns its log lines and its table rows in time order, so the AI SRE sees an error log and the order rows that changed around it together.
- The Postgres rows are your application’s source-of-truth data on the same timeline as the telemetry, not a separate lookup.
- A single otlp source covers every service you instrument, with no per-service setup.
- Rows are read incrementally. Only new or changed rows become events, tracked by the cursor column.
- The extra
tenant_idlabel lets you narrow rows to one customer while theservicelabel keeps the join. - The postgres
dsnstays a secret: it is redacted in the API and omitted from catalog exports.
When something else fits
- If the data you want next to the logs comes from a JSON API rather than a table, the HTTP API (poll) connector stores one event per item and joins the same way, through a shared primary label value.
- If your log lines and rows share no value at all, there is nothing to join on. Add a shared identifier to one side first, such as a service column in the table.
Related
- Postgres table
- How do I read JSON API endpoints next to Grafana Loki logs when an AI SRE investigates?
- How do I read JSON API endpoints next to logs from Docker containers when an AI SRE investigates?
- How do I read Postgres tables next to logs from Vercel projects when an AI SRE investigates?
Still stuck? Get help