We use cookies or similar technologies to personalize your online experience & tailor marketing to you. Many of our product features require cookies to function properly.

Read our privacy policy I accept cookies from this site

AWS Lambda Instrumentation


For serverless apps based on Lambda and similar platforms, observability can be challenging. With no server to log in to, clunky logging interfaces, and short, event-driven process runtimes, making sense of what’s going on is often difficult.

To help with this, Honeycomb has an extension that can run as a Lambda Layer, or inside a container alongside your lambda function. You can also instrument your application code with our NodeJS, Python or Go SDKs.

Honeycomb Lambda Extension  🔗

The Honeycomb Lambda Extension is designed to run alongside your lambda function. It integrates with the Lambda Logs API and receives log messages from your lambda function, which are then sent to Honeycomb as events. Structured log messages sent to stdout or stderr from your lambda function will be sent to Honeycomb as events.

The extension can be run inside a container or added as a Lambda Layer.

Overview of Lambda Extension

How it works  🔗

AWS Lambda Extensions allow you to extend the functionality of Lambda functions through configuration as Lambda Layers or run inside a container image.

Extensions run alongside the Lambda Runtime and can read data environment variables from the environment.

Once the Honeycomb Lambda Extension is configured, any structured logs emitted to stdout or stderr by your Lambda function will be parsed by the extension and sent to a dataset that you specify in Honeycomb.

Installing as a Lambda Layer  🔗

Installing the Honeycomb Lambda Extension as a Lambda Layer is currently supported for the following Lambda runtimes:

The extension can be added as a Lambda Layer with the AWS CLI tool:

$ aws lambda update-function-configuration --function-name YourFunctionName \
    --layers "arn:aws:lambda:<AWS_REGION>:702835727665:layer:honeycomb-lambda-extension:3"

Replace AWS_REGION with the appropriate value (e.g. us-east-1).

You can also use infrastructure as code tools such as Terraform:

resource "aws_lambda_function" "extensions-demo-example-lambda" {
  function_name = "HoneycombExtensionExample"
  ...
  layers = ["arn:aws:lambda:<AWS_REGION>:702835727665:layer:honeycomb-lambda-extension:3"]
}

Additionally, you’ll need to add the following environment variables to your Lambda function configuration:

As well as the following optional environment variable:

Installing in a Container Image  🔗

AWS Lambda functions can now be packaged and deployed as container images. This allows developers to leverage the flexibility and familiarity of container tooling, workflows and dependencies.

To run the Honeycomb Lambda Extension inside your container image, you must download the extension and place it in the /opt/extensions directory of your image.

FROM amazon/aws-lambda-ruby:2.7

ARG FUNCTION_DIR="/var/task"

RUN mkdir -p /opt/extensions
RUN yum install -y curl
RUN curl https://honeycomb.io/download/honeycomb-lambda-extension/v3.0.0/honeycomb-lambda-exten
sion -o /opt/extensions/honeycomb-lambda-extension

RUN mkdir -p ${FUNCTION_DIR}
COPY . ${FUNCTION_DIR}
ENV LIBHONEY_API_KEY <HONEYCOMB_API_KEY>
ENV LIBHONEY_DATASET <HONEYCOMB_DATASET>

CMD ["app.handler"]

AWS Lambda provides base images for dotnetcore2.1, dotnetcore3.1, go1.x, java8, java8.al2, java11, nodejs12.x, nodejs10.x, python3.8, python3.7, python3.6, python2.7, ruby2.5 and ruby2.7 runtimes. You can also use a custom base image, and adjust the examples accordingly.

The following is a list of base image names, locations and tags:

Java

Python

NodeJS

.NET

Ruby

Go

BYOL (Provided)

Examples  🔗

Below are some structured logging examples using some libraries we are familiar with at Honeycomb. Feel free to use your own!

Go


import (
  log "github.com/sirupsen/logrus"
)

log.SetFormatter(&log.JSONFormatter{})

func Handler(ctx context.Context) error {
  // Measure execution time
  startTime := time.Now()

  // ...

  // Get the Lambda context object
  lc, _ := lambdacontext.FromContext(ctx)

  log.WithFields(log.Fields{
      "function_name": lambdacontext.FunctionName,
      "function_version": lambdacontext.FunctionVersion,
      "request_id": lc.AwsRequestID,
      "duration_ms": time.Since(startTime).Milliseconds(),
      // other fields of interest
  }).Info("Hello World from Lambda!")
}

Python

import structlog
structlog.configure(processors=[structlog.processors.JSONRenderer()])
log = structlog.get_logger()

def handler(event, context):
    # measure execution time
    start_time = datetime.datetime.now()

    # ...

    log.msg(
        "Hello World from Lambda!",
        function_name=context.function_name,
        function_version=context.function_version,
        request_id=context.aws_request_id,
        duration_ms=(datetime.datetime.now() - start_time).total_seconds() * 1000,
        # other fields of interest
    )

JavaScript

var bunyan = require('bunyan');
var log = bunyan.createLogger();


module.exports.handler = (event, context, callback) => {
  // Measure execution time
  let startTime = Date.now();

  // ...

  log.info({
    functionName: context.functionName,
    functionVersion: context.functionVersion,
    requestId: context.awsRequestId,
    // Example fields - send anything that seems relevant!
    userId: event.UserId,
    userAction: event.UserAction,
    latencyMs: Date.now() - startTime,
  },
  'Hello World from Lambda!');
}

Note: Do not use console.log to write structured log lines in Lambda. Lambda uses a patched version of console.log, injecting extra information with each line that does not work correctly with the Honeycomb Extension Lambda Logs integration.