Skip to content

Commit a4e53b2

Browse files
committed
Address PR review feedback
1 parent 31a7a20 commit a4e53b2

File tree

3 files changed

+100
-68
lines changed

3 files changed

+100
-68
lines changed

lambdacontext/example_logger_test.go

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import (
1111
"github.com/aws/aws-lambda-go/lambdacontext"
1212
)
1313

14-
// ExampleLogHandler demonstrates basic usage of LogHandler for structured logging.
15-
// The handler automatically injects requestId from Lambda context into each log record.
16-
func ExampleLogHandler() {
17-
// Set up the Lambda-aware slog handler
18-
slog.SetDefault(slog.New(lambdacontext.LogHandler()))
14+
// ExampleNewLogger demonstrates the simplest usage of NewLogger for structured logging.
15+
// The logger automatically injects requestId from Lambda context into each log record.
16+
func ExampleNewLogger() {
17+
// Set up the Lambda-aware slog logger
18+
slog.SetDefault(lambdacontext.NewLogger())
1919

2020
lambda.Start(func(ctx context.Context) (string, error) {
2121
// Use slog.InfoContext to include Lambda context in logs
@@ -24,12 +24,24 @@ func ExampleLogHandler() {
2424
})
2525
}
2626

27-
// ExampleLogHandler_withFields demonstrates LogHandler with additional fields.
28-
// Use WithFields with FieldFunctionARN() and FieldTenantID() to include extra context.
29-
func ExampleLogHandler_withFields() {
27+
// ExampleNewLogHandler demonstrates using NewLogHandler for more control.
28+
func ExampleNewLogHandler() {
29+
// Set up the Lambda-aware slog handler
30+
slog.SetDefault(slog.New(lambdacontext.NewLogHandler()))
31+
32+
lambda.Start(func(ctx context.Context) (string, error) {
33+
slog.InfoContext(ctx, "processing request", "action", "example")
34+
return "success", nil
35+
})
36+
}
37+
38+
// ExampleNewLogHandler_withOptions demonstrates NewLogHandler with additional fields.
39+
// Use WithFunctionARN() and WithTenantID() to include extra context.
40+
func ExampleNewLogHandler_withOptions() {
3041
// Set up handler with function ARN and tenant ID fields
31-
slog.SetDefault(slog.New(lambdacontext.LogHandler(
32-
lambdacontext.WithFields(lambdacontext.FieldFunctionARN(), lambdacontext.FieldTenantID()),
42+
slog.SetDefault(slog.New(lambdacontext.NewLogHandler(
43+
lambdacontext.WithFunctionARN(),
44+
lambdacontext.WithTenantID(),
3345
)))
3446

3547
lambda.Start(func(ctx context.Context) (string, error) {
@@ -38,13 +50,12 @@ func ExampleLogHandler_withFields() {
3850
})
3951
}
4052

41-
// ExampleWithFields demonstrates using WithFields to include specific Lambda context fields.
42-
func ExampleWithFields() {
53+
// ExampleWithFunctionARN demonstrates using WithFunctionARN to include the function ARN.
54+
func ExampleWithFunctionARN() {
4355
// Include only function ARN
44-
handler := lambdacontext.LogHandler(
45-
lambdacontext.WithFields(lambdacontext.FieldFunctionARN()),
46-
)
47-
slog.SetDefault(slog.New(handler))
56+
slog.SetDefault(lambdacontext.NewLogger(
57+
lambdacontext.WithFunctionARN(),
58+
))
4859

4960
lambda.Start(func(ctx context.Context) (string, error) {
5061
// Log output will include "functionArn" field

lambdacontext/logger.go

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,41 @@ import (
1111
"os"
1212
)
1313

14-
// Field represents a Lambda context field to include in log records.
15-
type Field struct {
14+
// field represents a Lambda context field to include in log records.
15+
type field struct {
1616
key string
1717
value func(*LambdaContext) string
1818
}
1919

20-
// FieldFunctionARN returns a Field that includes the invoked function ARN in log records.
21-
func FieldFunctionARN() Field {
22-
return Field{"functionArn", func(lc *LambdaContext) string { return lc.InvokedFunctionArn }}
23-
}
24-
25-
// FieldTenantID returns a Field that includes the tenant ID in log records (for multi-tenant functions).
26-
func FieldTenantID() Field {
27-
return Field{"tenantId", func(lc *LambdaContext) string { return lc.TenantID }}
28-
}
29-
3020
// logOptions holds configuration for the Lambda log handler.
3121
type logOptions struct {
32-
fields []Field
22+
fields []field
3323
}
3424

3525
// LogOption is a functional option for configuring the Lambda log handler.
3626
type LogOption func(*logOptions)
3727

38-
// WithFields includes the specified fields in log records.
39-
func WithFields(fields ...Field) LogOption {
28+
// WithFunctionARN includes the invoked function ARN in log records.
29+
func WithFunctionARN() LogOption {
30+
return func(o *logOptions) {
31+
o.fields = append(o.fields, field{"functionArn", func(lc *LambdaContext) string { return lc.InvokedFunctionArn }})
32+
}
33+
}
34+
35+
// WithTenantID includes the tenant ID in log records (for multi-tenant functions).
36+
func WithTenantID() LogOption {
4037
return func(o *logOptions) {
41-
o.fields = append(o.fields, fields...)
38+
o.fields = append(o.fields, field{"tenantId", func(lc *LambdaContext) string { return lc.TenantID }})
4239
}
4340
}
4441

45-
// LogHandler returns a [slog.Handler] for AWS Lambda structured logging.
42+
// NewLogHandler returns a [slog.Handler] for AWS Lambda structured logging.
4643
// It reads AWS_LAMBDA_LOG_FORMAT and AWS_LAMBDA_LOG_LEVEL from environment,
4744
// and injects requestId from Lambda context into each log record.
4845
//
49-
// By default, only requestId is injected. Use WithFields to include more.
46+
// By default, only requestId is injected. Use WithFunctionARN or WithTenantID to include more.
5047
// See the package examples for usage.
51-
func LogHandler(opts ...LogOption) slog.Handler {
48+
func NewLogHandler(opts ...LogOption) slog.Handler {
5249
options := &logOptions{}
5350
for _, opt := range opts {
5451
opt(options)
@@ -70,6 +67,12 @@ func LogHandler(opts ...LogOption) slog.Handler {
7067
return &lambdaHandler{handler: h, fields: options.fields}
7168
}
7269

70+
// NewLogger returns a [*slog.Logger] configured for AWS Lambda structured logging.
71+
// This is a convenience function equivalent to slog.New(NewLogHandler(opts...)).
72+
func NewLogger(opts ...LogOption) *slog.Logger {
73+
return slog.New(NewLogHandler(opts...))
74+
}
75+
7376
// ReplaceAttr maps slog's default keys to AWS Lambda's log format (time->timestamp, msg->message).
7477
func ReplaceAttr(groups []string, attr slog.Attr) slog.Attr {
7578
if len(groups) > 0 {
@@ -85,16 +88,10 @@ func ReplaceAttr(groups []string, attr slog.Attr) slog.Attr {
8588
return attr
8689
}
8790

88-
// Attrs returns Lambda context fields as slog-compatible key-value pairs.
89-
// For most use cases, using [LogHandler] with slog.InfoContext is preferred.
90-
func (lc *LambdaContext) Attrs() []any {
91-
return []any{"requestId", lc.AwsRequestID}
92-
}
93-
9491
// lambdaHandler wraps a slog.Handler to inject Lambda context fields.
9592
type lambdaHandler struct {
9693
handler slog.Handler
97-
fields []Field
94+
fields []field
9895
}
9996

10097
// Enabled implements slog.Handler.

lambdacontext/logger_test.go

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,6 @@ import (
1616
"github.com/stretchr/testify/require"
1717
)
1818

19-
func TestAttrs(t *testing.T) {
20-
lc := &LambdaContext{
21-
AwsRequestID: "test-request-id",
22-
}
23-
attrs := lc.Attrs()
24-
assert.Equal(t, []any{"requestId", "test-request-id"}, attrs)
25-
}
26-
2719
func TestReplaceAttr(t *testing.T) {
2820
tests := []struct {
2921
name string
@@ -268,9 +260,15 @@ func TestLogHandler_WithFields(t *testing.T) {
268260
ReplaceAttr: ReplaceAttr,
269261
}
270262
baseHandler := slog.NewJSONHandler(&buf, opts)
263+
264+
// Create options with fields
265+
options := &logOptions{}
266+
WithFunctionARN()(options)
267+
WithTenantID()(options)
268+
271269
handler := &lambdaHandler{
272270
handler: baseHandler,
273-
fields: []Field{FieldFunctionARN(), FieldTenantID()},
271+
fields: options.fields,
274272
}
275273

276274
lc := &LambdaContext{
@@ -300,9 +298,13 @@ func TestLogHandler_WithFieldFunctionARNOnly(t *testing.T) {
300298
ReplaceAttr: ReplaceAttr,
301299
}
302300
baseHandler := slog.NewJSONHandler(&buf, opts)
301+
302+
options := &logOptions{}
303+
WithFunctionARN()(options)
304+
303305
handler := &lambdaHandler{
304306
handler: baseHandler,
305-
fields: []Field{FieldFunctionARN()},
307+
fields: options.fields,
306308
}
307309

308310
lc := &LambdaContext{
@@ -332,9 +334,14 @@ func TestLogHandler_FieldsEmpty(t *testing.T) {
332334
ReplaceAttr: ReplaceAttr,
333335
}
334336
baseHandler := slog.NewJSONHandler(&buf, opts)
337+
338+
options := &logOptions{}
339+
WithFunctionARN()(options)
340+
WithTenantID()(options)
341+
335342
handler := &lambdaHandler{
336343
handler: baseHandler,
337-
fields: []Field{FieldFunctionARN(), FieldTenantID()},
344+
fields: options.fields,
338345
}
339346

340347
lc := &LambdaContext{
@@ -356,26 +363,43 @@ func TestLogHandler_FieldsEmpty(t *testing.T) {
356363
assert.NotContains(t, logOutput, "tenantId")
357364
}
358365

359-
func TestWithFields(t *testing.T) {
366+
func TestWithFunctionARN(t *testing.T) {
360367
options := &logOptions{}
361-
WithFields(FieldFunctionARN(), FieldTenantID())(options)
368+
WithFunctionARN()(options)
362369

363-
assert.Len(t, options.fields, 2)
370+
assert.Len(t, options.fields, 1)
364371
assert.Equal(t, "functionArn", options.fields[0].key)
365-
assert.Equal(t, "tenantId", options.fields[1].key)
372+
373+
lc := &LambdaContext{InvokedFunctionArn: "arn:aws:lambda:us-east-1:123456789:function:test"}
374+
assert.Equal(t, "arn:aws:lambda:us-east-1:123456789:function:test", options.fields[0].value(lc))
366375
}
367376

368-
func TestFieldFunctions(t *testing.T) {
369-
lc := &LambdaContext{
370-
InvokedFunctionArn: "arn:aws:lambda:us-east-1:123456789:function:test",
371-
TenantID: "tenant-abc",
372-
}
377+
func TestWithTenantID(t *testing.T) {
378+
options := &logOptions{}
379+
WithTenantID()(options)
380+
381+
assert.Len(t, options.fields, 1)
382+
assert.Equal(t, "tenantId", options.fields[0].key)
383+
384+
lc := &LambdaContext{TenantID: "tenant-abc"}
385+
assert.Equal(t, "tenant-abc", options.fields[0].value(lc))
386+
}
387+
388+
func TestNewLogger(t *testing.T) {
389+
LogFormat = "JSON"
390+
LogLevel = "INFO"
391+
392+
logger := NewLogger()
393+
assert.NotNil(t, logger)
394+
}
395+
396+
func TestNewLogHandler(t *testing.T) {
397+
LogFormat = "JSON"
398+
LogLevel = "INFO"
373399

374-
arnField := FieldFunctionARN()
375-
assert.Equal(t, "functionArn", arnField.key)
376-
assert.Equal(t, "arn:aws:lambda:us-east-1:123456789:function:test", arnField.value(lc))
400+
handler := NewLogHandler()
401+
assert.NotNil(t, handler)
377402

378-
tenantField := FieldTenantID()
379-
assert.Equal(t, "tenantId", tenantField.key)
380-
assert.Equal(t, "tenant-abc", tenantField.value(lc))
403+
handlerWithOpts := NewLogHandler(WithFunctionARN(), WithTenantID())
404+
assert.NotNil(t, handlerWithOpts)
381405
}

0 commit comments

Comments
 (0)