Find Brisket Client Logs in Axiom
Find Brisket Client Logs in Axiom
Section titled “Find Brisket Client Logs in Axiom”Brisket browser reports are logs with service brisket and event
source=browser. Use this runbook when investigating frontend errors, checkout
failures, or the tags, extra context, and breadcrumbs previously sent to Sentry.
Create failures
Section titled “Create failures”For Faceted create unavailable reports, inspect
attributes.context.submit.serverReason. Payment-past-due rejections use
payment_past_due; unsupported GFY characters use unsupported_character.
These classifications describe rejected submissions, not failed render jobs.
Explore and Get inspired family-resolution failures include
attributes.context.resolution.requestId and elapsedMs. Search server spans
for faceted.resolution_request_id with the same UUID. Each open or retry gets
a new UUID and fetches a fresh family rather than sharing the five-minute query
cache. Requests that never reach the authenticated resolver have no matching
server attribute. Gallery, dedicated preset pages, and submission do not yet
carry this resolution correlation.
attributes.context.browser.online and visibility describe browser state at
report time. They are diagnostic hints, not proof of connectivity. Reports do
not add raw prompts, request payloads, or server error messages.
Model changes in preset and custom-prompt forms preserve the selected SFW/NSFW axis, regardless of family axis or option order. Only an explicit rating change can change that axis. If the requested model lacks that rating, the resolver falls back within the same rating; if none is available, submission is blocked. Other later axes still reset to their authored defaults.
Where to look
Section titled “Where to look”The reporting path is browser capture → same-origin POST /api/telemetry →
server Pino logger → OpenTelemetry logs → Axiom. The server verifies user
identity and supplies the browser source label. Its OTel resource runtime is
nodejs, while the forwarded event’s runtime is browser. Filter by the event
source; filtering by resource runtime can select the wrong reports.
Sources: browser capture, ingestion route, metadata, and Node exporter.
- Start with
beef-prodfor production orbeef-stagingfor staging; verify the effective dataset in the deployment’sBRISKET_OTEL_TRACES_LOGS_HEADERS(x-axiom-dataset). Never copy its authorization value into a query or report. - Use the logs/traces dataset, not an
*-otel-metricsdataset. See the Axiom dataset inventory. - Start with the incident’s time window. If unknown, use the last hour, then widen to 24 hours. Record the timezone and exact window when sharing results.
Verify the live field layout first
Section titled “Verify the live field layout first”Production browser-log delivery and the source, severity, and payment-tag
queries were verified in beef-prod on 2026-09-22. Live events included
breadcrumbs, context, exception stacks, user identity, and browser release.
Staging was not checked. Inspect an event if another dataset’s layout differs.
In Axiom, open Query → Builder, select the dataset and time range, and add
service.name == brisket under Where. Run and expand an event. In Editor,
the equivalent discovery query is:
['beef-prod']| where _time > ago(1h)| where ['service.name'] == "brisket"| sort by _time desc| limit 20In the verified production log layout, service.name is a top-level field
and event metadata is exposed as attributes.source, attributes.path,
attributes.tags, and similar fields. Use ['attributes.source'] in APL.
The event viewer displays these fields inside an attributes object.
Do not use ['attributes.custom']['source'] for these browser logs: that
filter returned no events even while client reports were arriving. Trace
attributes can use a different layout. Inspect body, severity_text, and
attributes on a log event before adapting queries to another dataset.
References: Axiom Builder, OTel field mappings.
Find browser reports
Section titled “Find browser reports”Replace beef-prod with beef-staging when investigating staging. Each
pipe passes matching events to the next step; where filters rows, sort orders them, and limit caps the result.
['beef-prod']| where _time > ago(1h)| where ['service.name'] == "brisket"| where ['attributes.source'] == "browser"| sort by _time desc| limit 100Insert any of these filters before sort and limit. Add one at a time;
multiple where lines must all match.
| Investigation | APL filter |
|---|---|
| Errors only | where severity_text =~ "error" |
| Message text | where body contains "timeout" |
| Page | where ['attributes.path'] == "/checkout" |
| Verified user | where ['attributes.user_id'] == "user_REPLACE_ME" |
| Frontend build | where ['attributes.browser_release'] == "REPLACE_WITH_RELEASE" |
| Ingestion server build | where ['attributes.release'] == "REPLACE_WITH_RELEASE" |
| Environment | where ['attributes.environment'] == "production" |
| Next.js digest | where ['attributes.digest'] == "REPLACE_WITH_DIGEST" |
Prefix each filter with |. == is exact equality, =~ is case-insensitive
text equality, and contains searches inside text. Check severity values on an
actual event before adding the error filter; browser diagnostics include info
and warning reports too.
Filter Sentry-style tags
Section titled “Filter Sentry-style tags”The capture API retains tags and extra; ingestion writes extra as
context. There is no requirement for a field named sentry.
This example finds payment-tagged browser reports, including warnings and informational diagnostics:
['beef-prod']| where _time > ago(1h)| where ['service.name'] == "brisket"| where ['attributes.source'] == "browser"| extend tags = parse_json(tostring(['attributes.tags']))| where tags.feature == "payment"| sort by _time desc| limit 100extend adds a query-local field. The conversion handles tags represented as
JSON text; if the live schema exposes individual tag fields, filter those
directly. See Axiom JSON parsing.
Existing tag examples:
feature=payment,component=checkout: checkout error boundary.feature=payment,component=payment_modal: Primer payment diagnostics.feature=gallery-v2: gallery tag lifecycle.
Tags are optional. A tag filter excludes untagged global errors, so remove it before concluding that a feature has no failures.
Read one event and correlate it
Section titled “Read one event and correlate it”The names below are emitted by the
ingestion route.
Event metadata is exposed under attributes.*, for example
attributes.context and attributes.breadcrumbs. The exporter may normalize
exception details; inspect the event before assuming their final path.
| Field | Meaning |
|---|---|
body |
Captured error or diagnostic message, exported as the OTel log body. |
tags |
Short feature/component labels. |
context |
Sanitized extra details supplied by the caller. |
breadcrumbs |
Up to ten explicitly recorded recent actions, not a full session replay. |
user_id |
Clerk-verified identity; anonymous reports can have no user ID. |
path |
Browser page pathname, without query parameters or fragment. |
browser_release |
Build loaded in the browser; can differ from the server after deployment. |
release |
Ingestion server commit SHA, deployment ID fallback, or unknown. |
client_timestamp |
Browser-reported capture time; distinguish it from the server log’s _time and allow for clock skew. |
digest |
Optional Next.js error digest; not a universal issue ID. |
exception |
Captured error name and stack when the thrown value supplies them. |
For a render failure, keep service/time filters, remove source=browser, and
search for the same digest to find server reports too. Compare releases and
timestamps. Use a trace_id only when present and relevant: this transport
does not establish distributed browser tracing or guarantee an end-to-end
browser/server trace. Next request errors are handled in
instrumentation.ts.
To rank repeated browser errors, replace the starting query’s sort/limit with:
| where severity_text =~ "error"| summarize occurrences = count() by body| sort by occurrences desc| limit 20This counts identical messages, not Sentry issues or affected users. Messages
containing different IDs can form separate groups. To group a custom field,
cast it, for example summarize count() by tostring(['attributes.path']).
Empty results and reporting limits
Section titled “Empty results and reporting limits”- Confirm the environment, effective dataset, time window, and that the Sentry replacement is deployed. Remove user, release, tag, and severity filters; first look for any Brisket log, then browser reports.
- Inspect the live schema and field values. A wrong field path or absent tag is not evidence of zero client errors.
- Check stage and exporter configuration. Brisket enables reporting only in staging/production; logs export requires the OTLP URL and traces/logs headers. The endpoint’s 204 response alone does not prove Axiom receipt.
- Check delivery failures and reporting limits. Browser queues, per-minute budgets, server admission limits, ignored noise, offline clients, and exporter failures can drop reports. Oversized events can lose context, tags, and breadcrumbs. Account changes clear buffered context.
- Expect redaction and truncation. Email, token, payment, and other sensitive
values may be replaced; URL queries are removed. Raw stacks can contain
compiled frames; automatic browser source-map symbolication is not provided.
Credential assignments such as
password=...andapi_key=...are redacted in message and stack text; authorization and cookie values are removed through the end of their line, including spaces and additional cookies.
Sources: runtime gates, client limits, redaction, and coverage limitations.
When reporting an investigation, include the dataset, exact time range and timezone, query, observed field layout, and matching event count or query link. State whether Axiom was actually queried. If access is unavailable, provide the query to run and label delivery unverified; do not report zero errors.