Overcast is alpha — behaviour and APIs may change between releases. Pin your version and read the changelog before upgrading.

overcast

Lambda logging

What Overcast writes around each Lambda invocation: the Text lines, the JSON telemetry record vocabulary and its levels, what filtering applies to, and the extension telemetry gaps.

What Lambda writes around each invocation, and which of it reaches CloudWatch Logs.

LogFormat decides what is written; under JSON the two log levels decide how much of it is kept. LogGroup is respected too: a function with a custom group writes there, and the group is created on CreateFunction like the default /aws/lambda/<function-name> one.

Text is the default and unchanged: the plain-text START, END and REPORT lines real Lambda writes, byte for byte. ApplicationLogLevel and SystemLogLevel do not apply to Text on AWS, so Overcast filters nothing in this mode.

The JSON record vocabulary

JSON replaces those three lines with the events AWS publishes through the Telemetry API, one object per line, shaped {"time", "type", "record"}:

Event typeReplacesSystem log levelRecord
platform.initStartDEBUGinitializationType, phase, functionName, functionVersion
platform.initRuntimeDoneDEBUG on success, else WARNinitializationType, phase, status
platform.initReportDEBUG on-demand, INFO provisioned, WARN when it failedplus metrics: durationMs
platform.startSTARTINFOrequestId, version, tracing
platform.runtimeDoneENDDEBUG on success, else WARNrequestId, status, tracing, spans (responseLatency), metrics: durationMs, producedBytes
platform.reportREPORTINFO on success, else WARNrequestId, status, tracing, metrics: durationMs, billedDurationMs, memorySizeMB, maxMemoryUsedMB

The levels are AWS’s own system-log-level mapping. status is success, failure (the handler returned an error), error (the environment ended the invocation) or timeout. metrics.initDurationMs appears in platform.report only on the first report of an on-demand cold start. tracing carries the X-Amzn-Trace-Id the runtime genuinely received; spanId stays absent, because Overcast mints no spans.

platform.runtimeDone’s metrics are measured by the in-container init — the invocation being handed to the runtime, to its answer arriving back at the init’s proxy — so they are a different, smaller span than platform.report’s host-measured durationMs, as AWS’s two records are two different measurements. The host’s own measurements are the fallback where the runtime never answered.

The three init-phase records come from that same init — PID 1 and the proxy in front of the Runtime API, so it observes the phase beginning and ending rather than inferring either — and travel on the same sequence-ordered stream as the container’s own output. All three are DEBUG for a successful on-demand cold start, so a default log stream never shows them; a provisioned environment’s platform.initReport is INFO, and an environment whose runtime died before asking for work reports status: error on both closing records at WARN. platform.initReport’s durationMs is measured inside the environment, a different span from the host-measured Init Duration; the two differ by a few milliseconds. In Text format none of the three is written to CloudWatch Logs.

What is missing from the vocabulary

Overcast emits only the subset of AWS’s schema it genuinely observes.

Field or recordOvercast
errorTypePopulated for the one outcome whose AWS name is documented — a runtime that exited is Runtime.ExitError
platform.initStart’s runtimeVersion, runtimeVersionArn, instanceId, instanceMaxMemoryOmitted
platform.runtimeDone spans other than responseLatencyOmitted — responseDuration ends only after the answer has finished streaming through the init’s unbuffered proxy, and runtimeOverhead exists only at the runtime’s next poll, both after the record is on its way
SnapStart restore records (platform.restoreStart, platform.restoreRuntimeDone, platform.restoreReport)Not emitted; SnapStart is not emulated
Extension telemetry destinations (Logs API PUT /2020-08-15/logs, Telemetry API PUT /2022-07-01/telemetry)HTTP only. Buffering configuration is honoured with AWS’s defaults; out-of-range values are clamped to the documented limits rather than rejected. Batches are posted from inside the execution environment, as on AWS, so a destination on any of the three hosts a sandbox resolves — 127.0.0.1, localhost and AWS’s documented sandbox.localdomain — works on every developer platform
UpdateFunctionConfiguration with an explicitly empty LoggingConfig: {}501 — AWS’s semantics for that shape are undocumented. LoggingConfig with explicit members applies normally

Filtering

Both levels default to INFO under JSON, as on AWS. The ordering is TRACE < DEBUG < INFO < WARN < ERROR < FATAL, and a record is kept when its own level is at or above the configured one.

  • SystemLogLevel filters the platform records above, by the level in the table.
  • ApplicationLogLevel filters the function’s own stdout and stderr, by the "level" member of each record. A line that parses as a JSON object with a recognised level is filtered on it; everything else — unstructured text, malformed JSON, a missing or unknown level — is treated as INFO, which is what AWS documents. Level names are matched case-insensitively.

Filtering decides what reaches CloudWatch Logs and the X-Amz-Log-Result tail only. Telemetry and Logs API subscribers receive the complete set either way — AWS is explicit that the CloudWatch system log level does not affect Telemetry API behaviour.

The configuration is handed to the runtime the way AWS hands it over, for the managed runtimes and Powertools to read: AWS_LAMBDA_LOG_FORMAT is always set to Text or JSON, and AWS_LAMBDA_LOG_LEVEL is set to the effective ApplicationLogLevel only under JSON. Those values are baked into a container at start, so changing LogFormat or either level retires the warm environments.