Request Logs model: Cloud Logging under a deadline-backed read
Every API call your workspace makes lands in Request Logs within seconds, and any window inside the retained year is queryable. This page covers the model behind that surface — what backs the data, why it is not a database table, how paging works over it, and why a degraded log query answers fast and empty instead of hanging. The step-by-step walkthrough lives in the Request Logs console guide; parameter-by-parameter shapes are in the API reference.One model behind three surfaces
Request Logs appears in three places, all reading the same underlying data:- The Request Logs viewer — Developer → Request Logs in the dashboard, an interactive table with filters, summary cards, and a live tail.
- The
GET /api/v1/stats/request-logsendpoint — the same rows over the public API, for scripted exports and integration-debugging workflows. - The dashboard’s
/developer/request-logsroute — the console page itself, which is a client of the same endpoint you call directly.
Backing store: GCP Cloud Logging, not Postgres
Sibling stats surfaces — the activity feed, usage aggregates, insights roll-ups — read from Postgres. Request Logs does not. Each completed API request is stamped into GCP Cloud Logging as an HTTP access entry carrying the timestamp, method, path, status, latency, caller IP, user agent, request id, response size, trace id, and your tenant id. The endpoint queries that corpus through Cloud Logging’s entry API, newest first, scoped to a filter anchored on your tenant id — that anchor is the tenant boundary, and it is met before any optional filter is applied. Practical consequences of that choice:- Retention is roughly one year. Any window inside that horizon returns in full — this is why the API accepts trailing windows and custom
start_date/end_dateranges up to 365 days. - Only HTTP access entries are returned. Your workspace also emits background service log lines stamped with the same tenant id; the query filters those out so the table shows real requests, not platform noise.
- Postgres load is zero. Inspecting a year of HTTP traffic never competes with the send path for database capacity, which is the reason this surface can offer a live tail at all.
Cursor pagination and filters
Responses page withnext_cursor:
next_cursorpresent — more rows exist; pass it back ascursorto continue. A page can come back short of yourlimit, or even empty, with a cursor still attached — that means the window is sparse at that point, not that the data ended. Keep following the cursor until it comes back null.next_cursornull — the window is genuinely exhausted; paging further returns nothing.
limit + since_hours window): those ask the server for a recent bounded slice, while Request Logs is a resumable scan over the log corpus meant for drilling into wide, sparse, older windows.
The filter set:
Windows are bounded with
days (trailing, capped at 365) or an explicit start_date / end_date pair, which takes precedence. With no window given, the endpoint serves the last 24 hours.
The 10-second query deadline
A Cloud Logging query can be slow — a sparse historical window sits behind many empty-but-tokened slices of the corpus — and a query can also stall outright deep in the client’s retry layers. Neither may block a developer-facing page. The endpoint races the whole log query against a 10-second deadline, deliberately tighter than the 20-second backstop the database-backed stats routes use: network edges and uptime probes abandon a request at roughly 15 seconds, so the deadline must fire below that to answer before the probe gives up. A healthy query fills a page in well under a second, so the deadline only trips on the pathological path. When it fires, the endpoint returns a fast empty page; the abandoned query is left to settle harmlessly in the background so a late rejection can never surface. Two internal bounds keep a single call from turning into an RPC storm: the walk stops after 8 seconds of page-following — returning the rows already gathered, with a resumable cursor — and follows at most a bounded number of continuation tokens per call.Why a degraded query fails empty instead of throwing
Request Logs is an always-on observability surface, not a transactional read. If Postgres-backed stats fail, they fail; a log-sink blip should not take the debugging page down with it at the exact moment you need it. So on any Cloud Logging error or on the deadline above, the endpoint returns 200 with an empty page rather than a 5xx. When a page you expect to be populated comes back empty:- Empty with a
next_cursor— the window is sparse there, not empty. Keep paging; your rows exist further along. - Empty with no cursor, over a window you know had traffic — either the query exceeded its deadline or a transient log-sink condition (read quota, temporary unavailability) degraded the read. Retry; healthy queries return in well under a second.
- Repeatedly empty — the window predates the ~1-year retention horizon, or a
status/path/api_keyfilter narrowed the result to nothing. Widen the window or clear filters.
Related reading
- Request Logs console guide — walk through the dashboard page: filters, summary cards, live tail, payload drill-down.
- Request Logs endpoint reference — every query parameter and example calls.