Runtime Logs
Runtime Logs help you observe and troubleshoot Reactor and Proxy code-transform executions. They capture messages emitted by your code and platform lifecycle events, such as when execution starts, completes, times out, or fails.
Configurable runtime-log records use the structure documented on this page and can be delivered through Webhooks.
Enable Runtime Logs
Set runtime.logs.enabled to true on a Reactor or on each Proxy code transform whose records you want to capture. Configurable runtime-log capture is disabled by default.
{
"runtime": {
"image": "node24",
"logs": {
"enabled": true,
"level": "info"
}
}
}
Changing runtime.logs.enabled or runtime.logs.level updates the Reactor or affected Proxy code transform asynchronously. While the resource is updating, invocations use the previous active configuration. If the resource becomes outdated, invocations continue using the previous working version. The new log settings apply after the resource returns to active.
See the Reactor API and Pre-Configured Proxies API for complete configuration examples.
Choose a Log Level
runtime.logs.level is optional and defaults to info.
| Level | Use it for |
|---|---|
debug | Detailed diagnostic context during development or focused troubleshooting. This can produce a high volume of records. |
info | Normal execution milestones and meaningful operations. |
warn | Unexpected or degraded conditions from which execution can continue. |
error | Failures that prevent an operation from completing as intended. |
The configured level includes application records at that level and every more severe level. For example, warn captures warn and error records. This setting does not filter platform lifecycle records.
Log Application Activity
Use the logger passed on the Reactor or transform event:
module.exports = async function handler(event) {
const { logger } = event;
logger.debug("Received request");
logger.info("Processing request", { operation: "tokenize" });
logger.warn("Using fallback configuration");
logger.error("Processing failed", new Error("Processor unavailable"));
// ...
};
The logger supports logger.debug, logger.info, logger.warn, and logger.error. logger.log is an alias for logger.info.
Output written through console.* is not captured as a runtime-log record. Use the injected logger instead.
Application records use source: "application" and record_type: "log". They contain a level and message, and can include structured attributes and direct JavaScript Error details.
Understand Platform Lifecycle Records
Basis Theory emits platform lifecycle records independently of application logging calls. These records use source: "platform" and record_type: "lifecycle":
execution.startedindicates that runtime execution started.execution.completedindicates that runtime execution ended and includes anoutcome.
A completed execution has one of these outcomes:
| Outcome | Meaning |
|---|---|
success | Runtime execution completed successfully. |
timeout | Runtime execution exceeded its configured timeout. |
out_of_memory | Runtime execution exhausted its available memory. |
application_error | Application code failed without a platform error classification. |
platform_error | The runtime platform reported another recognized failure. |
An execution.completed record can include duration_ms, the elapsed execution time in milliseconds.
Runtime Log Record Types
| Source | Record type | Description | Type-specific fields |
|---|---|---|---|
application | log | A message or structured diagnostic context emitted through event.logger. | level, message, attributes, error |
platform | lifecycle | Execution lifecycle information emitted automatically by the runtime. | event, outcome, duration_ms |
Record Examples
debug, info, and warn application records share the same shape; error records can also include the nested error object.
- Application info
- Application error
- Lifecycle started
- Lifecycle completed
{
"occurred_at": "2026-08-11T12:00:00.010Z",
"sequence": 1,
"source": "application",
"record_type": "log",
"level": "info",
"message": "Processing request",
"attributes": {
"operation": "tokenize"
}
}
{
"occurred_at": "2026-08-11T12:00:00.010Z",
"sequence": 1,
"source": "application",
"record_type": "log",
"level": "error",
"message": "Processing failed",
"error": {
"type": "Error",
"message": "Processor unavailable",
"stack_trace": "Error: Processor unavailable\n at handler (index.js:7:37)"
}
}
{
"occurred_at": "2026-08-11T12:00:00.000Z",
"sequence": 0,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.started"
}
{
"occurred_at": "2026-08-11T12:00:30.000Z",
"sequence": 2,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.completed",
"outcome": "timeout",
"duration_ms": 30000
}
Sanitization and Capture Limitations
Basis Theory bounds and sanitizes customer application messages, attributes, and direct Error details before publishing them. Sensitive values in supported content are replaced with [REDACTED]. Only the documented runtime-log fields are published. Internal execution metadata and identifiers are omitted.
Runtime Logs are fail-open: a failure to capture or deliver records does not change the customer code result. Abrupt runtime termination can prevent final records, including an execution.completed lifecycle record, from being captured.
Avoid logging sensitive information, including secrets, authentication tokens, or payment card data, in messages or attributes. Sanitization reduces accidental exposure but does not replace controlling what your code logs.
Receive Runtime Logs through Webhooks
For Node.js runtime images, enable configurable runtime-log capture and subscribe to:
reactor.logfor Reactor runtime records.proxy.logfor Proxy request-transform and response-transform runtime records.
Register these values in the Webhook events list through the Create Webhook API. Follow the Webhooks overview for signature verification, retries, and idempotent processing.
Each delivery has three contract layers:
- The outer
eventfields, includingevent.trace_id, anddelivered_atfollow the common Webhook event structure. Related events can share this request-level correlation identifier. event.datafollows the shared runtime-log event data fields. This layer containsinvocation_id, resource identity, batch sequence bounds, andrecords.- Each item in
event.data.recordsfollows the shared runtime-log record fields.
Runtime Log Event Data Fields
Runtime-log event batches have these data fields:
| Name | Type | Presence | Applies to | Description |
|---|---|---|---|---|
| invocation_id | String | Required | reactor.log, proxy.log | Invocation identity shared by every batch from one invocation. |
| reactor | Object | Required | reactor.log | Customer-visible Reactor resource. |
| reactor.id | String | Required | reactor.log | Reactor identifier. |
| proxy | Object | Required | proxy.log | Customer-visible Proxy resource. |
| proxy.id | String | Required | proxy.log | Proxy identifier. |
| transform_type | String | Required | proxy.log | request_transform or response_transform. |
| sequence_start | Number | Required | reactor.log, proxy.log | Sequence of the first record in this batch. |
| sequence_end | Number | Required | reactor.log, proxy.log | Sequence of the last record in this batch. |
| records | Array | Required | reactor.log, proxy.log | Non-empty ordered array. Each item follows the runtime-log record fields. |
Runtime Log Record Fields
Each runtime-log record has this shape:
| Name | Type | Presence | Description |
|---|---|---|---|
| occurred_at | String | Required | ISO 8601 timestamp when the record occurred. |
| sequence | Number | Required | Invocation-local record order. |
| source | String | Required | application for logger output or platform for runtime lifecycle records. |
| record_type | String | Required | log for application records or lifecycle for platform records. |
| event | String | Lifecycle only | execution.started or execution.completed. |
| level | String | Application only | debug, info, warn, or error. |
| message | String | Application only | Sanitized application message. If no message argument is supplied, this is the direct Error's message when present, otherwise an empty string. |
| attributes | Object | Optional application field | Sanitized structured attributes supplied to the logger. |
| error | Object | Optional application field | Sanitized details from a direct JavaScript Error. |
| error.type | String | When error is present | Error type or name. |
| error.message | String | When error is present | Error message. |
| error.stack_trace | String | Optional when error is present | Sanitized stack trace. |
| outcome | String | Completed lifecycle only | success, timeout, out_of_memory, application_error, or platform_error. |
| duration_ms | Number | Optional completed lifecycle field | Elapsed execution time in milliseconds. |
occurred_at describes when an individual record occurred. Use sequence, not a timestamp, to order records from the same invocation.
The following tabs show complete deliveries for each event type.
- Reactor
- Proxy request transform
- Proxy response transform
{
"event": {
"id": "730ffeb4-d511-5d96-bc79-98ae023a8bc7",
"type": "reactor.log",
"timestamp": "2026-08-11T12:00:00.100Z",
"tenant_id": "869d5b1c-1ae8-4ce6-96c6-73a602b407ff",
"trace_id": "ecae00ef-164d-4c0e-aa58-3f306fc7600b",
"data": {
"invocation_id": "8230820d-6e6d-4a87-b801-991e2c4535d1",
"reactor": {
"id": "5b493235-6917-4307-906a-2cd6f1a90b13"
},
"sequence_start": 0,
"sequence_end": 2,
"records": [
{
"occurred_at": "2026-08-11T12:00:00.000Z",
"sequence": 0,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.started"
},
{
"occurred_at": "2026-08-11T12:00:00.010Z",
"sequence": 1,
"source": "application",
"record_type": "log",
"level": "info",
"message": "Validating payload",
"attributes": {
"operation": "validate_payload"
}
},
{
"occurred_at": "2026-08-11T12:00:00.018Z",
"sequence": 2,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.completed",
"outcome": "success",
"duration_ms": 18.4
}
]
}
},
"delivered_at": "2026-08-11T12:00:00.673Z"
}
{
"event": {
"id": "2fcaf393-b635-5ce8-b6a7-b1fc82c78970",
"type": "proxy.log",
"timestamp": "2026-08-11T12:01:00.100Z",
"tenant_id": "869d5b1c-1ae8-4ce6-96c6-73a602b407ff",
"trace_id": "0f777425-9080-4d82-b557-799f43aefcba",
"data": {
"invocation_id": "d00ab9c5-b8ec-46de-8db1-6952d4f19a4c",
"proxy": {
"id": "2db4ded5-98a1-4b3f-87d1-2788340d89ed"
},
"transform_type": "request_transform",
"sequence_start": 0,
"sequence_end": 2,
"records": [
{
"occurred_at": "2026-08-11T12:01:00.000Z",
"sequence": 0,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.started"
},
{
"occurred_at": "2026-08-11T12:01:00.010Z",
"sequence": 1,
"source": "application",
"record_type": "log",
"level": "info",
"message": "Validating payload",
"attributes": {
"operation": "validate_payload"
}
},
{
"occurred_at": "2026-08-11T12:01:00.018Z",
"sequence": 2,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.completed",
"outcome": "success",
"duration_ms": 18.4
}
]
}
},
"delivered_at": "2026-08-11T12:01:00.673Z"
}
{
"event": {
"id": "5a9fb4f1-bd87-5707-9a40-bc2f4a24c872",
"type": "proxy.log",
"timestamp": "2026-08-11T12:01:01.100Z",
"tenant_id": "869d5b1c-1ae8-4ce6-96c6-73a602b407ff",
"trace_id": "0f777425-9080-4d82-b557-799f43aefcba",
"data": {
"invocation_id": "755362a9-0d89-475a-a8aa-b4156e9c5307",
"proxy": {
"id": "2db4ded5-98a1-4b3f-87d1-2788340d89ed"
},
"transform_type": "response_transform",
"sequence_start": 0,
"sequence_end": 2,
"records": [
{
"occurred_at": "2026-08-11T12:01:01.000Z",
"sequence": 0,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.started"
},
{
"occurred_at": "2026-08-11T12:01:01.012Z",
"sequence": 1,
"source": "application",
"record_type": "log",
"level": "info",
"message": "Validating payload",
"attributes": {
"operation": "validate_payload"
}
},
{
"occurred_at": "2026-08-11T12:01:01.015Z",
"sequence": 2,
"source": "platform",
"record_type": "lifecycle",
"event": "execution.completed",
"outcome": "success",
"duration_ms": 15.2
}
]
}
},
"delivered_at": "2026-08-11T12:01:01.744Z"
}
Process Ordered Webhook Batches
One invocation can produce multiple bounded Webhook events. Every batch for the invocation shares invocation_id. Within each event, sequence_start and sequence_end identify the first and last included record, and every record has its own sequence.
Webhook delivery order is not record order. Deduplicate repeated deliveries by event.id, group batches by invocation_id, and order their records by sequence.
For example, these deliveries arrive as batch B, batch A, and a retry of batch A:
[
{
"event": {
"id": "8d8fd53b-a0a5-553c-8aeb-32dd7133885d",
"data": {
"invocation_id": "c7a9e2b4-1d63-4f85-a0c2-5e7b913d684f",
"sequence_start": 2,
"sequence_end": 3,
"records": [
{ "sequence": 2, "message": "record 2" },
{ "sequence": 3, "message": "record 3" }
]
}
}
},
{
"event": {
"id": "f032f5bc-c05c-5889-b833-9778c406592a",
"data": {
"invocation_id": "c7a9e2b4-1d63-4f85-a0c2-5e7b913d684f",
"sequence_start": 0,
"sequence_end": 1,
"records": [
{ "sequence": 0, "message": "record 0" },
{ "sequence": 1, "message": "record 1" }
]
}
}
},
{
"event": {
"id": "f032f5bc-c05c-5889-b833-9778c406592a",
"data": {
"invocation_id": "c7a9e2b4-1d63-4f85-a0c2-5e7b913d684f",
"sequence_start": 0,
"sequence_end": 1,
"records": [
{ "sequence": 0, "message": "record 0" },
{ "sequence": 1, "message": "record 1" }
]
}
}
}
]
After deduplicating by event ID and sorting by record sequence, the result is 0, 1, 2, 3.
Delivery is at least once. Do not depend on Webhook arrival time or any timestamp for ordering. Sequences are invocation-local ordering keys, but abrupt termination can prevent records from being captured. Do not assume that every invocation contains a complete or contiguous sequence.
Existing CLI Live Logs
You can continue using the CLI's existing live log stream for real-time Reactor and Proxy troubleshooting. CLI streaming is separate from configurable runtime-log capture and does not provide the normalized application and lifecycle record format documented on this page. See Reactor Logs and Proxy Logs for details.