The three logs

Every statement that crosses the Gateway is written down three times: once as a raw log, once into a summary, and once as a row in a queryable columnar view. Same event, three artifacts — and they are not three lenses on one record. They differ in grain, in vocabulary, and above all in what they cost to read. Picking the right one is most of the skill.

Start with the cost

None of the three is gated. All are open to any member of the tenant, which means the only thing steering you to the right one is knowing what each does when you ask it a question.

Summarized

Already computed

Rolled up on a schedule. Reading one is a single fetch of a small JSON object — nothing computes when you ask.

Raw

Cheap per call, costly in calls

One small object per request, fetched one at a time, after walking the tree to find it. A day of traffic is thousands of round trips.

Queryable

Real compute

Starts a query engine and scans columnar files. Seconds, not milliseconds, with a 15-second ceiling. The heavy tool, for the questions that earn it.

Precomputed, then round trips, then compute. If a summary can answer your question, let it. Reach for proxy_logs when the answer genuinely isn’t in a rollup — that is what it is for, and it is very good at it.

Summarized — the answer, already computed

A scheduled run rolls each day up and writes it as JSON: totals, hit and miss counts, response-time statistics, an hour-by-hour breakdown, and one record per distinct statement. Months and years are rolled up the same way, with a per-user cut alongside.

GET /tenants/{tenant}/summaries/{year}/{month}/{day}

The grain is one record per distinct statement per day. Ten thousand executions of the same query are one record with a count of ten thousand. That is what makes it cheap, and it is also the limit: a summary cannot tell you about one execution, only about a day of them.

This is what the dashboard reads, and what a BI tool should read. Full field list in the Analytics API reference.

Raw — the untouched record

The Gateway writes one JSON object per request into your own bucket, under an hourly prefix. This is the source of truth — the other two are derived from it — and the only one carrying every field the Gateway captured, including originalSql with its literal values still inline.

logs/YYYY/MM/DD/HH/<file>.json

The same tree is browsable over the API one level at a time: a year lists its months, a month its days, a day its hours, an hour its files. Which is exactly the cost — finding one request means walking down, and reading a day means fetching every object in it, one by one. Every read of a raw log is itself audit-logged.

Reach for raw when you are exporting into your own pipeline, or when you need a field that exists nowhere else. Don’t reach for it to answer a question — that is what the other two are for. Paths and examples in the query log reference.

Queryable — one row per request, in SQL

The same raw logs are also written into a columnar table, exposed as a single fixed view called proxy_logs and read with read-only SQL:

POST /tenants/{tenant}/query

The grain is one row per request — the same grain as raw, but columnar, so you can ask questions across millions of them without fetching any. It carries far more per request than the summaries do: the cache decision and why it went that way, the shape of the SQL, the warehouse’s own query ID, and the caller’s address, user agent and forwarded headers. A security review is a query here rather than a grep over raw JSON.

It is also the one that costs something to run. Three limits shape every query you write:

The workflow, with worked examples, is Query the raw log; the columns are in the proxy_logs column reference.

How they fit together

The Gateway writes the raw log. A scheduled run then reads those raw objects and writes the other two side by side — the summaries and the columnar table are siblings, derived in parallel from the same source. Neither is built from the other.

Gateway ──► raw logs ──┬──► summaries   (rolled up per day)
                       └──► proxy_logs  (one row per request)

That shape is worth holding onto, because it explains the next section. Two independent derivations of the same event will not agree about everything, and knowing where they part company is the difference between a correct number and a plausible one.

Where they disagree

FieldIn a summaryIn proxy_logs
cacheStatus The status of the most recent execution of that statement that day — a sample, not a rollup. The status of that one request.
Miss arithmetic cacheMisses counts MISS + BYPASS + PASSTHROUGH. Twelve distinct statuses. You decide what counts as a miss.
cacheKey, when there is none Empty string. NULL, so cacheKey IS NOT NULL selects the requests that touched the cache.
parameterValues An array. JSON held as text — open it with json_extract.

The first row catches people out most often. A daily summary’s cacheStatus looks like it summarizes that statement’s day. It doesn’t — it is whichever status was seen last. If you need the distribution, count it in proxy_logs.

Twelve statuses, not four

HIT, MISS, BYPASS and PASSTHROUGH are the four that take part in hit-rate arithmetic, and the four the summaries count. They are not the only four the Gateway emits. proxy_logs also carries SPOOFED, TELEMETRY, ERROR, INVALIDATION_ERROR, AUTH_ERROR, EXECUTOR, HOLD and BLOCKED — that last one being a deny rule refusing a statement.

So a filter like cacheStatus IN ('HIT','MISS','BYPASS','PASSTHROUGH') is not the no-op it looks like. It silently drops every refusal and every error from your result.

The same fact, different names

The two derived logs were built at different times for different readers, and they do not share a vocabulary. When you carry a field name from one page to another, translate it:

The factIn a summaryIn proxy_logs
The SQL, normalizedstatementstandardizedSql
Who ran ituserIduserName
Which rule matchedmatchedRule (the ID)matchedRule (the ID), plus matchedRuleName
When it ranISO timestampstimestamp as epoch milliseconds; day as 'YYYY-MM-DD'

What lives in only one place

Result rows are never recorded anywhere. The contents of a response are not read and not retained, in any of the three. See security posture.

Which one do I want?

The questionThe log
A number for a dashboard, or a trend over months.Summarized
Our hit rate last quarter, by user.Summarized
Which uncached statements repeat the most, and what do they cost?Queryable
Which addresses hit this tenant last week, and what did they run?Queryable
Why did this particular execution miss?Queryable, or the App
Everything, into our own pipeline or SIEM.Raw
The literal values in a statement.Raw

Where to go next

Ask the log something

Querying proxy_logs starts with a least-privilege token.

Create a personal access token