AWS Lambda
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
bun add evlog
yarn add evlog
npm install evlog
2. Initialize once, log per invocation
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.
import { createAxiomDrain } from 'evlog/axiom'
import { initLogger } from 'evlog'
initLogger({
env: { service: 'sqs-consumer' },
silent: process.env.NODE_ENV === 'production',
drain: createAxiomDrain(),
})
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.
import { createError } from 'evlog'
throw createError({
message: 'Invalid payload',
status: 400,
why: 'Required field missing',
fix: 'Include orderId in the message body',
})
Related
- Standalone TypeScript: same
initLogger+createLogger+emit()model - Configuration:
silent,env.region(AWS_REGION), drains - Wide Events: designing one comprehensive event per unit of work