Frameworks

AWS Lambda

Wide events and structured logging in AWS Lambda functions, including SQS consumers and event-driven handlers.

AWS Lambda has no HTTP middleware lifecycle like Nuxt or Express, so evlog behaves like standalone TypeScript: call initLogger() once, create a logger per invocation (or per SQS message) with createLogger(), then call log.emit() when work finishes.

Set up evlog in my AWS Lambda function

Why not one global createLogger?

Lambda execution environments are reused: the same process can handle many invocations in sequence. Module-level variables persist, so one shared logger instance can leak fields from a previous invocation into the next.

Do this: initLogger() once at the top level (configuration only), and createLogger() inside the handler (or inside the loop over SQS records) for each unit of work.

Dependency injection (passing log into functions) is optional—it helps tests and clarity—but what matters is one logger per invocation, not whether you use DI.

Quick Start

1. Install

pnpm add evlog

2. Initialize once, log per invocation

src/handler.ts
import type { SQSEvent } from 'aws-lambda'
import { initLogger, createLogger } from 'evlog'

initLogger({
  env: { service: 'sqs-consumer', environment: process.env.NODE_ENV },
})

export async function handler(event: SQSEvent) {
  for (const record of event.Records) {
    const log = createLogger({
      messageId: record.messageId,
      approximateReceiveCount: record.attributes?.ApproximateReceiveCount,
    })

    try {
      log.set({ queue: { name: record.eventSourceARN } })
      // … parse record.body and process the message
      log.set({ status: 'ok' })
    } catch (error) {
      log.error(error instanceof Error ? error : new Error(String(error)))
      log.set({ status: 'error' })
      throw error
    } finally {
      log.emit()
    }
  }
}

If you process the whole batch as one logical unit, use a single createLogger() per handler invocation with batch metadata instead of one logger per record.

Stdout and silent

Many teams ingest Lambda logs from CloudWatch via stdout. If you use a drain adapter (OTLP, Datadog, Axiom, etc.) and want JSON or platform-specific formatting without duplicate console noise, set silent: true in production—see Configuration.

src/handler.ts
import { createAxiomDrain } from 'evlog/axiom'
import { initLogger } from 'evlog'

initLogger({
  env: { service: 'sqs-consumer' },
  silent: process.env.NODE_ENV === 'production',
  drain: createAxiomDrain(),
})
If silent is enabled without a drain, events may not be visible anywhere. See the configuration docs for details.

Error handling

Use createError where you want structured fields (why, fix, link). Map failures to your Lambda return or rethrow so SQS retry/DLQ behavior stays correct—evlog does not replace AWS error semantics.

src/handler.ts
import { createError } from 'evlog'

throw createError({
  message: 'Invalid payload',
  status: 400,
  why: 'Required field missing',
  fix: 'Include orderId in the message body',
})