> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bronto.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Serverless Logs and Traces with ADOT Lambda Layer

> Instrument AWS Lambda functions with the AWS Distro for OpenTelemetry (ADOT) Lambda Layer to send serverless logs and distributed traces directly to Bronto.

## When to Use the ADOT Lambda Layer

The ADOT Lambda Layer is the **recommended** Lambda ingestion path when your function runtime supports the OpenTelemetry SDK. It is a good fit when you want to:

* Send **logs and traces** from AWS Lambda directly to Bronto via OTLP, without going through CloudWatch
* Avoid CloudWatch ingestion fees on Lambda log volume
* Use the same `service.name` / `service.namespace` routing across Lambda, ECS, and EKS workloads

<Info>
  The layer captures logs emitted through the OpenTelemetry logging API in your function code (for example, via the OTel SDK or a bridged framework logger). Lambda's automatic `stdout` / `stderr` capture still goes to CloudWatch — to also ship those, use the [CloudWatch Log Forwarder](./aws-client-cloudwatch) alongside the layer, or route your application logger through OTel.
</Info>

***

## Supported AWS Services

The ADOT Lambda Layer is Lambda-specific:

| Service    | Data types                                                                |
| ---------- | ------------------------------------------------------------------------- |
| AWS Lambda | Function logs (via OTel SDK) and traces (auto-instrumented via the layer) |

Supported runtimes are listed in the [ADOT Lambda documentation](https://aws-otel.github.io/docs/getting-started/lambda) — currently Node.js, Python, Java, .NET, and Go. For runtimes without OTel SDK support (Ruby, custom runtimes), use the [CloudWatch Log Forwarder](./aws-client-cloudwatch). For non-Lambda compute, see [ADOT — ECS / EKS](./aws-adot) or the [overview](./aws-overview).

***

## What is the ADOT Lambda Layer?

The [ADOT Lambda Layer](https://aws-otel.github.io/docs/getting-started/lambda) is an AWS Lambda Layer that wraps your function with an embedded OpenTelemetry Collector. It auto-instruments your function's runtime and exports logs and traces to any OTLP-compatible backend — including Bronto — without requiring code changes beyond adding the layer and setting environment variables.

***

## Bronto OTLP Endpoints

| Region | Logs Endpoint                            | Traces Endpoint                            |
| ------ | ---------------------------------------- | ------------------------------------------ |
| EU     | `https://ingestion.eu.bronto.io/v1/logs` | `https://ingestion.eu.bronto.io/v1/traces` |
| US     | `https://ingestion.us.bronto.io/v1/logs` | `https://ingestion.us.bronto.io/v1/traces` |

<Note>
  The `/v1/logs` and `/v1/traces` endpoints accept **OTLP protobuf only**. The embedded OTel Collector inside the ADOT Lambda Layer handles protobuf serialisation automatically. Do not attempt to POST plain JSON or HTTP requests directly to these paths.
</Note>

All requests require the header:

```
x-bronto-api-key: <YOUR_API_KEY>
```

See [API Keys](/Account-Management/API-Keys) for how to generate a key.

***

## Setup

### Step 1 — Add the ADOT Lambda Layer

Add the ADOT managed Lambda Layer to your function. Layer ARNs are region- and architecture-specific. Find the latest ARN for your runtime in the [official ADOT Lambda Layer documentation](https://aws-otel.github.io/docs/getting-started/lambda#find-your-layer-arn).

Via AWS CLI:

```bash theme={"dark"}
aws lambda update-function-configuration \
  --function-name <YOUR_FUNCTION_NAME> \
  --layers <ADOT_LAYER_ARN>
```

Via Terraform:

```hcl theme={"dark"}
resource "aws_lambda_function" "example" {
  # ... other config ...
  layers = ["<ADOT_LAYER_ARN>"]
}
```

### Step 2 — Configure the collector to export to Bronto

Set the `OPENTELEMETRY_COLLECTOR_CONFIG_URI` environment variable to point to a collector config stored in S3 or SSM Parameter Store, or use inline configuration.

Minimal collector config:

```yaml theme={"dark"}
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:

exporters:
  otlphttp/bronto:
    logs_endpoint: "https://ingestion.<REGION>.bronto.io/v1/logs"
    traces_endpoint: "https://ingestion.<REGION>.bronto.io/v1/traces"
    compression: none
    headers:
      x-bronto-api-key: <YOUR_API_KEY>

service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/bronto]
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/bronto]
```

### Step 3 — Set environment variables on your Lambda function

| Variable                             | Value                                                             |
| ------------------------------------ | ----------------------------------------------------------------- |
| `OPENTELEMETRY_COLLECTOR_CONFIG_URI` | S3 URI or SSM path to your collector config                       |
| `AWS_LAMBDA_EXEC_WRAPPER`            | `/opt/otel-handler` (or runtime-specific wrapper — see ADOT docs) |

### Step 4 — Instrument your application code

Add the OTel SDK for your runtime and configure it to export to the local collector on `localhost:4317`. For per-language setup instructions, see the [OpenTelemetry SDK guides](/opentelemetry/overview).

***

## Data Organization

The Lambda Layer uses OpenTelemetry resource attributes for routing. Bronto maps `service.name` to a **Dataset** and `service.namespace` to a **Collection** — see [Data Organization](/Search-and-Visualize/Partitions) for how datasets, collections, and tags work.

Set these via Lambda environment variables — the OTel SDK reads them automatically:

| Variable                   | Maps to                                                                     |
| -------------------------- | --------------------------------------------------------------------------- |
| `OTEL_SERVICE_NAME`        | Dataset (`service.name`)                                                    |
| `OTEL_RESOURCE_ATTRIBUTES` | Additional attributes — set `service.namespace=<YOUR_COLLECTION_NAME>` here |

You can also override routing in the collector config using HTTP headers on the exporter:

| Header                | Description                              |
| --------------------- | ---------------------------------------- |
| `x-bronto-dataset`    | Overrides `service.name`                 |
| `x-bronto-collection` | Overrides `service.namespace`            |
| `x-bronto-tags`       | Comma-separated tags to attach to events |

```yaml theme={"dark"}
exporters:
  otlphttp/bronto:
    traces_endpoint: "https://ingestion.<REGION>.bronto.io/v1/traces"
    headers:
      x-bronto-api-key: <YOUR_API_KEY>
      x-bronto-dataset: <YOUR_DATASET_NAME>
      x-bronto-collection: <YOUR_COLLECTION_NAME>
      x-bronto-tags: env=prod,function=<FUNCTION_NAME>
```

***

## Cost Notes

* The ADOT Lambda Layer itself is free.
* Logs and traces sent through the layer bypass CloudWatch entirely — no CloudWatch ingestion fees.
* Lambda's automatic `stdout` / `stderr` capture still writes to CloudWatch by default. To stop paying for that, route your application logger through the OTel SDK; to keep both paths, add the [CloudWatch Log Forwarder](./aws-client-cloudwatch) alongside the layer.

***

For assistance, contact [support@bronto.io](mailto:support@bronto.io).
