Skip to content

AWS Lambda

One event per invocation, including the invocations that time out.

Updated View as Markdown
from widelog import lambda_wide_event, use_logger


@lambda_wide_event
def handler(event, context):
    use_logger().set(order={"id": "o_1"})
    return {"statusCode": 200}

The decorator adds request_id, cold_start, function, remaining_ms, and the X-Ray trace_id. It reads method and path from Function URL, API Gateway v1, and API Gateway v2 payloads, and records statusCode from what the handler returns.

{
  "timestamp": "2026-07-30T10:23:45.612Z",
  "level": "info",
  "service": "checkout",
  "duration_ms": 812.4,
  "method": "POST",
  "path": "/checkout",
  "status": 200,
  "request_id": "req_1",
  "cold_start": true,
  "trace_id": "Root=1-abc;Parent=def;Sampled=1",
  "function": { "name": "checkout-fn", "version": "$LATEST", "memory_mb": 512 },
  "remaining_ms": 29187,
  "order": { "id": "o_1" }
}

No drain needed

On Lambda, stdout is CloudWatch Logs. The default sink already goes there, so there is no batching to configure, no waitUntil callback to pass, and no extension to install.

The timeout guard

500ms before the invocation deadline, widelog emits the event with "timed_out": true.

This matters because Lambda kills the process at the deadline. Without the guard, the one invocation you most want a record of is the only one that leaves none. emit() is idempotent, so if the handler does return afterwards, nothing is written twice.

{ "level": "error", "timed_out": true, "request_id": "req_1", "cold_start": false }

Cold starts

cold_start is true on the first invocation in a container and absent afterwards, which makes “how much of our p99 is cold starts” a filter rather than a research project.

Errors

There is no framework to convert an exception into a response, so decide what the caller should see. Catching and returning gives API Gateway a real status:

try:
    order_id = charge(card)
except WidelogError as exc:
    log.error(exc)
    return {"statusCode": exc.status, "body": json.dumps(exc.to_dict())}

Letting it raise instead marks the invocation failed, which is what you want when the trigger should retry. The event is emitted either way.

Try it without an AWS account

The repository ships a runnable example that fakes the context object and prints three events: one authorized, one declined, and one that hits the timeout guard.

uv run examples/aws_lambda_handler.py
Navigation

Type to search…

↑↓ navigate↵ selectEsc close