Skip to content

feat(monitor): serve the Logs tab's site events from the site's OpenTelemetry - #8426

Merged
tlgimenes merged 2 commits into
mainfrom
feat/monitor-site-events
Nov 10, 2026
Merged

tlgimenes merged 2 commits into
mainfrom
feat/monitor-site-events

Conversation

@tlgimenes

@tlgimenes tlgimenes commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The Monitor's Logs tab (#8332, which was UI only) calls GET /api/:org/monitor/:site/site/events. That route was never added, so the router answers 404 and the tab shows "Couldn't load the site's logs / Not Found" for every Blocks v8 site.

Change

A new route next to /site/data in monitor.ts. It uses the same requireOwnedSite guard (ownership first, then the plan), the same { available: false } answer when the warehouse isn't configured, and returns 502 when a query fails.

monitor-site-events.ts reads otel_logs and otel_traces from the same warehouse, with the same SITE_SCOPE as the aggregated views (the collector's stamp on the site, production only). Every value is bound as a parameter. It returns the shape in apps/web/src/components/monitor/site-events-contract.ts:

  • events: logs and spans, newest first, 100 per page, with a keyset cursor on (ts, id).
  • levels: a log's level comes from its OTel severity number (17+ error, 13+ warn, 9+ info, 1–8 debug, unset → info). A span is error when its status is Error, otherwise ok.
  • filters: kinds, levels, services (comma lists, validated) and q (case-insensitive text in the message, or an exact trace id).
  • counts: the first page also returns histogram (one bucket per window step, under every filter except level), facets.levels, facets.services (under every filter except service) and total. Pages after a cursor skip these counts.

SITE_SCOPE, inWindow, LOGS_TABLE and the new TRACES_TABLE are now exported from monitor-site.ts.

What sites send today, from @decocms/blocks: error logs at the error sample rate, and spans only when the site raises its trace sample rate (0 by default). So most sites will list error logs only.

Verification

  • monitor-site-events.test.ts covers:
    • parsing filters, including unknown, duplicate and oversized input;
    • cursor round-trips and rejecting invalid cursors;
    • tenant scope and parameter binding on all three queries, with a hostile site slug;
    • which filter each count ignores;
    • row → wire event mapping;
    • histogram buckets, including empty ones;
    • paging, nextCursor, totals under a level filter, and no counts after a cursor.
  • bun test apps/api/src/api/routes/monitor*: 39 pass. tsc --noEmit, oxlint, biome and knip are clean.
  • Not verified against the production warehouse. The query assumes the collector's ClickHouse exporter writes otel_traces with the standard schema (Timestamp, TraceId, SpanId, SpanName, Duration, StatusCode, SpanAttributes). If that table is missing, the route answers 502 instead of 404. The spans branch would then need to be gated or dropped, and that should be checked right after deploy.

🤖 Generated with Claude Code


Summary by cubic

Implements the route behind the Monitor's Logs tab, which until now returned 404 and showed "Couldn't load the site's logs" for every Blocks v8 site.

Adds GET /api/:org/monitor/:site/site/events, serving the site's own logs and spans from the OpenTelemetry warehouse (otel_logs and otel_traces) with the same tenant scope and ownership guard as /site/data. It returns pages of 100 newest-first events with a keyset cursor; the first page also carries the histogram and level and service counts. New filters: kinds, levels, services, and q (message text or exact trace id). Most sites send only error logs today, since trace sampling defaults to 0, so listings will usually be error logs only.

Not verified against the production warehouse — the spans branch assumes the collector writes otel_traces with the standard schema, and answers 502 if that table is missing. Worth checking right after deploy.

Written for commit 41909c6. Summary will update on new commits.

View guided diff Turn on auto-fix

@tlgimenes
tlgimenes enabled auto-merge (squash) November 10, 2026 15:51
@github-actions github-actions Bot added the claude PR authored by a coding agent label Nov 10, 2026
tlgimenes and others added 2 commits November 10, 2026 16:57
…elemetry

The Monitor's Logs tab (#8332, UI only) reads
GET /api/:org/monitor/:site/site/events, which had no route: the router
answered 404 and the tab showed "Couldn't load the site's logs".

The route reads otel_logs and otel_traces from the same warehouse, with
the same tenant scope and guard as /site/data: one page of logs and spans,
newest first, with a keyset cursor, and on the first page the histogram,
the level counts and the service counts the contract asks for. Filters:
kinds, levels, services, and q (text in the message, or a trace id).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tlgimenes
tlgimenes force-pushed the feat/monitor-site-events branch from c3ea2d6 to 41909c6 Compare November 10, 2026 19:58
@tlgimenes
tlgimenes merged commit eca2952 into main Nov 10, 2026
24 checks passed
@tlgimenes
tlgimenes deleted the feat/monitor-site-events branch November 10, 2026 20:06
decocms Bot pushed a commit that referenced this pull request Nov 10, 2026
PR: #8426 feat(monitor): serve the Logs tab's site events from the site's OpenTelemetry
Bump type: minor

- decocms (apps/api/package.json): 4.568.2 -> 4.569.0

Deploy-Scope: server
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude PR authored by a coding agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant