How can an AI SRE see who was affected by the errors in OpenTelemetry log records using Postgres tables?
Tares puts Postgres rows on the same timeline as your OpenTelemetry log records when both sources carry the same primary label value, such as the service name. An AI SRE then reads the error lines and the table rows around them together. The Postgres rows show the state of your data around the errors.
How it works
Tares joins sources by label value. Each source marks one label as primary, and events whose primary label has the same value share one timeline.
- The OpenTelemetry source receives OTLP logs, traces and metrics. Resource attributes such as
service.namebecome labels, so theservicelabel is the natural key. - The Postgres source polls a table incrementally and stores one event per new or changed row. A cursor tracks a column that only grows, such as
idorupdated_at. - To join the two, give the Postgres source a primary label named
service, read from a table column that holds the same service name the log records carry. Rows and log lines with the sameservicevalue then appear on the same timeline. - Other columns, such as
tenant_idorstatus, stay plain labels. They are stored on each row event, so they sit next to the error lines on that timeline.
The AI SRE guide describes how an agent works from these timelines. If your data lives behind a JSON endpoint instead of a table, the HTTP API (poll) connector stores one event per item the same way, and it joins by the same label rule.
Set it up
-
Start Tares locally.
tares up -
Create an OpenTelemetry source with
serviceas the primary label. In the UI, go to Sources, Add source, otlp. There is no required config.- name: otel connector: otlp config: labels: - { name: service, field: service, primary: true } -
Point your exporter at the daemon. Use the gRPC endpoint if you installed the
otlp-grpcextra.OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8787 # OTLP/HTTP → /v1/{logs,traces,metrics} # or gRPC (otlp-grpc extra): OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317On a secured instance, the exporter sends a Bearer Authorization header holding an ingest-scoped API key. Tares needs it only when auth is on.
-
Create the Postgres source. In the UI, go to Sources, Add source, Postgres, enter the connection URL and press Discover. Add the column that holds the service name to
columns, and mark a label namedserviceas primary. This example assumes yourorderstable has aservicecolumn. Keeptenant_idas a plain label.- 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 } -
Verify each source. On the Postgres source, Recent events shows rows on the next poll, and Fields reports each column’s coverage, so you can confirm the
servicecolumn is reliably present. On the OpenTelemetry source, Recent events shows log records as they arrive, and Explore lists services under theservicekey.
The dsn is stored as a secret, redacted in the API and left out of catalog exports. The cursor column must only ever grow. Pick updated_at if you want updated rows as well as inserts.
What you get
- Log records and table rows on one timeline for each service, so an agent reads the errors and the data state around them in one place.
- Row events that carry the columns you pulled. With
statusandtenant_idas labels, they sit beside the error lines. - Incremental reads. Only new or changed rows become events, so the timeline does not fill with repeats of unchanged data.
- One OpenTelemetry source for every service, with no per-service setup. Resource attributes become labels.
The join depends on the label value. Rows appear on the same timeline as the log records only when the service value in the table matches the one the logs carry. If the values differ in spelling, the two sources stay on separate timelines.
When something else fits
- If the data you want next to the errors comes from an API and not a table, use HTTP API (poll), which polls a JSON endpoint and stores one event per item.
- If you only need the error lines and their traces, the OpenTelemetry source alone is enough, and you can skip the Postgres source.