Documentation
¶
Overview ¶
Package logging is the Powertools structured logger for Lambda, the Go side of Powertools for AWS Lambda (Python)'s Logger: JSON records with the Lambda context, cold start, correlation ids, debug sampling, per-invocation keys and log buffering, built on log/slog.
Getting started ¶
A Logger is a *slog.Logger with the Powertools helpers. Create it once and log with slog's Context methods, which give the record its invocation:
var logger = logging.New(logging.WithService("payment"))
func handle(ctx context.Context, order Order) (Receipt, error) {
logger.InfoContext(ctx, "collecting payment", "order_id", order.ID)
...
}
func main() {
lambda.Start(logger.InjectContext(handle))
}
Every record is one line of JSON on standard output:
{"level":"INFO","location":"main.handle:21","message":"collecting payment",
"timestamp":"2026-10-10T12:00:00.123Z","service":"payment","cold_start":true,
"function_name":"payment","function_memory_size":128,
"function_arn":"arn:aws:lambda:eu-west-1:123456789012:function:payment",
"function_request_id":"52fdfc07-2182-154f-163f-5f0f9a621d72","order_id":"o-1",
"xray_trace_id":"1-5759e988-bd862e3fe1be46a994272793"}
The keys are Powertools Python's, so that Go and Python functions share log queries: level, location, message, timestamp, service, sampling_rate, cold_start, function_name, function_memory_size, function_arn, function_request_id, correlation_id, exception, exception_name, stack_trace, xray_trace_id (the Key constants).
The Handler underneath is a plain slog.Handler: pass it to slog.SetDefault so that libraries logging through slog's default logger (and the standard log package) write Powertools records too, or to any library that takes a slog.Handler, such as the durable functions SDK (durable.WithLogHandler(logger.Handler())).
The invocation is in the context ¶
Lambda Managed Instances run several invocations at once in one process, so nothing that belongs to an invocation lives in the Logger. A record takes it from the context it is logged with:
- function_request_id and function_arn from aws-lambda-go's Lambda context, function_name and function_memory_size from the environment, cold_start from the first invocation the process serves (never with provisioned concurrency);
- xray_trace_id from the active OpenTelemetry span, else from the trace header aws-lambda-go stores in the context, else from _X_AMZN_TRACE_ID (set by aws-lambda-go only when it runs one invocation at a time);
- the keys of AppendKeys and WithKeys, the correlation id, the sampling decision and the log buffer, from the state InjectContext puts there.
A record logged without the invocation's context (logger.Info instead of logger.InfoContext) has none of these.
Keys ¶
Keys that every record of a logger carries are slog's With: logger.With("tenant", "acme"). Keys of one invocation go in its context:
- AppendKeys(ctx, "order_id", id) adds keys to the rest of the invocation (Python's append_keys); RemoveKeys, ClearState and CurrentKeys go with it;
- WithKeys(ctx, "step", "charge") returns a context whose records carry the keys (Python's append_context_keys);
- SetCorrelationID and CorrelationID set and read correlation_id.
Keys behave as in a Python dict: a key appears once, a later value replaces an earlier one, and a nil value removes the key. The record's own attributes win over the logger's, which win over the invocation's, which win over the Powertools keys; level, location, message and timestamp cannot be overridden.
An error under the "err" key (ErrorKey) is written as exception (its message) and exception_name (the name of its type, the first one in the wrapped chain that is not the standard library's), plus stack_trace when it carries its stack (StackTracer):
logger.ErrorContext(ctx, "charge failed", "err", err)
InjectContext ¶
InjectContext wraps the handler with the per-invocation work of Python's inject_lambda_context: the invocation's state, the event logged (WithLogEvent, POWERTOOLS_LOGGER_LOG_EVENT), the correlation id selected with JMESPath (WithCorrelationIDPath and the Correlation constants), the buffer flushed when the handler fails (WithFlushBufferOnUncaughtError), a panic logged with its stack (WithLogUncaughtPanics) before it goes on.
Levels and sampling ¶
The level is, in Powertools Python's order: AWS_LAMBDA_LOG_LEVEL (Lambda's Advanced Logging Controls, which drop what is below them anyway), then WithLevel or SetLevel, then POWERTOOLS_LOG_LEVEL, then LOG_LEVEL, then INFO. A configured level more verbose than Lambda's gives a warning. LevelTrace and LevelFatal (alias LevelCritical) complete slog's levels.
WithSamplingRate (POWERTOOLS_LOGGER_SAMPLE_RATE) logs at DEBUG a fraction of the invocations: InjectContext draws the decision for each one, and the records carry the rate in sampling_rate.
Buffering ¶
WithBuffer keeps the records at or below a level (DEBUG by default) in a per-invocation buffer instead of writing them, and writes them, with their original time and location, only when the invocation logs an ERROR (by default), when the handler fails (WithFlushBufferOnUncaughtError) or when FlushBuffer is called. When the buffer is full the oldest records make room; a warning says so when it is flushed. Records logged outside an invocation are never buffered.
Formatting ¶
Records carry Lambda's level names and RFC 3339 timestamps with milliseconds in UTC, which Lambda's log-level filtering needs. The other knobs are options: WithLevelNames(PythonLevelName) and WithTimestampFormat(TimestampPython) for Python's output, WithUTC, WithRecordOrder, WithKeyName, WithoutKeys, WithReplaceAttr, WithIndent (four spaces when POWERTOOLS_DEV is on), WithJSONOptions. Python's custom formatters map to these options and to wrapping the Handler in a slog.Handler of your own.
Environment variables ¶
POWERTOOLS_SERVICE_NAME service, default "service_undefined" POWERTOOLS_LOG_LEVEL level, then LOG_LEVEL; default INFO AWS_LAMBDA_LOG_LEVEL Lambda's Advanced Logging Controls, highest precedence POWERTOOLS_LOGGER_SAMPLE_RATE debug sampling rate, 0 to 1 POWERTOOLS_LOGGER_LOG_EVENT log the event in InjectContext, default false POWERTOOLS_DEV indented records AWS_LAMBDA_FUNCTION_NAME, AWS_LAMBDA_FUNCTION_MEMORY_SIZE, _X_AMZN_TRACE_ID, TZ
An option always wins over its variable. A variable with an invalid value is ignored with a warning; it never stops the function. Warnings, like Python's, are JSON records at WARN on standard error, once per cause.
Differences from Powertools Python ¶
- Lambda level names (WARN, FATAL) and RFC 3339 timestamps by default, since Lambda's log-level filtering files records with Python's names or timestamp format under INFO; Python's are options.
- Per-invocation state is in the context. Python's append_keys keys persist across warm invocations; here they end with the invocation, and WithKeys restores the values it shadowed instead of deleting the keys. clear_state is therefore only needed to drop keys a wrapping middleware added (WithClearState).
- The Lambda context keys come from each record's context, with or without InjectContext; cold_start appears with them.
- Buffers belong to the invocation, not to the trace id: Python drops bufferable records logged without a trace id (outside Lambda, with concurrency above one), here they are written. A buffered record keeps the keys it had when it was logged, and its size is that of its JSON.
- The X-Ray trace id comes from the context first, as aws-lambda-go does not set _X_AMZN_TRACE_ID with concurrent invocations.
- Messages are strings, as in slog: Python's decoding of JSON-looking messages is dropped; put structured data in attributes. The event that WithLogEvent logs is still the record's message, as a JSON object.
- sampling_rate is always a number (Python writes the variable's text).
- exception is the error's message: Go errors have no traceback; stack_trace frames have no source statement, and exception_notes does not exist.
- Uncaught exceptions are panics: WithLogUncaughtPanics logs them from InjectContext, where Python's sys.excepthook seldom runs in Lambda. Returned errors are not logged, as Python does not log exceptions that propagate.
- The function name and memory size are left out when unknown (outside Lambda), where Python writes "UNDEFINED" and 128 for a missing context.
- Python's stdlib logging plumbing has no Go counterpart and is not ported: child loggers and logger reuse by service name (share one Logger, or use With), handler and filter management and attribute proxying, structure_logs, the deduplication filter and POWERTOOLS_LOG_DEDUPLICATION_DISABLED, %-format overrides of reserved keys, use_datetime_directive (Go has one layout syntax), copy_config_to_registered_loggers (slog.SetDefault redirects slog's and the log package's default loggers), set_package_logger (Powertools diagnostics follow POWERTOOLS_DEBUG), and InvalidLoggerSamplingRateError (the rate is a float64; an invalid variable warns).
Index ¶
- Constants
- Variables
- func AppendKeys(ctx context.Context, args ...any)
- func ClearState(ctx context.Context)
- func CorrelationID(ctx context.Context) string
- func CurrentKeys(ctx context.Context) []slog.Attr
- func LambdaLevelName(level slog.Level) string
- func ParseLevel(name string) (slog.Level, error)
- func PythonLevelName(level slog.Level) string
- func RemoveKeys(ctx context.Context, keys ...string)
- func SetCorrelationID(ctx context.Context, id string)
- func WithKeys(ctx context.Context, args ...any) context.Context
- type BufferOption
- type Handler
- func (h *Handler) ClearBuffer(ctx context.Context)
- func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool
- func (h *Handler) FlushBuffer(ctx context.Context) error
- func (h *Handler) Handle(ctx context.Context, r slog.Record) error
- func (h *Handler) Level() slog.Level
- func (h *Handler) RefreshSampleRateCalculation(ctx context.Context)
- func (h *Handler) Service() string
- func (h *Handler) SetLevel(level slog.Level)
- func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *Handler) WithGroup(name string) slog.Handler
- type InjectOption
- type Logger
- func (l *Logger) ClearBuffer(ctx context.Context)
- func (l *Logger) FlushBuffer(ctx context.Context) error
- func (l *Logger) InjectContext[E, R any](next middleware.Handler[E, R], opts ...InjectOption) middleware.Handler[E, R]
- func (l *Logger) Level() slog.Level
- func (l *Logger) RefreshSampleRateCalculation(ctx context.Context)
- func (l *Logger) Service() string
- func (l *Logger) SetLevel(level slog.Level)
- func (l *Logger) With(args ...any) *Logger
- func (l *Logger) WithGroup(name string) *Logger
- type Option
- func WithBuffer(opts ...BufferOption) Option
- func WithClock(now func() time.Time) Option
- func WithIndent(indent string) Option
- func WithJSONOptions(opts ...json.Options) Option
- func WithKeyName(key, name string) Option
- func WithLevel(level slog.Level) Option
- func WithLevelNames(name func(slog.Level) string) Option
- func WithLogUncaughtPanics(enabled bool) Option
- func WithRandom(random func() float64) Option
- func WithRecordOrder(keys ...string) Option
- func WithReplaceAttr(fn func(groups []string, a slog.Attr) slog.Attr) Option
- func WithSamplingRate(rate float64) Option
- func WithService(name string) Option
- func WithStackTrace(enabled bool) Option
- func WithTimestampFormat(layout string) Option
- func WithUTC(utc bool) Option
- func WithWriter(w io.Writer) Option
- func WithoutKeys(keys ...string) Option
- type StackTracer
Examples ¶
Constants ¶
const ( // KeyLevel holds the record's level name. KeyLevel = "level" // KeyLocation holds where the record was logged: function:line. KeyLocation = "location" // KeyMessage holds the message. KeyMessage = "message" // KeyTimestamp holds when the record was logged. KeyTimestamp = "timestamp" // KeyService holds the service name. KeyService = "service" // KeySamplingRate holds the debug sampling rate, when one is set. KeySamplingRate = "sampling_rate" // KeyColdStart tells whether the invocation is the cold one. KeyColdStart = "cold_start" // KeyFunctionName holds the Lambda function's name. KeyFunctionName = "function_name" // KeyFunctionMemorySize holds the memory configured for the function, in MB. KeyFunctionMemorySize = "function_memory_size" // KeyFunctionARN holds the ARN the function was invoked with. KeyFunctionARN = "function_arn" // KeyFunctionRequestID holds the invocation's request id. KeyFunctionRequestID = "function_request_id" // KeyCorrelationID holds the correlation id of the invocation. KeyCorrelationID = "correlation_id" // KeyXRayTraceID holds the X-Ray trace id of the invocation. KeyXRayTraceID = "xray_trace_id" // KeyException holds the logged error's message. KeyException = "exception" // KeyExceptionName holds the logged error's type name. KeyExceptionName = "exception_name" // KeyStackTrace holds the stack of a panic, or of an error that carries one. KeyStackTrace = "stack_trace" )
The keys of a Powertools log record, named as Powertools Python names them, so that a query written for a Python function reads a Go one too.
const ( CorrelationAPIGatewayREST = "requestContext.requestId" CorrelationAPIGatewayHTTP = CorrelationAPIGatewayREST CorrelationAppSyncAuthorizer = "requestContext.requestId" CorrelationAppSyncResolver = `request.headers."x-amzn-trace-id"` CorrelationApplicationLoadBalancer = `headers."x-amzn-trace-id"` CorrelationEventBridge = "id" CorrelationLambdaFunctionURL = CorrelationAPIGatewayREST CorrelationS3ObjectLambda = "xAmzRequestId" CorrelationVPCLattice = `headers."x-amzn-trace-id"` )
The JMESPath expressions that select the correlation id of the common event sources, Powertools Python's correlation_paths. Pass one to WithCorrelationIDPath.
const ( LevelTrace slog.Level = slog.LevelDebug - 4 LevelFatal slog.Level = slog.LevelError + 4 LevelCritical = LevelFatal )
The levels slog lacks and Lambda knows. LevelCritical is Powertools Python's name for LevelFatal.
const ( // TimestampRFC3339Milli is the default: RFC 3339 with milliseconds, the // format Lambda's log-level filtering requires of a JSON record's // timestamp (it files a record without one under INFO). TimestampRFC3339Milli = "2006-01-02T15:04:05.000Z07:00" // TimestampPython is Powertools Python's default, 2021-05-03 // 11:47:12,494+0000. Lambda's log-level filtering does not read it. TimestampPython = "2006-01-02 15:04:05,000-0700" )
Timestamp layouts for WithTimestampFormat.
const ErrorKey = "err"
ErrorKey is the attribute key that carries the error of a record:
logger.ErrorContext(ctx, "charge failed", "err", err)
An error under this key, outside any group, is written as the exception, exception_name and, when a stack is known, stack_trace keys.
Variables ¶
var ErrInvalidLevel = errors.New("invalid log level")
ErrInvalidLevel is returned by ParseLevel for a name it does not know.
Functions ¶
func AppendKeys ¶
AppendKeys adds keys to every record of the invocation ctx belongs to, from now until it ends, whoever logs them: Powertools Python's append_keys, scoped to the invocation. The arguments are key-value pairs or slog.Attr values, as slog.Logger.With takes them. A key that is already there takes the new value; a nil value hides the key.
The keys live in the state InjectContext puts in the context; outside an invocation wrapped by InjectContext there is nowhere to keep them, and AppendKeys does nothing. Use slog's With for keys every record of a logger carries, and WithKeys for keys bound to a context.
func ClearState ¶
ClearState removes every key added by AppendKeys and the correlation id from the invocation ctx belongs to: Powertools Python's clear_state.
func CorrelationID ¶
CorrelationID returns the correlation id of the invocation ctx belongs to, or "" when it has none.
func CurrentKeys ¶
CurrentKeys returns the keys ctx adds to records: the correlation id, the keys of AppendKeys, then those of WithKeys. Powertools Python's get_current_keys; the slice is a copy.
func LambdaLevelName ¶
LambdaLevelName names a level the way Lambda's log-level filtering reads it: TRACE, DEBUG, INFO, WARN, ERROR or FATAL. A level between two names takes the lower one (INFO+2 is INFO), since Lambda files an unknown name under INFO, which would let an ERROR+2 record through a WARN filter as INFO and drop it.
func ParseLevel ¶
ParseLevel reads a level name, in any case: the Lambda names (TRACE, DEBUG, INFO, WARN, ERROR, FATAL), the Python ones (WARNING, CRITICAL, NOTSET, which logs everything), and slog's offsets such as "INFO+2".
func PythonLevelName ¶
PythonLevelName names a level the way Powertools Python does: DEBUG, INFO, WARNING, ERROR or CRITICAL, and TRACE below DEBUG. Pass it to WithLevelNames when Go and Python functions share log queries on level names. Lambda's log-level filtering does not know WARNING and CRITICAL.
func RemoveKeys ¶
RemoveKeys removes keys added by AppendKeys from the invocation ctx belongs to. Keys bound by WithKeys stay with their context.
func SetCorrelationID ¶
SetCorrelationID sets the correlation_id key of the invocation ctx belongs to; an empty id removes it. Like AppendKeys, it needs InjectContext.
func WithKeys ¶
WithKeys returns a context whose records carry keys, on top of those of ctx: Powertools Python's append_context_keys. The keys end with the returned context, and the values they shadowed come back: unlike Python, leaving the scope never loses a key that existed before it.
Example ¶
package main
import (
"context"
"fmt"
"strings"
"time"
"github.com/alessandrolattao/powertools-lambda-go/logging"
)
// clock makes the examples' timestamps stable.
func clock() time.Time { return time.Date(2026, 10, 10, 12, 0, 0, 123_000_000, time.UTC) }
func main() {
var out strings.Builder
logger := logging.New(logging.WithService("orders"), logging.WithWriter(&out), logging.WithClock(clock),
logging.WithoutKeys(logging.KeyLocation))
ctx := logging.WithKeys(context.Background(), "step", "charge")
logger.InfoContext(ctx, "charging")
logger.InfoContext(context.Background(), "done")
fmt.Print(out.String())
}
Output: {"level":"INFO","message":"charging","timestamp":"2026-10-10T12:00:00.123Z","service":"orders","step":"charge"} {"level":"INFO","message":"done","timestamp":"2026-10-10T12:00:00.123Z","service":"orders"}
Types ¶
type BufferOption ¶
type BufferOption func(*bufferConfig)
BufferOption configures WithBuffer.
func BufferAtVerbosity ¶
func BufferAtVerbosity(level slog.Level) BufferOption
BufferAtVerbosity sets the most severe level buffered: DEBUG (the default), INFO or WARN, and every level below it. ERROR and above are never buffered.
func BufferFlushOnErrorLog ¶
func BufferFlushOnErrorLog(enabled bool) BufferOption
BufferFlushOnErrorLog controls whether a record at ERROR or above flushes the buffer before it is written. It is on by default.
func BufferMaxBytes ¶
func BufferMaxBytes(n int) BufferOption
BufferMaxBytes sets how many bytes of records an invocation buffers. When a record does not fit, the oldest ones make room for it; a record larger than the whole buffer is written at once. It defaults to 20480.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler is a slog.Handler that writes Powertools JSON records: one JSON object per line, with the Powertools keys, the Lambda context of the invocation the record belongs to, and the keys, sampling decision and buffer of that invocation. Create it with NewHandler; use it with slog.New, slog.SetDefault, or any library that takes a slog.Handler.
A Handler is safe for concurrent use. The handlers WithAttrs and WithGroup return share its configuration, level and buffers.
func NewHandler ¶
NewHandler returns a Handler configured by opts, then by the POWERTOOLS_* environment variables, then by Powertools' defaults.
Example ¶
package main
import (
"fmt"
"log/slog"
"strings"
"time"
"github.com/alessandrolattao/powertools-lambda-go/logging"
)
// clock makes the examples' timestamps stable.
func clock() time.Time { return time.Date(2026, 10, 10, 12, 0, 0, 123_000_000, time.UTC) }
func main() {
var out strings.Builder
handler := logging.NewHandler(logging.WithService("orders"), logging.WithWriter(&out), logging.WithClock(clock),
logging.WithoutKeys(logging.KeyLocation))
// slog.SetDefault(slog.New(handler)) sends slog's default logger, and the
// standard log package, through Powertools.
logger := slog.New(handler)
logger.Info("from slog", "library", "any")
fmt.Print(out.String())
}
Output: {"level":"INFO","message":"from slog","timestamp":"2026-10-10T12:00:00.123Z","service":"orders","library":"any"}
func (*Handler) ClearBuffer ¶
ClearBuffer drops the records buffered for the invocation ctx belongs to: Powertools Python's clear_buffer.
func (*Handler) Enabled ¶
Enabled reports whether a record at level would be written, or buffered, in the invocation ctx belongs to.
func (*Handler) FlushBuffer ¶
FlushBuffer writes the records buffered for the invocation ctx belongs to, and empties the buffer: Powertools Python's flush_buffer.
func (*Handler) RefreshSampleRateCalculation ¶
RefreshSampleRateCalculation draws the sampling decision again: for the invocation ctx belongs to, or outside any invocation, for the records logged outside one. InjectContext draws a decision for every invocation; call this when you do not use it.
func (*Handler) SetLevel ¶
SetLevel changes the minimum level, with the precedence NewHandler applies: Lambda's Advanced Logging Controls still win, as with Powertools Python's setLevel.
type InjectOption ¶
type InjectOption func(*injectConfig)
InjectOption configures InjectContext.
func WithClearState ¶
func WithClearState(enabled bool) InjectOption
WithClearState starts every invocation without the keys a middleware wrapping this one put in its context (AppendKeys, WithKeys, the correlation id): Powertools Python's clear_state. Keys appended during an invocation never reach the next one, with or without it.
func WithCorrelationIDPath ¶
func WithCorrelationIDPath(expression string) InjectOption
WithCorrelationIDPath sets the correlation id of every invocation to what the JMESPath expression selects in the event; the Correlation constants cover the common event sources. The Powertools JMESPath functions are available. An expression that does not compile fails every invocation, as in Powertools Python: it is a programming error. An event the expression cannot be evaluated against (a payload that is not JSON the way the expression expects) leaves the invocation without a correlation id: a logging feature never stops a function, and the handler's own decoding reports a malformed event better.
func WithFlushBufferOnUncaughtError ¶
func WithFlushBufferOnUncaughtError(enabled bool) InjectOption
WithFlushBufferOnUncaughtError flushes the invocation's log buffer when the handler returns an error or panics, so that the records leading to the failure are written.
func WithLogEvent ¶
func WithLogEvent(enabled bool) InjectOption
WithLogEvent logs every event the handler receives, at INFO, as the record's message. It defaults to POWERTOOLS_LOGGER_LOG_EVENT, which is off: events often carry personal data.
Example ¶
package main
import (
"context"
"fmt"
"strings"
"time"
"github.com/alessandrolattao/powertools-lambda-go/logging"
)
// clock makes the examples' timestamps stable.
func clock() time.Time { return time.Date(2026, 10, 10, 12, 0, 0, 123_000_000, time.UTC) }
func main() {
var out strings.Builder
logger := logging.New(logging.WithService("orders"), logging.WithWriter(&out), logging.WithClock(clock),
logging.WithoutKeys(logging.KeyLocation))
type order struct {
ID string `json:"id"`
}
handle := logger.InjectContext(func(context.Context, order) (string, error) {
return "accepted", nil
}, logging.WithLogEvent(true))
if _, err := handle(context.Background(), order{ID: "o-1"}); err != nil {
panic(err)
}
fmt.Print(out.String())
}
Output: {"level":"INFO","message":{"id":"o-1"},"timestamp":"2026-10-10T12:00:00.123Z","service":"orders"}
type Logger ¶
Logger is a *slog.Logger writing through a Handler, with the Powertools helpers next to slog's methods. Log with slog's Context methods (InfoContext, ErrorContext, ...) inside an invocation: the context is where the record finds the invocation's Lambda context, keys, sampling decision and buffer.
A Logger is safe for concurrent use; create it once, in main or at package level, and share it.
func New ¶
New returns a Logger configured by opts, then by the POWERTOOLS_* environment variables, then by Powertools' defaults.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/alessandrolattao/powertools-lambda-go/logging"
)
// clock makes the examples' timestamps stable.
func clock() time.Time { return time.Date(2026, 10, 10, 12, 0, 0, 123_000_000, time.UTC) }
func main() {
var out strings.Builder
logger := logging.New(logging.WithService("payment"), logging.WithWriter(&out), logging.WithClock(clock))
logger.InfoContext(context.Background(), "collecting payment", "charge_id", "ch_42")
logger.ErrorContext(context.Background(), "charge failed", "err", errors.New("card declined"))
fmt.Print(out.String())
}
Output: {"level":"INFO","location":"logging_test.ExampleNew:23","message":"collecting payment","timestamp":"2026-10-10T12:00:00.123Z","service":"payment","charge_id":"ch_42"} {"level":"ERROR","location":"logging_test.ExampleNew:24","message":"charge failed","timestamp":"2026-10-10T12:00:00.123Z","service":"payment","exception":"card declined","exception_name":"error"}
func (*Logger) ClearBuffer ¶
ClearBuffer drops the records buffered for the invocation ctx belongs to: Powertools Python's clear_buffer.
func (*Logger) FlushBuffer ¶
FlushBuffer writes the records buffered for the invocation ctx belongs to and empties the buffer: Powertools Python's flush_buffer.
func (*Logger) InjectContext ¶
func (l *Logger) InjectContext[E, R any](next middleware.Handler[E, R], opts ...InjectOption) middleware.Handler[E, R]
InjectContext wraps a Lambda handler with what the logger does around each invocation, Powertools Python's inject_lambda_context:
- it gives the invocation its own state in the context: the keys of AppendKeys and the correlation id, the sampling decision (drawn anew for every invocation when a sampling rate is set) and the log buffer;
- it sets the correlation id (WithCorrelationIDPath) and logs the event (WithLogEvent);
- when the handler fails, it flushes the buffer (WithFlushBufferOnUncaughtError); when it panics, it logs the panic with its stack at ERROR (WithLogUncaughtPanics), then lets the panic go on;
- it drops what is left in the buffer when the invocation ends.
The Lambda context keys (function_request_id, cold_start, ...) come from the context of each record, with or without InjectContext. An error the handler returns is not logged: Lambda records it, and the handler knows best which errors deserve a record.
lambda.Start(logger.InjectContext(handle, logging.WithCorrelationIDPath(logging.CorrelationAPIGatewayHTTP)))
Example ¶
package main
import (
"context"
"fmt"
"strings"
"time"
"github.com/aws/aws-lambda-go/events"
"github.com/alessandrolattao/powertools-lambda-go/logging"
)
// clock makes the examples' timestamps stable.
func clock() time.Time { return time.Date(2026, 10, 10, 12, 0, 0, 123_000_000, time.UTC) }
func main() {
var out strings.Builder
logger := logging.New(logging.WithService("orders"), logging.WithWriter(&out), logging.WithClock(clock),
logging.WithoutKeys(logging.KeyLocation))
handle := func(ctx context.Context, request events.APIGatewayV2HTTPRequest) (events.APIGatewayV2HTTPResponse, error) {
logging.AppendKeys(ctx, "route", request.RouteKey)
logger.InfoContext(ctx, "listing orders")
return events.APIGatewayV2HTTPResponse{StatusCode: 200}, nil
}
// In main: lambda.Start(logger.InjectContext(handle, ...)).
wrapped := logger.InjectContext(handle, logging.WithCorrelationIDPath(logging.CorrelationAPIGatewayHTTP))
_, err := wrapped(context.Background(), events.APIGatewayV2HTTPRequest{
RouteKey: "GET /orders",
RequestContext: events.APIGatewayV2HTTPRequestContext{RequestID: "r-1"},
})
if err != nil {
panic(err)
}
fmt.Print(out.String())
}
Output: {"level":"INFO","message":"listing orders","timestamp":"2026-10-10T12:00:00.123Z","service":"orders","correlation_id":"r-1","route":"GET /orders"}
func (*Logger) Level ¶
Level returns the minimum level of the records written, sampling aside: Powertools Python's log_level.
func (*Logger) RefreshSampleRateCalculation ¶
RefreshSampleRateCalculation draws the sampling decision again for the invocation ctx belongs to, or for the records logged outside invocations: Powertools Python's refresh_sample_rate_calculation. InjectContext draws a decision for every invocation on its own.
func (*Logger) Service ¶
Service returns the service name the records carry: Powertools Python's name.
func (*Logger) SetLevel ¶
SetLevel changes the minimum level of the records written. Lambda's Advanced Logging Controls still take precedence, as with Powertools Python's setLevel.
type Option ¶
type Option func(*config)
Option configures a Handler or a Logger.
func WithBuffer ¶
func WithBuffer(opts ...BufferOption) Option
WithBuffer buffers the low-level records of each invocation instead of writing them, to flush them only when something goes wrong: Powertools Python's log buffering. Without options it buffers DEBUG records, up to 20480 bytes per invocation, and flushes on any ERROR record.
Example ¶
package main
import (
"context"
"fmt"
"strings"
"time"
"github.com/alessandrolattao/powertools-lambda-go/logging"
)
// clock makes the examples' timestamps stable.
func clock() time.Time { return time.Date(2026, 10, 10, 12, 0, 0, 123_000_000, time.UTC) }
func main() {
var out strings.Builder
logger := logging.New(logging.WithService("orders"), logging.WithWriter(&out), logging.WithClock(clock),
logging.WithoutKeys(logging.KeyLocation), logging.WithBuffer())
handle := logger.InjectContext(func(ctx context.Context, id string) (string, error) {
logger.DebugContext(ctx, "loading order", "id", id) // buffered
if id == "" {
logger.ErrorContext(ctx, "no order id") // flushes the buffer first
}
return id, nil
})
for _, id := range []string{"o-1", ""} { // the first invocation's debug record is dropped with it
if _, err := handle(context.Background(), id); err != nil {
panic(err)
}
}
fmt.Print(out.String())
}
Output: {"level":"DEBUG","message":"loading order","timestamp":"2026-10-10T12:00:00.123Z","service":"orders","id":""} {"level":"ERROR","message":"no order id","timestamp":"2026-10-10T12:00:00.123Z","service":"orders"}
func WithClock ¶
WithClock replaces the time of every record with what now returns, for tests and examples whose output must not change.
func WithIndent ¶
WithIndent writes each record over several lines, indented by indent. It defaults to four spaces when POWERTOOLS_DEV is on, and to one line per record otherwise, the shape CloudWatch Logs expects.
func WithJSONOptions ¶
WithJSONOptions passes options to encoding/json/v2 when it encodes attribute values, for example json.WithMarshalers to control how a type is written: Powertools Python's json_default. A value that cannot be encoded is written as its fmt.Sprint text.
func WithKeyName ¶
WithKeyName renames one of the keys Powertools writes (the Key constants): WithKeyName(KeyMessage, "msg").
func WithLevel ¶
WithLevel sets the minimum level of the records written. Lambda's Advanced Logging Controls (AWS_LAMBDA_LOG_LEVEL) take precedence, as in Powertools Python: Lambda drops the records below them anyway. Without this option the level comes from POWERTOOLS_LOG_LEVEL, then LOG_LEVEL, then defaults to INFO.
func WithLevelNames ¶
WithLevelNames sets how levels are named in the level key. It defaults to LambdaLevelName; PythonLevelName gives Powertools Python's names.
Example ¶
package main
import (
"context"
"fmt"
"strings"
"time"
"github.com/alessandrolattao/powertools-lambda-go/logging"
)
// clock makes the examples' timestamps stable.
func clock() time.Time { return time.Date(2026, 10, 10, 12, 0, 0, 123_000_000, time.UTC) }
func main() {
var out strings.Builder
logger := logging.New(logging.WithService("orders"), logging.WithWriter(&out), logging.WithClock(clock),
logging.WithoutKeys(logging.KeyLocation),
logging.WithLevelNames(logging.PythonLevelName), logging.WithTimestampFormat(logging.TimestampPython))
logger.WarnContext(context.Background(), "as Powertools Python writes it")
fmt.Print(out.String())
}
Output: {"level":"WARNING","message":"as Powertools Python writes it","timestamp":"2026-10-10 12:00:00,123+0000","service":"orders"}
func WithLogUncaughtPanics ¶
WithLogUncaughtPanics makes InjectContext log a panicking handler at ERROR, with the panic's stack, before the panic goes on: Powertools Python's log_uncaught_exceptions. It is off by default.
func WithRandom ¶
WithRandom replaces the source of the sampling decisions: a function returning a number in [0, 1), safe for concurrent use. Tests use it.
func WithRecordOrder ¶
WithRecordOrder sets the keys that open every record, in order: Powertools Python's log_record_order. The other keys follow in their usual order. It defaults to level, location, message, timestamp.
func WithReplaceAttr ¶
WithReplaceAttr rewrites or removes attributes before they are written, as slog.HandlerOptions.ReplaceAttr does: it sees every non-group attribute, the Powertools keys included. The level arrives as a slog.Level and the timestamp as a time.Time; returned unchanged, they are formatted as configured. Returning an empty Attr removes the attribute.
func WithSamplingRate ¶
WithSamplingRate logs at DEBUG the given fraction of invocations, from 0 (none) to 1 (all): each invocation that InjectContext wraps draws its own decision. Records carry the rate in the sampling_rate key. It defaults to POWERTOOLS_LOGGER_SAMPLE_RATE.
func WithService ¶
WithService names the service every record carries in the service key. It defaults to POWERTOOLS_SERVICE_NAME, then to "service_undefined".
func WithStackTrace ¶
WithStackTrace controls the stack_trace key of panics and of errors that carry their stack (see StackTracer). It is on by default, as Powertools Python's serialize_stacktrace.
func WithTimestampFormat ¶
WithTimestampFormat sets the time layout of the timestamp key, in the syntax of time.Format. It defaults to TimestampRFC3339Milli.
func WithUTC ¶
WithUTC writes timestamps in UTC when true, the default, and in the local time zone (the TZ variable) when false, as Powertools Python's default. Lambda runs functions in UTC unless TZ says otherwise.
func WithWriter ¶
WithWriter writes the records to w instead of standard output. Every record is written in a single Write call.
func WithoutKeys ¶
WithoutKeys leaves keys out of every record, whoever sets them: the Powertools ones (WithoutKeys(KeyLocation, KeyXRayTraceID)) or your own.
type StackTracer ¶
type StackTracer interface {
Callers() []uintptr
}
StackTracer is implemented by errors that remember where they were created, the convention of go-errors and similar packages: Callers returns program counters as runtime.Callers fills them. When the error logged under ErrorKey, or one it wraps, has such a method, the record carries the stack in its stack_trace key.