proxy_logs columns

proxy_logs is the queryable log — one row per request, 51 data columns plus the day partition, read with read-only SQL over POST /tenants/{tenant}/query. This page is the column list, and the handful of conventions that decide whether your query is correct or merely plausible.

New to the three logs and not sure this is the one you want? Start with the three logs.

Read this first

Five conventions account for almost every wrong answer this table produces. None of them are guessable from a column name.

Confirming the list yourself

The table below is generated from the same definition the query service serves, but a tenant’s table picks up new columns on its own schedule. To see exactly what yours holds, ask it for a row and read the columns array off the response:

curl -X POST \
  -H "Authorization: Bearer $AIRBRX_PAT" \
  -H "Content-Type: application/json" \
  -d '{"sql":"SELECT * FROM proxy_logs LIMIT 1"}' \
  "https://api.airbrx.ai/tenants/your-slug/query"

DESCRIBE proxy_logs does not work — the executor accepts SELECT and WITH only, and rejects everything else with a 400.

The columns

Partition

ColumnTypeWhat it holds
dayVARCHARPartition column: UTC date as an ISO string 'YYYY-MM-DD'. Filter on this for time ranges (e.g. day >= '2026-06-01') so DuckDB prunes partitions; compare as a string, not a DATE.

Request

ColumnTypeWhat it holds
timestampBIGINTRequest time as epoch milliseconds. Use to_timestamp(timestamp / 1000) for a readable time. For date-range filters prefer the partition column day.
userNameVARCHARDisplay name of the data consumer who issued the request.
statusCodeINTEGERHTTP-style status code returned to the consumer.
responseSizeBIGINTSize of the response body returned to the consumer. Never null: 0 when the field was absent, -1 when the Gateway reported it unmeasured. Use responseSize > 0 to exclude non-measurements. Unit: bytes.
requestSizeBIGINTMeasured size of the request body. Null when unmeasured. Unit: bytes.
responseTimeMsBIGINTEnd-to-end time to serve the request. This is the field to use for "slow query" questions. Never null: 0 when nothing could be measured. Unit: ms.
warehouseExecutionTimeMsINTEGERTime the warehouse itself spent on the request. This is what a HIT avoided; responseTimeMs is wall-clock through the proxy and cannot answer that. Null when unmeasured, including requests the warehouse never saw. Unit: ms.

Cache decision

ColumnTypeWhat it holds
cacheStatusVARCHAROutcome of the proxy cache lookup. Only HIT, MISS, BYPASS and PASSTHROUGH count toward hit rate; the rest are error, hold and telemetry outcomes that still served a request. BLOCKED is a deny-rule refusal. One of: HIT, MISS, BYPASS, PASSTHROUGH, SPOOFED, TELEMETRY, ERROR, INVALIDATION_ERROR, AUTH_ERROR, EXECUTOR, HOLD, BLOCKED.
missReasonVARCHARWhy a MISS missed. Null when the request did not miss, or when the Gateway recorded no reason. One of: no-cache-entry, ttl-expired, invalidation-marker.
cacheKeyVARCHARCache key the proxy computed for the request. Null for BYPASS and PASSTHROUGH, where no cache entry exists, so cacheKey IS NOT NULL selects the requests that touched the cache.
cacheKeyVersionINTEGERVersion of the cache-key scheme used to compute cacheKey.
matchedRuleVARCHARIdentifier of the cache/routing rule that matched, if any. Stable across edits; matchedRuleName is what a human recognizes.
matchedRuleNameVARCHARHuman-readable name of the rule identified by matchedRule.
ruleVersionVARCHARVersion of the matched rule as it stood when the request was served.
cacheTtlSecondsINTEGERTime-to-live the matched rule applied to the cache entry. Unit: s.
cacheAgeSecondsINTEGERAge of the cache entry when it was served. Unit: s.
cacheExpiresAtBIGINTWhen the cache entry expires, as epoch milliseconds — the same units as timestamp.

The SQL

ColumnTypeWhat it holds
standardizedSqlVARCHARNormalized SQL statement the consumer ran (parameters removed).
queryHashVARCHARStable hash identifying the statement. Cached paths carry the Gateway hash, which covers the statement and its bound parameters; BYPASS and PASSTHROUGH rows are re-hashed from standardizedSql alone, so their variants collapse to one template hash.
parameterValuesVARCHARJSON-encoded bound parameter values for the statement, held as text — open it with json_extract.
statementTypeVARCHARStatement kind the parser identified, such as SELECT or INSERT.
isReadOnlyBOOLEANTrue when the statement only reads.
isDataChangeBOOLEANTrue when the statement modifies data.
isDDLBOOLEANTrue when the statement changes schema rather than data.
cacheOverrideVARCHARCaching forced by a directive in a SQL comment on the statement. Null when the statement carried no directive. One of: cache, nocache.
hasParametersBOOLEANTrue when the statement was parameterized.
hasNonDeterministicFunctionsBOOLEANTrue when the statement calls a function whose result can differ between runs, which is why an otherwise repeatable statement may not be cacheable.
tableCountINTEGERNumber of tables the statement references.
tablesVARCHARJSON-encoded array of the tables the statement references, held as text — open it with json_extract. Null when it references none.

Correlation

ColumnTypeWhat it holds
logIdVARCHARIdentifier of the raw log record this row was derived from.
queryIdVARCHARThe warehouse own query ID. Join key to warehouse query history, so a HIT can be tied to the cost it avoided.
sessionIdVARCHARConsumer session the request belonged to.
operationIdVARCHARIdentifier of the protocol operation the request carried.
operationTypeVARCHARKind of protocol operation the request carried.
adapterTypeVARCHARWarehouse adapter that handled the request. One of: databricks, snowflake, postgresql.
serverHostnameVARCHARServer hostname the Gateway recorded for the request.

Caller identity and request headers

These are here so a security review is a query rather than a grep over raw JSON. Every value except clientIp is supplied by the caller and copied down verbatim — inspect it, never trust it. clientIp is the address the Gateway resolved through its trusted-proxy rules, which is why it, and not xForwardedFor, is the one that agrees with the audit log.

ColumnTypeWhat it holds
clientIpVARCHARCaller address as the Gateway resolved it through its trusted-proxy rules, so this agrees with the audit log rather than forming a separate opinion about which hop was the client.
userTokenHashVARCHARHash of the caller token. Identifies a caller across requests without carrying the token itself.
userAgentVARCHARUser-Agent header, typically identifying the driver and its version.
hostVARCHARHost header.
originVARCHAROrigin header.
refererVARCHARReferer header.
connectionVARCHARConnection header.
contentTypeVARCHARContent-Type header.
contentLengthVARCHARContent-Length header verbatim, as text — see requestSize for measured bytes.
contentEncodingVARCHARContent-Encoding header.
acceptEncodingVARCHARAccept-Encoding header.
xForwardedForVARCHARX-Forwarded-For header.
xForwardedProtoVARCHARX-Forwarded-Proto header.
xForwardedHostVARCHARX-Forwarded-Host header.
xRealIpVARCHARX-Real-IP header.

Worked queries

Each of these carries a day predicate. Copy that habit before you copy anything else.

What is the cache actually doing?

SELECT cacheStatus, count(*) AS executions
FROM proxy_logs
WHERE day >= '2026-06-01'
GROUP BY ALL
ORDER BY executions DESC

Expect more than four rows. PASSTHROUGH is the interesting one — those statements matched no rule at all, so they are the population your next cache rule would be drawn from. BLOCKED is a deny rule refusing a statement, not an error.

Which uncached statements repeat the most?

SELECT queryHash,
       any_value(standardizedSql)    AS statement,
       count(*)                      AS executions,
       count(DISTINCT userName)      AS users,
       sum(warehouseExecutionTimeMs) AS warehouse_ms
FROM proxy_logs
WHERE day >= '2026-06-01'
  AND cacheStatus = 'PASSTHROUGH'
GROUP BY queryHash
HAVING count(*) > 1
ORDER BY warehouse_ms DESC
LIMIT 25

The ranked list of work your warehouse did more than once and did not need to. Sorting by warehouseExecutionTimeMs rather than execution count puts the expensive repeats first, which is the order you want to write rules in — and it is the right column for the job, because responseTimeMs is wall-clock through the Gateway and cannot tell you what the warehouse itself spent.

Is a rule earning its keep?

SELECT matchedRuleName,
       ruleVersion,
       count(*)                                    AS executions,
       count(*) FILTER (WHERE cacheStatus = 'HIT') AS hits,
       round(100.0 * count(*) FILTER (WHERE cacheStatus = 'HIT') / count(*), 1) AS hit_rate_pct
FROM proxy_logs
WHERE day >= '2026-06-01'
  AND matchedRule IS NOT NULL
GROUP BY ALL
ORDER BY executions DESC

High executions with a low hit rate usually means a cache key that is too specific — every execution computes a different key, so nothing is ever read back. missReason will tell you which kind of miss it is.

Everything one caller did

SELECT to_timestamp(timestamp / 1000) AS at,
       userName, userAgent, cacheStatus, statusCode, standardizedSql
FROM proxy_logs
WHERE day >= '2026-06-01'
  AND clientIp = '203.0.113.24'
ORDER BY timestamp

Security review is a first-class use of this table, which is why the caller columns are here: clientIp, userTokenHash, userAgent and the forwarded header set. They are already in the raw logs this table derives from, under the same tenant prefix and the same retention — a column adds query reach, not exposure.

What did the cache save us?

SELECT day,
       count(*) FILTER (WHERE cacheStatus = 'HIT') AS hits,
       sum(warehouseExecutionTimeMs) FILTER (WHERE cacheStatus <> 'HIT') AS warehouse_ms
FROM proxy_logs
WHERE day >= '2026-06-01'
GROUP BY day
ORDER BY day

For tying a HIT to the cost it avoided, queryId is the join key into your warehouse’s own query history.

What is not here

See also