BVE Gateway emits one structured JSON log line per request to the Cloudflare log stream. Consume the stream with bunx wrangler tail during development, or ship it to S3, R2, Splunk, or Datadog via Cloudflare Logpush in production.
Each log line is a single JSON object. level maps directly to the console API used (info → console.log, warn → console.warn, error → console.error) so Cloudflare Workers Logs can filter by severity without post-processing.
Normalized Hono route pattern (e.g. /v1/chat/completions, /admin/api-keys/:id). Differs from path — use this for grouping by endpoint in Logpush without raw IDs obscuring aggregates
query
string
Query string present
URL query string (after ?), capped at 200 chars. Credential-like params (api_key, token, secret, etc.) are replaced with [REDACTED]
requestSize
integer
Content-Length header present
Parsed request body size in bytes. Absent for GET/OPTIONS and streaming uploads
responseSize
integer
Content-Length response header present
Response body size in bytes. Absent for streaming responses (SSE/NDJSON) and audio/image pass-throughs
clientRequestId
string
Client sent X-Request-Id
Client-supplied request ID, preserved for cross-system tracing. Absent when the client did not send X-Request-Id or the value fails the safe-character check (alphanumeric + -_., ≤ 128 chars)
Quick flag for slow requests — filter without parsing integers
stream
true
SSE or NDJSON response
Present on streaming responses. High latencyMs is expected (stream stays open until the client disconnects)
webSearch
true
web_search_options present
Set when the POST /v1/chat/completions body included web_search_options, indicating the upstream performed a real-time web search. Filter Logpush by webSearch=true to monitor web-search traffic share
upstreamLatencyMs
integer
Upstream call made
Time in milliseconds from the upstream HTTP request to response headers received. Absent for gateway-rejected requests (401, 403, 429 from middleware)
slowUpstream
true
upstreamLatencyMs > 3000
Quick flag for slow upstream responses
gatewayOverheadMs
integer
upstreamLatencyMs present
max(0, latencyMs - upstreamLatencyMs). Covers auth cache lookups, D1 allowlist queries, Durable Object quota checks, and request validation. Use to distinguish “Fuelix is slow” from “gateway is slow”
Expiry timestamp. Use in Logpush to alert on soon-to-expire keys without querying D1
keyRpmLimit
integer
429 rate_limit_exceeded
Configured requests-per-minute limit for the key. Makes rejection log entries self-contained — pair with quotaReason and keyRpdLimit for Logpush alert rules
keyRpdLimit
integer
429 rate_limit_exceeded
Configured requests-per-day limit for the key
keyMonthlyReqLimit
integer
429 rate_limit_exceeded, limit set
Configured monthly request cap. Absent for keys with no monthly request limit
keyMonthlyTokenLimit
integer
429 rate_limit_exceeded, limit set
Configured monthly token cap. Absent for keys with no monthly token limit
Model ID from the request body, credential-pattern-redacted
provider
string
Model recognized
Model creator inferred from the model ID prefix. Values: openai, anthropic, google, cohere, meta, mistral, cursor, deepseek, wasikan. Absent for D1-only custom models. Matches the bve_provider annotation from GET /v1/models
HTTP status returned by Fuelix before any gateway normalization
upstreamRequestId
string
Upstream returned a request ID
Fuelix’s own request ID from the x-request-id (OpenAI, Groq) or request-id (Anthropic) response header. Use this to correlate a BVE log entry with the corresponding entry in Fuelix’s logs
completionId
string
Non-streaming buffered response
The id field from the upstream JSON response (e.g. chatcmpl-abc123, msg_01abc123, resp_abc123). Absent for streaming, embeddings, and Gemini
Each cache status is "HIT" (fresh), "STALE" (past soft TTL, served stale + background refresh scheduled), or "MISS" (synchronous D1/upstream fetch). Monitor these to detect elevated D1 round-trips or cache invalidation storms.
Field
Condition
Description
authCacheStatus
All authenticated requests
API key SWR cache (20 s soft / 30 s hard TTL). HIT = no D1 query; MISS = synchronous D1 lookup
modelsCacheStatus
GET /v1/models
Upstream model list cache (4 min soft / 5 min hard TTL)
modelAllowlistCacheStatus
JSON requests with a model field
Per-model global D1 allowlist cache (20 s / 30 s). Present even on rejected requests
tokenCacheStatus
Keys with monthly_token_limit
Monthly token total cache (20 s / 30 s). Present even on 429 quota rejections
Token fields are only present for non-streaming buffered JSON responses where usage is synchronously available. All are absent for streaming, embeddings, audio, and gateway-level rejections.
Field
Type
Condition
Description
promptTokens
integer
Usage available
Input tokens consumed
completionTokens
integer
Usage available
Output tokens generated
totalTokens
integer
Usage available
promptTokens + completionTokens
cachedTokens
integer
Non-zero cached tokens
Prompt tokens served from provider cache. For OpenAI: billed at 50% of normal rate. For Anthropic (cache_read_input_tokens): billed at ~10% of normal rate
cacheWriteTokens
integer
Anthropic only, non-zero
Prompt tokens written to Anthropic’s cache for the first time, billed at 125% of normal. Absent for all other providers
reasoningTokens
integer
OpenAI o-series, non-zero
Internal chain-of-thought reasoning tokens (not visible in the response). Counts toward completionTokens but billed separately
Version UUID from the CF_VERSION_METADATA binding. Always present in production when the binding is configured. Also set as X-BVE-Worker response header
workerTag
string
Optional deploy tag set via bunx wrangler deploy --tag <tag>. Use in CI to correlate log entries with a specific release
To ship logs to an external destination (S3, R2, Splunk, Datadog, etc.), configure a Cloudflare Logpush job for the Workers Trace Events dataset in the Cloudflare dashboard:
Go to Cloudflare Dashboard → Analytics & Logs → Logpush
Create a new job for Workers Trace Events
Select the bve-gateway Worker
Set your destination (R2 bucket, S3, HTTP endpoint, etc.)
Apply a filter if needed (e.g. Outcome = ok for success-only logs)
Each delivered record includes the ConsoleLog fields containing your structured JSON. Parse the Message field with jq or your SIEM’s JSON extractor to access individual fields.
Models running on Groq infrastructure are identified by their model ID prefix: llama-* → provider: "meta", mixtral-* → provider: "mistral".
upstreamRequestId comes from the x-groq-request-id response header.
x-groq-processing-time (server-side inference seconds) is forwarded to clients for latency diagnostics but is not in the structured log. Use upstreamLatencyMs and gatewayOverheadMs for end-to-end analysis.
OpenRouter-routed requests show the actual model selected after provider routing in x-openrouter-model (forwarded to clients). The model log field reflects the model ID the client requested.
x-or-cache-status (HIT/MISS) and x-or-remaining-tokens are forwarded to clients but not in the structured log.
Slash-style routing prefixes (e.g. openai/gpt-4o) are rejected at the gateway with errorCode: "invalid_value" before reaching Fuelix.