Skip to content

Observability and Usage

What the gateway reports about itself: logs, traces, metrics, per-request cost and the usage endpoints. Part of the Configuration Guide.

Settings Summary

Logging

Variable Default Description
LOG_LEVEL info Minimum log severity: info, warning, error, critical, or disabled
LOG_REQUEST_PARAMS false Include request/response parameters in logs (not recommended for production)
LOG_CLIENT_IP false Log client IP addresses (requires ENABLE_PROXY_HEADERS for real IPs behind proxies)

CloudWatch Metrics

Variable Default Description
CLOUDWATCH_METRICS false Emit per-request AWS-billed usage as CloudWatch EMF log lines
CLOUDWATCH_METRICS_NAMESPACE stdapi CloudWatch namespace for the emitted usage metrics
CLOUDWATCH_METRICS_USER_DIMENSION false Also publish the authenticated caller as a User metric dimension
CLOUDWATCH_METRICS_REGION Server's region Region the Usage API reads the published metrics from

Cost Tracking

Variable Default Description
COST_TRACKING false Estimate each request's cost from the published AWS price list
COST_PRICE_OVERRIDES {} JSON map of operator-supplied unit prices for models missing from the AWS catalog

Usage API

Variable Default Description
USAGE_API false Serve the organization usage and costs endpoints (requires CLOUDWATCH_METRICS)
USAGE_API_ADMIN_SCOPES None OAuth 2.0 scopes an Amazon Cognito token must all carry to read these endpoints
USAGE_API_MAX_METRICS 500 Refuse a query that would read more metric series than this
USAGE_API_MAX_RANGE_DAYS 92 Longest span between start_time and end_time on a query
USAGE_API_CACHE_TTL 60 Seconds an answered query is reused for (0 disables the cache)

Observability (OpenTelemetry)

Variable Default Description
OTEL_ENABLED false Enable distributed tracing via OpenTelemetry (integrates with AWS X-Ray, Jaeger, etc.)
OTEL_SERVICE_NAME stdapi.ai Service name identifier in trace visualizations
OTEL_EXPORTER_ENDPOINT http://127.0.0.1:4318/v1/traces OTLP HTTP endpoint URL for trace export
OTEL_SAMPLE_RATE 1.0 Trace sampling rate from 0.0 (none) to 1.0 (all requests)

Observability (OpenTelemetry)

Configure distributed tracing for debugging and performance monitoring. stdapi.ai integrates with AWS X-Ray, Jaeger, DataDog, and other OTLP-compatible systems.

OTEL_ENABLED

Purpose : Enable or disable OpenTelemetry tracing

Type : Boolean

Default : false

export OTEL_ENABLED=true

Performance Consideration

Disable in performance-critical deployments where observability is not needed.

OTEL_SERVICE_NAME

Purpose : Service identifier in trace visualizations

Default : stdapi.ai

Best Practice : Use descriptive names with environment information

export OTEL_SERVICE_NAME=stdapi-production-us-east-1

OTEL_EXPORTER_ENDPOINT

Purpose : OTLP HTTP endpoint URL for sending traces

Default : http://127.0.0.1:4318/v1/traces

Protocol : Must support OTLP HTTP format

AWS X-Ray (via ADOT):

export OTEL_EXPORTER_ENDPOINT=http://127.0.0.1:4318/v1/traces

Jaeger:

export OTEL_EXPORTER_ENDPOINT=http://jaeger:14268/api/traces

Cloud Provider OTLP:

# Use provider-specific OTLP endpoints
export OTEL_EXPORTER_ENDPOINT=https://your-provider-otlp-endpoint.com/v1/traces

OTEL_SAMPLE_RATE

Purpose : Percentage of requests to trace (controls cost vs. observability)

Type : Float (0.0 to 1.0)

Default : 1.0 (100%)

Development:

# Trace everything for debugging
export OTEL_SAMPLE_RATE=1.0

Production (Moderate Traffic):

# Sample 10% of requests
export OTEL_SAMPLE_RATE=0.1

Production (High Traffic):

# Sample 1% of requests
export OTEL_SAMPLE_RATE=0.01

Sampling Recommendations

Sample Rate Use Case
1.0 (100%) Development, debugging, low-traffic services
0.1 (10%) Production with moderate traffic
0.01 (1%) High-traffic production services
0.0 (0%) Equivalent to disabling tracing

Validation and Logging

The settings below decide what the gateway writes; the Logging and Monitoring guide describes what comes out of them — the event types and the fields common to all of them, the usage metrics carried on each entry, the CloudWatch EMF metric lines, and CloudWatch Logs Insights queries that follow a single request across its request, stream and background events.

STRICT_INPUT_VALIDATION

Purpose : Reject API requests containing unknown/extra fields instead of ignoring them

Type : Boolean

Default : false

# Returns HTTP 400 for requests with unexpected fields
export STRICT_INPUT_VALIDATION=true

CHAT_COMPLETIONS_REASONING_FIELD

Purpose : Choose which field carries a reasoning model's thinking text on /v1/chat/completions

Type : String

Default : reasoning_content

Options : reasoning_content, reasoning, none

Behavior : The OpenAI Chat Completions API returns no thinking text of its own — it reports only a reasoning_tokens count — so the providers that do return it have settled on two different names. reasoning_content is the DeepSeek spelling, which most clients that read reasoning at all look for first. reasoning is the name used by OpenRouter and vLLM. none emits neither, keeping responses strictly OpenAI-shaped. : The setting applies to both the completed message and the streamed deltas, so a client never sees one name while streaming and another at the end. Callers can also suppress reasoning per request with include_reasoning: false or reasoning: {"exclude": true}, whatever this is set to.

# Default: the name most clients read
export CHAT_COMPLETIONS_REASONING_FIELD=reasoning_content

# For clients written against OpenRouter or vLLM
export CHAT_COMPLETIONS_REASONING_FIELD=reasoning

# Strict OpenAI shape: never return thinking text
export CHAT_COMPLETIONS_REASONING_FIELD=none

LOG_LEVEL

Purpose : Control the minimum severity of log events written to STDOUT

Default : info

Options : info, warning, error, critical, disabled

Behavior : Only log events at or above the configured level are output. Log levels are ordered by severity: info < warning < error < critical

# Default: Output all log events
export LOG_LEVEL=info

# Production: Suppress info logs, show only warnings and higher
export LOG_LEVEL=warning

# Critical only: Show only critical errors
export LOG_LEVEL=critical

# Disable logging: Suppress all log output (not recommended)
export LOG_LEVEL=disabled

Log Level Examples

Level Outputs Use Case
info info, warning, error, critical Development, debugging, full visibility
warning warning, error, critical Production (recommended for most deployments)
error error, critical High-traffic production, reduce log volume
critical critical only Minimal logging, only show fatal errors
disabled none Not recommended - disables all logging

Production Recommendation

For production deployments, warning is recommended to reduce log volume while maintaining visibility into issues. The info level can generate significant log volume in high-traffic environments.

For detailed information about log events, structure, and monitoring strategies, see the Logging and Monitoring guide.

LOG_REQUEST_PARAMS

Purpose : Include request and response parameters (JSON body, form, query) in logs for integration debugging

Type : Boolean

Default : false

# Enable for debugging (NOT recommended for production)
export LOG_REQUEST_PARAMS=true

Security and Cost Warning

Enabling LOG_REQUEST_PARAMS may expose sensitive data in logs. Use only in development/debugging environments.

Logging full request/response payloads can also significantly increase log ingestion and storage costs, especially for large LLM prompts, tool calls, and generated outputs. If you must enable it, prefer short log retention, targeted sampling, and temporary use only.

LOG_CLIENT_IP

Purpose : Enable logging of client IP addresses for each request and add IP to OpenTelemetry spans

Type : Boolean

Default : false (disabled for privacy)

# Disabled (default) - no client IP logging
# No environment variable needed

# Enable client IP logging
export LOG_CLIENT_IP=true

Client IP Behavior

When enabled, client IP addresses are:

  • Included in log output for each request
  • Added as the client.address attribute to OpenTelemetry spans (when OTEL_ENABLED=true)

The IP address depends on your proxy configuration:

With ENABLE_PROXY_HEADERS=true (behind reverse proxy):

  • Logs the real client IP address from the X-Forwarded-For header
  • Shows the actual end-user IP, not the proxy IP
  • Requires your reverse proxy (ALB, CloudFront, etc.) to set the header correctly

With ENABLE_PROXY_HEADERS=false (default):

  • Logs the direct connection IP address
  • Typically shows your reverse proxy or load balancer IP, not the end-user IP
  • Limited usefulness unless application is directly exposed to clients

When to Enable

Enable LOG_CLIENT_IP when:

  • You need client IP addresses for security auditing or compliance
  • Analyzing traffic patterns and geographic distribution
  • Investigating abuse, fraud, or suspicious activity
  • Debugging client-specific issues

Important: Also enable ENABLE_PROXY_HEADERS=true when behind AWS ALB, CloudFront, or other reverse proxies to log the real client IP instead of the proxy IP.

Privacy Consideration

Client IP addresses are considered personal data under privacy regulations like GDPR. When logging IP addresses:

  • Consider shorter log retention periods
  • Document the purpose in your privacy policy
  • Ensure logs are stored securely
  • Implement log deletion procedures aligned with your data retention policy

Configuration for AWS Deployments

Behind AWS ALB or CloudFront:

# Enable proxy headers to get real client IPs
export ENABLE_PROXY_HEADERS=true
# Enable client IP logging
export LOG_CLIENT_IP=true

Direct exposure (not recommended for production):

# Only enable client IP logging
export LOG_CLIENT_IP=true
# ENABLE_PROXY_HEADERS remains false (default)

TIMEZONE

Purpose : IANA timezone identifier used for request date and time

Type : String (IANA timezone identifier)

Default : UTC

# UTC (default)
export TIMEZONE=UTC

# North America
export TIMEZONE=America/New_York

# Europe
export TIMEZONE=Europe/London

CloudWatch Metrics and Cost Tracking

The behavior of these settings — EMF line structure, cost log format, pricing accuracy, regional price fallback, known limitations, and the price override format with examples — is documented in CloudWatch Metrics (EMF) and Cost Tracking in the Logging and Monitoring guide.

CLOUDWATCH_METRICS

Purpose : Emit per-request AWS-billed usage as CloudWatch Embedded Metric Format (EMF) log lines

Type : Boolean

Default : false

export CLOUDWATCH_METRICS=true

CLOUDWATCH_METRICS_NAMESPACE

Purpose : CloudWatch namespace under which the emitted usage metrics are grouped

Type : String

Default : stdapi

Requirement : 1-255 characters, alphanumeric plus . - _ / # :, must not start with the reserved AWS/ prefix

export CLOUDWATCH_METRICS_NAMESPACE=my-app-metrics

CLOUDWATCH_METRICS_USER_DIMENSION

Purpose : Also publish the authenticated caller as a User metric dimension. This is what makes group_by=user_id answerable on the Usage API; without it, those queries have no per-user series to read.

Type : Boolean

Default : false

Requirement : CLOUDWATCH_METRICS and USAGE_API must both be enabled

The cardinality of this dimension is your caller population

Off by default because it adds one stored metric series per user × model × metric name, and Amazon CloudWatch bills every stored custom metric monthly. Enable it only where the number of distinct callers is bounded and known — see Usage API Query Cost.

export CLOUDWATCH_METRICS_USER_DIMENSION=true

CLOUDWATCH_METRICS_REGION

Purpose : Region the Usage API reads the published metrics from. Metrics are published in the region the server's logs are ingested in, which is the server's own region by default — set this only when the logs are shipped elsewhere, because a mismatch makes every usage query answer with empty buckets.

Type : String (AWS region name)

Default : The region the server runs in — its configured AWS region, or the first AWS_BEDROCK_REGIONS entry when none is set

export CLOUDWATCH_METRICS_REGION=eu-west-1

COST_TRACKING

Purpose : Estimate each request's cost from the published AWS price list — an estimate computed from billed quantities, not read back from your invoice (details and accuracy caveats). Disabled by default: it requires the extra pricing:GetProducts IAM permission — see Cost Tracking IAM Permissions.

Type : Boolean

Default : false

export COST_TRACKING=true

COST_PRICE_OVERRIDES

Purpose : Operator-supplied unit price overrides for models not covered by the AWS Price List API (format and example)

Type : JSON object — keys are model IDs, values are dicts mapping dimension name to price per one unit

Default : {}


Usage API

The Organization Usage and Costs API serves the OpenAI Administration usage and costs endpoints from the metrics this deployment already publishes. It is opt-in and off by default; while it is off, the routes still exist and answer 503, so a client can tell a disabled feature from a missing deployment.

The routes it adds, under the configured OPENAI_ROUTES_PREFIX:

/v1/organization/usage/completions
/v1/organization/usage/embeddings
/v1/organization/usage/moderations
/v1/organization/usage/images
/v1/organization/usage/audio_speeches
/v1/organization/usage/audio_transcriptions
/v1/organization/usage/web_search_calls
/v1/organization/usage/file_search_calls
/v1/organization/usage/vector_stores
/v1/organization/usage/code_interpreter_sessions
/v1/organization/costs

Prerequisites

  • CLOUDWATCH_METRICS must be enabled: the usage endpoints read the metrics it publishes. Enabling USAGE_API without it fails startup rather than serving endpoints that could never answer.
  • /v1/organization/costs additionally requires COST_TRACKING, which is what puts a cost on the published metrics; missing it is only a startup warning, and the endpoint answers with no cost data.
  • The role needs cloudwatch:GetMetricData and cloudwatch:ListMetrics — see Usage API IAM Permissions.

These queries are billed, and are not in the CloudWatch free tier

Amazon CloudWatch bills each query by the number of metric series it reads, and enabling the Usage API also publishes the usage metrics under an extra dimension set that is billed monthly. A single client polling the endpoints once a minute is a recurring three-figure monthly charge on a large catalogue. Read Usage API Query Cost before enabling it, and keep the limits below at their defaults unless you have priced the change.

USAGE_API

Purpose : Serve the organization usage and costs endpoints. When disabled, the routes exist and answer 503. Enabling it also publishes the usage metrics under an additional dimension set, which carries its own monthly cost.

Type : Boolean

Default : false

Requirement : CLOUDWATCH_METRICS must be enabled, or startup fails; /v1/organization/costs additionally requires COST_TRACKING, which is only a startup warning when missing

export USAGE_API=true

USAGE_API_ADMIN_SCOPES

Purpose : OAuth 2.0 scopes an Amazon Cognito token must all carry to read these endpoints. Left empty, no user pool token is accepted and only the deployment's own API_KEY may read them. A tenant API key is never accepted, whatever this is set to — organization-wide usage is not a tenant's to read.

Type : Comma-separated list of scope names

Default : Empty — admin API key only

export USAGE_API_ADMIN_SCOPES=stdapi/admin,stdapi/usage.read

USAGE_API_MAX_METRICS

Purpose : Refuse a query that would read more metric series than this, before it is billed. A query over the limit is rejected rather than served, so a client narrows its group_by or its time range instead of running up the bill.

Type : Integer

Default : 500

Requirement : 1–500 — 500 is also the Amazon CloudWatch per-request maximum

# Tighter cap on a deployment with a large model catalogue
export USAGE_API_MAX_METRICS=100

USAGE_API_MAX_RANGE_DAYS

Purpose : Longest span allowed between start_time and end_time on a query. A longer range is rejected rather than served, so a client cannot ask for a year of daily buckets in one call.

Type : Integer (days)

Default : 92

Requirement : 1–455

# One month per query
export USAGE_API_MAX_RANGE_DAYS=31

USAGE_API_CACHE_TTL

Purpose : Seconds an answered query is reused for. This is what makes a polling client affordable: within the TTL, repeated identical queries are served from the cached answer and cost nothing. Set 0 to disable the cache — every query then reaches Amazon CloudWatch and is billed.

Type : Integer (seconds)

Default : 60

Requirement : 0–3600

# Longer reuse window for dashboards that poll aggressively
export USAGE_API_CACHE_TTL=300