Skip to content

Commit 79cb4a4

Browse files
authored
docs: add guide for AWS lambda (#270)
1 parent 037dc81 commit 79cb4a4

4 files changed

Lines changed: 136 additions & 0 deletions

File tree

.changeset/aws-lambda-docs.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'evlog': patch
3+
---
4+
5+
Add an [AWS Lambda](https://www.evlog.dev/frameworks/aws-lambda) guide to the documentation site (`initLogger` once, `createLogger` per invocation, manual `emit`).
Lines changed: 5 additions & 0 deletions
Loading

apps/docs/content/4.frameworks/00.overview.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ evlog provides native integrations for every major TypeScript framework. The sam
2525
| [Fastify](/frameworks/fastify) | `evlog/fastify` | Plugin | `request.log` / `useLogger()` | Stable |
2626
| [Elysia](/frameworks/elysia) | `evlog/elysia` | Plugin | `log` (context) / `useLogger()` | Stable |
2727
| [Cloudflare Workers](/frameworks/cloudflare-workers) | `evlog/workers` | Factory | `createWorkersLogger()` | Stable |
28+
| [AWS Lambda](/frameworks/aws-lambda) | `evlog` | Manual | `createLogger()` / `createRequestLogger()` | Guide |
2829
| [Standalone](/frameworks/standalone) | `evlog` | Manual | `createLogger()` / `createRequestLogger()` | Stable |
2930
| [Astro](/frameworks/astro) | `evlog` | Manual | `createRequestLogger()` | Guide |
3031
| [Custom](/frameworks/custom-integration) | `evlog/toolkit` | Build your own | `createMiddlewareLogger()` | Beta |
@@ -147,6 +148,15 @@ evlog provides native integrations for every major TypeScript framework. The sam
147148
:::
148149
:::card
149150
---
151+
icon: i-custom-lambda
152+
title: AWS Lambda
153+
to: /frameworks/aws-lambda
154+
color: neutral
155+
---
156+
`initLogger` once per runtime; `createLogger` per invocation (SQS, events, HTTP API).
157+
:::
158+
:::card
159+
---
150160
icon: i-simple-icons-typescript
151161
title: Standalone
152162
to: /frameworks/standalone
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: AWS Lambda
3+
description: Wide events and structured logging in AWS Lambda functions, including SQS consumers and event-driven handlers.
4+
navigation:
5+
title: AWS Lambda
6+
icon: i-custom-lambda
7+
---
8+
9+
AWS Lambda has **no HTTP middleware lifecycle** like Nuxt or Express, so evlog behaves like [standalone TypeScript](/frameworks/standalone): call `initLogger()` once, create a logger **per invocation** (or per SQS message) with `createLogger()`, then call `log.emit()` when work finishes.
10+
11+
::code-collapse
12+
13+
```txt [Prompt]
14+
Set up evlog in an AWS Lambda function (e.g. SQS consumer).
15+
16+
- Install evlog: pnpm add evlog
17+
- Call initLogger({ env: { service: 'my-fn' } }) once at module load (cold start)
18+
- In the handler, create a new createLogger({ messageId, ... }) per invocation or per message
19+
- Use log.set() to accumulate context; call log.emit() when done
20+
- Avoid a single module-level logger instance reused across invocations (Lambda reuses runtimes)
21+
22+
Docs: https://www.evlog.dev/frameworks/aws-lambda
23+
Adapters: https://www.evlog.dev/adapters
24+
```
25+
26+
::
27+
28+
## Why not one global `createLogger`?
29+
30+
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.
31+
32+
**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.
33+
34+
**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.
35+
36+
## Quick Start
37+
38+
### 1. Install
39+
40+
```bash [Terminal]
41+
bun add evlog
42+
```
43+
44+
### 2. Initialize once, log per invocation
45+
46+
```typescript [src/handler.ts]
47+
import type { SQSEvent } from 'aws-lambda'
48+
import { initLogger, createLogger } from 'evlog'
49+
50+
initLogger({
51+
env: { service: 'sqs-consumer', environment: process.env.NODE_ENV },
52+
})
53+
54+
export async function handler(event: SQSEvent) {
55+
for (const record of event.Records) {
56+
const log = createLogger({
57+
messageId: record.messageId,
58+
approximateReceiveCount: record.attributes?.ApproximateReceiveCount,
59+
})
60+
61+
try {
62+
log.set({ queue: { name: record.eventSourceARN } })
63+
// … parse record.body and process the message
64+
log.set({ status: 'ok' })
65+
} catch (error) {
66+
log.error(error instanceof Error ? error : new Error(String(error)))
67+
log.set({ status: 'error' })
68+
throw error
69+
} finally {
70+
log.emit()
71+
}
72+
}
73+
}
74+
```
75+
76+
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.
77+
78+
## Stdout and `silent`
79+
80+
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](/core-concepts/configuration#silent-mode).
81+
82+
```typescript [src/handler.ts]
83+
import { createAxiomDrain } from 'evlog/axiom'
84+
import { initLogger } from 'evlog'
85+
86+
initLogger({
87+
env: { service: 'sqs-consumer' },
88+
silent: process.env.NODE_ENV === 'production',
89+
drain: createAxiomDrain(),
90+
})
91+
```
92+
93+
::callout{icon="i-lucide-alert-triangle" color="warning"}
94+
If `silent` is enabled without a `drain`, events may not be visible anywhere. See the configuration docs for details.
95+
::
96+
97+
## Error handling
98+
99+
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.
100+
101+
```typescript [src/handler.ts]
102+
import { createError } from 'evlog'
103+
104+
throw createError({
105+
message: 'Invalid payload',
106+
status: 400,
107+
why: 'Required field missing',
108+
fix: 'Include orderId in the message body',
109+
})
110+
```
111+
112+
## Related
113+
114+
- [Standalone TypeScript](/frameworks/standalone): same `initLogger` + `createLogger` + `emit()` model
115+
- [Configuration](/core-concepts/configuration): `silent`, `env.region` (`AWS_REGION`), drains
116+
- [Wide Events](/logging/wide-events): designing one comprehensive event per unit of work

0 commit comments

Comments
 (0)