# Instrumentação Lambda com Serverless Framework e Elven Plugin

***

### Sumário

* Visão geral
* Pré-requisitos
* Instalação
* Quick Start
* Configuração do plugin
* Referência de opções
* Controle por função
* Variáveis de ambiente injetadas
* Layers aplicadas
* Exemplos completos
* Boas práticas
* Troubleshooting
* FAQ

***

### Visão geral

O **`elven-instrumentation-serverless-plugin`** automatiza toda a instrumentação das suas funções Lambda. Com uma única configuração no `serverless.yml`, o plugin:

1. **Adiciona as layers** de OpenTelemetry e Elven Log Extension em cada função
2. **Injeta todas as variáveis de ambiente** necessárias para traces e logs
3. **Detecta a arquitetura** (x86\_64/arm64) e aplica as layers corretas
4. **Gera nomes de serviço** padronizados automaticamente (`{service}_{stage}_{function}`)

```
┌──────────────────────────────────────────────────────────────┐
│                   serverless.yml                             │
│                                                              │
│  plugins:                                                    │
│    - elven-instrumentation-serverless-plugin                 │
│                                                              │
│  custom:                                                     │
│    elvenLayerPlugin:                                         │
│      tenant: "meu-tenant"        ──────┐                     │
│      token: "meu-token"                │                     │
│                                        ▼                     │
│                              ┌─────────────────┐             │
│                              │  Plugin injeta:  │             │
│                              │  - 2 layers      │             │
│                              │  - 14+ env vars  │             │
│                              └────────┬────────┘             │
│                                       │                      │
│  functions:                           ▼                      │
│    api:      ← layers + envs injetados automaticamente       │
│    worker:   ← layers + envs injetados automaticamente       │
│    cron:     ← layers + envs injetados automaticamente       │
└──────────────────────────────────────────────────────────────┘
```

| Componente                    | O que faz                                                           |
| ----------------------------- | ------------------------------------------------------------------- |
| **Plugin Serverless**         | Injeta layers e variáveis de ambiente automaticamente no deploy     |
| **Layer OpenTelemetry**       | Instrumenta o runtime Node.js — gera traces e spans automaticamente |
| **Layer Elven Log Extension** | Captura logs da função via Lambda Logs API e envia para Loki        |

***

### Pré-requisitos

* **Serverless Framework** v3 ou v4 instalado
* **Node.js 18+**
* **Coletor OpenTelemetry (OTLP)** implantado na sua infraestrutura (a Elven realiza o deploy do collector no seu ambiente — não é um endpoint compartilhado)
* Credenciais da Elven Observability:
  * **Tenant ID**
  * **API Token**
  * **Endpoint do seu Collector OTLP**

***

### Instalação

```bash
npm install --save-dev elven-instrumentation-serverless-plugin
```

Adicione o plugin no `serverless.yml`:

```yaml
plugins:
  - elven-instrumentation-serverless-plugin
```

Verificar instalação:

```bash
npx serverless print 2>&1 | grep -i elven
```

***

### Quick Start

#### 1. Configure o plugin no `serverless.yml`

```yaml
service: minha-api

provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1

plugins:
  - elven-instrumentation-serverless-plugin

custom:
  elvenLayerPlugin:
    tenant: "seu-tenant-id"
    token: "seu-api-token"

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: /hello
          method: get
```

#### 2. Deploy normalmente

```bash
npx serverless deploy
```

O plugin automaticamente:

* Adiciona as layers de OTel e Log Extension na função
* Injeta as variáveis de ambiente com nome de serviço `minha-api_dev_hello`
* Configura o endpoint OTLP e Loki da Elven

**Pronto!** Ao invocar a função, traces e logs já aparecem na Elven Observability.

#### Output do plugin no deploy

```
✅ [Elven] Instrumented function: minha-api_dev_hello (x86_64)
🏗️  [Elven] Architecture: x86_64
📍 [Elven] Region: us-east-1
📦 [Elven] Default layers applied:
      - arn:aws:lambda:us-east-1:184161586896:layer:opentelemetry-nodejs-0_20_0:1
      - arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension:8
```

***

### Configuração do plugin

Todas as opções ficam dentro de `custom.elvenLayerPlugin` no `serverless.yml`:

```yaml
custom:
  elvenLayerPlugin:
    # === Obrigatórios ===
    tenant: "seu-tenant-id"
    token: "seu-api-token"

    # === Opcionais ===
    region: "us-east-1"                                    # default: us-east-1
    collector_endpoint: "https://otel-collector.minha-infra.com:4318"  # URL do seu collector
    logs_endpoint: "https://loki.elvenobservability.com"   # default: endpoint Elven
    architecture: "x86_64"                                 # default: x86_64
    log_version: "8"                                       # versão da layer de logs (x86_64)
    log_version_arm64: "7"                                 # versão da layer de logs (arm64)
    enabled_instrumentations: "http,express,pg"            # default: todas
    disabled_instrumentations: ""                          # default: nenhuma
    layers:                                                # override completo das layers
      - "arn:aws:lambda:us-east-1:..."
      - "arn:aws:lambda:us-east-1:..."
```

***

### Referência de opções

#### Opções obrigatórias

| Opção    | Descrição                        | Exemplo         |
| -------- | -------------------------------- | --------------- |
| `tenant` | Tenant ID na Elven Observability | `"meu-tenant"`  |
| `token`  | API Token de autenticação        | `"eyJhbGci..."` |

#### Opções opcionais

| Opção                       | Default                               | Descrição                                                                   |
| --------------------------- | ------------------------------------- | --------------------------------------------------------------------------- |
| `region`                    | `us-east-1`                           | Região AWS das layers                                                       |
| `collector_endpoint`        | — (**obrigatório**)                   | URL do seu coletor OTLP (ex: `https://otel-collector.minha-infra.com:4318`) |
| `logs_endpoint`             | `https://loki.elvenobservability.com` | URL **base** do Loki (sem `/loki/api/v1/push`)                              |
| `architecture`              | `x86_64`                              | Arquitetura padrão: `x86_64` ou `arm64`                                     |
| `log_version`               | `8`                                   | Versão da layer de logs para x86\_64                                        |
| `log_version_arm64`         | `7`                                   | Versão da layer de logs para arm64                                          |
| `enabled_instrumentations`  | todas                                 | Instrumentações OTel habilitadas (separadas por vírgula)                    |
| `disabled_instrumentations` | nenhuma                               | Instrumentações OTel a desabilitar                                          |
| `layers`                    | auto                                  | Override completo da lista de layers (substitui as layers padrão)           |

#### Ordem de resolução da arquitetura

A arquitetura é resolvida nesta ordem de precedência:

1. `custom.elvenLayerPlugin.architecture`
2. Variável de ambiente `LAMBDA_ARCHITECTURE`
3. `provider.architecture` do Serverless
4. Default: `x86_64`

Para funções individuais, `function.architecture` tem prioridade sobre o valor global.

***

### Controle por função

#### Desabilitar instrumentação em uma função específica

Adicione `disableElven: true` na função:

```yaml
functions:
  api:
    handler: handler.api
    # instrumentada normalmente

  internal:
    handler: handler.internal
    disableElven: true
    # NÃO será instrumentada

  worker:
    handler: handler.worker
    # instrumentada normalmente
```

Output no deploy:

```
✅ [Elven] Instrumented function: minha-api_dev_api (x86_64)
⚠️  [Elven] Skipping instrumentation for function: internal
✅ [Elven] Instrumented function: minha-api_dev_worker (x86_64)
```

#### Sobrescrever variáveis por função

O plugin **não sobrescreve** variáveis de ambiente já definidas na função. Isso permite customizar por função:

```yaml
functions:
  api:
    handler: handler.api
    # Usa configuração padrão do plugin

  heavy-worker:
    handler: handler.worker
    environment:
      OTEL_TRACES_SAMPLER: "traceidratio"
      OTEL_TRACES_SAMPLER_ARG: "0.1"
      # Sampling de 10% — demais variáveis são injetadas pelo plugin
```

#### Funções com arquiteturas diferentes

O plugin detecta a arquitetura por função e aplica as layers corretas:

```yaml
provider:
  architecture: x86_64  # padrão para todas

functions:
  api:
    handler: handler.api
    # usa x86_64 (do provider)

  ml-inference:
    handler: handler.inference
    architecture: arm64
    # usa arm64 — layers arm64 aplicadas automaticamente
```

***

### Variáveis de ambiente injetadas

O plugin injeta as seguintes variáveis em cada função:

#### OpenTelemetry (Traces)

| Variável                              | Valor                              | Descrição                       |
| ------------------------------------- | ---------------------------------- | ------------------------------- |
| `AWS_LAMBDA_EXEC_WRAPPER`             | `/opt/otel-handler`                | Ativa o wrapper OTel no runtime |
| `OTEL_SERVICE_NAME`                   | `{service}_{stage}_{function}`     | Nome do serviço nos traces      |
| `OTEL_TRACES_SAMPLER`                 | `always_on`                        | Estratégia de sampling          |
| `OTEL_LAMBDA_TRACE_MODE`              | `capture`                          | Modo de captura de traces       |
| `OTEL_PROPAGATORS`                    | `tracecontext,baggage,xray`        | Propagadores de contexto        |
| `OTEL_RESOURCE_ATTRIBUTES`            | `service.name=...,environment=...` | Atributos de recurso            |
| `OTEL_EXPORTER_OTLP_ENDPOINT`         | `collector_endpoint`               | Endpoint OTLP                   |
| `OTEL_NODE_ENABLED_INSTRUMENTATIONS`  | lista de instrumentações           | Instrumentações habilitadas     |
| `OTEL_NODE_DISABLED_INSTRUMENTATIONS` | *(condicional)*                    | Instrumentações desabilitadas   |

#### Elven Log Extension (Logs)

| Variável          | Valor           | Descrição              |
| ----------------- | --------------- | ---------------------- |
| `LOKI_URL`        | `logs_endpoint` | URL **base** do Loki   |
| `LOKI_TENANT_ID`  | `tenant`        | Tenant ID para o Loki  |
| `LOKI_AUTH_TOKEN` | `token`         | Token de autenticação  |
| `DEBUG`           | `false`         | Debug da log extension |

> **Nota sobre `LOKI_URL`:** O plugin envia apenas a URL base (ex: `https://loki.elvenobservability.com`). A log extension appenda `/loki/api/v1/push` automaticamente.

#### Naming convention automática

O `OTEL_SERVICE_NAME` segue o padrão:

```
{serviceName}_{stage}_{functionName}
```

Exemplo: serviço `ecommerce`, stage `prod`, função `checkout` → `ecommerce_prod_checkout`

***

### Layers aplicadas

O plugin adiciona automaticamente duas layers:

#### x86\_64

| Layer                 | ARN                                                                               |
| --------------------- | --------------------------------------------------------------------------------- |
| OpenTelemetry Node.js | `arn:aws:lambda:{region}:184161586896:layer:opentelemetry-nodejs-0_20_0:1`        |
| Elven Log Extension   | `arn:aws:lambda:{region}:911167927290:layer:elven-lambda-log-extension:{version}` |

#### arm64

| Layer                 | ARN                                                                                     |
| --------------------- | --------------------------------------------------------------------------------------- |
| OpenTelemetry Node.js | `arn:aws:lambda:{region}:184161586896:layer:opentelemetry-nodejs-0_20_0:1`              |
| Elven Log Extension   | `arn:aws:lambda:{region}:911167927290:layer:elven-lambda-log-extension-arm64:{version}` |

#### Override de layers

Para usar layers customizadas (ex: versão diferente ou layer própria):

```yaml
custom:
  elvenLayerPlugin:
    tenant: "meu-tenant"
    token: "meu-token"
    layers:
      - "arn:aws:lambda:us-east-1:184161586896:layer:opentelemetry-nodejs-0_20_0:1"
      - "arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension:8"
```

> Ao definir `layers`, a seleção automática por arquitetura é desabilitada. Certifique-se de usar as layers corretas para a arquitetura das suas funções.

***

### Exemplos completos

#### Exemplo 1 — API REST simples

```yaml
service: users-api

provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1
  stage: ${opt:stage, 'dev'}

plugins:
  - elven-instrumentation-serverless-plugin

custom:
  elvenLayerPlugin:
    tenant: "meu-tenant"
    token: "meu-api-token"

functions:
  getUsers:
    handler: src/handlers/users.list
    events:
      - http:
          path: /users
          method: get

  getUser:
    handler: src/handlers/users.get
    events:
      - http:
          path: /users/{id}
          method: get

  createUser:
    handler: src/handlers/users.create
    events:
      - http:
          path: /users
          method: post
```

#### Exemplo 2 — Microserviço com múltiplas arquiteturas

```yaml
service: ml-pipeline

provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1
  architecture: x86_64

plugins:
  - elven-instrumentation-serverless-plugin

custom:
  elvenLayerPlugin:
    tenant: "meu-tenant"
    token: "meu-api-token"
    region: "us-east-1"

functions:
  api:
    handler: src/api.handler
    events:
      - http:
          path: /predict
          method: post

  inference:
    handler: src/inference.handler
    architecture: arm64
    memorySize: 1024
    timeout: 30
    events:
      - sqs:
          arn: !GetAtt InferenceQueue.Arn

  data-ingestion:
    handler: src/ingestion.handler
    timeout: 300
    events:
      - schedule: rate(5 minutes)
```

#### Exemplo 3 — Instrumentações seletivas com funções excluídas

```yaml
service: ecommerce

provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1

plugins:
  - elven-instrumentation-serverless-plugin

custom:
  elvenLayerPlugin:
    tenant: "meu-tenant"
    token: "meu-api-token"
    enabled_instrumentations: "http,express,pg,redis,undici"

functions:
  checkout:
    handler: src/checkout.handler
    events:
      - http:
          path: /checkout
          method: post

  payments:
    handler: src/payments.handler
    events:
      - sqs:
          arn: !GetAtt PaymentsQueue.Arn

  # Função interna — sem necessidade de observabilidade
  migrations:
    handler: src/migrations.handler
    disableElven: true
    events:
      - schedule: rate(1 day)
```

#### Exemplo 4 — Usando variáveis do Serverless para credenciais

Para não hardcodar credenciais no `serverless.yml`:

```yaml
custom:
  elvenLayerPlugin:
    tenant: ${env:ELVEN_TENANT}
    token: ${env:ELVEN_TOKEN}
    region: ${self:provider.region}
```

Deploy passando as variáveis:

```bash
ELVEN_TENANT=meu-tenant ELVEN_TOKEN=meu-token npx serverless deploy
```

Ou usando SSM Parameter Store:

```yaml
custom:
  elvenLayerPlugin:
    tenant: ${ssm:/elven/tenant}
    token: ${ssm:/elven/token}
```

#### Exemplo 5 — Customizar sampling por função

```yaml
custom:
  elvenLayerPlugin:
    tenant: "meu-tenant"
    token: "meu-token"

functions:
  # Função com alto volume → sampling de 10%
  high-traffic-api:
    handler: src/api.handler
    environment:
      OTEL_TRACES_SAMPLER: "traceidratio"
      OTEL_TRACES_SAMPLER_ARG: "0.1"
    events:
      - http:
          path: /search
          method: get

  # Função crítica → capturar tudo (default do plugin)
  checkout:
    handler: src/checkout.handler
    events:
      - http:
          path: /checkout
          method: post
```

***

### Boas práticas

#### Credenciais seguras

Nunca hardcode credenciais no `serverless.yml`. Use uma das opções:

```yaml
# Opção 1: Variáveis de ambiente
tenant: ${env:ELVEN_TENANT}
token: ${env:ELVEN_TOKEN}

# Opção 2: SSM Parameter Store
tenant: ${ssm:/elven/tenant}
token: ${ssm:/elven/token~true}  # ~true para decrypt SecureString

# Opção 3: Secrets Manager
tenant: ${ssm:/aws/reference/secretsmanager/elven-tenant}
```

#### Instrumentações seletivas

Habilite apenas as instrumentações que o projeto realmente usa. Menos instrumentações = cold start mais rápido:

```yaml
custom:
  elvenLayerPlugin:
    enabled_instrumentations: "http,pg,redis,undici"
```

Instrumentações disponíveis: `http`, `express`, `graphql`, `grpc`, `hapi`, `ioredis`, `koa`, `mongodb`, `mysql`, `net`, `pg`, `redis`, `memcached`, `mongoose`, `amqplib`, `bunyan`, `cassandra-driver`, `connect`, `kafkajs`, `knex`, `mysql2`, `nestjs-core`, `pino`, `restify`, `socket.io`, `undici`, `winston`

#### Timeout da função

As layers adicionam \~200-400ms de overhead no cold start. Certifique-se de que o timeout acomoda isso:

```yaml
provider:
  timeout: 10  # mínimo recomendado
```

Para funções com múltiplas instrumentações, considere 30 segundos.

#### Memória

Mais memória = mais CPU = cold start mais rápido. Para funções instrumentadas, recomendamos pelo menos **256 MB**:

```yaml
provider:
  memorySize: 256
```

#### Stages

O plugin usa o stage do Serverless para compor o nome do serviço e o atributo `environment`:

```bash
# Deploy em staging → nomes: ecommerce_staging_checkout
npx serverless deploy --stage staging

# Deploy em production → nomes: ecommerce_production_checkout
npx serverless deploy --stage production
```

***

### Troubleshooting

#### O deploy falha com erro de layer

**Sintoma:** Erro `Layer version arn:aws:lambda:... does not exist`.

**Causas possíveis:**

1. **Região incorreta** — A layer não está disponível na região configurada. Verifique `region` no plugin
2. **Versão inexistente** — Verifique se `log_version` / `log_version_arm64` estão corretos

```bash
# Verificar se a layer existe na região
aws lambda get-layer-version \
  --layer-name opentelemetry-nodejs-0_20_0 \
  --version-number 1 \
  --region us-east-1 \
  --query "LayerVersionArn"
```

#### A função falha ao iniciar (timeout ou crash)

**Sintoma:** `Runtime.ExitError` ou timeout no cold start.

**Verificações:**

1. **Timeout muito baixo** — Aumente para pelo menos 10 segundos
2. **Layer incompatível com a arquitetura** — Verifique se a função arm64 está recebendo a layer arm64
3. **Wrapper incorreto** — O plugin seta `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler`. Se a layer OTel não estiver presente, isso causa crash

```bash
# Verificar configuração da função após deploy
npx serverless info --verbose
```

#### Traces não aparecem na Elven

**Sintoma:** Função executa normalmente, mas nenhum trace aparece.

**Verificações:**

1. Confirme que `collector_endpoint` está correto
2. Confirme que o `token` é válido
3. Se sobrescreveu `OTEL_TRACES_SAMPLER` na função, verifique o valor

```bash
# Ver as variáveis de ambiente da função
aws lambda get-function-configuration \
  --function-name minha-api-dev-hello \
  --query "Environment.Variables" \
  --output table
```

#### Logs não aparecem no Loki

**Sintoma:** Traces funcionam, mas logs não aparecem.

**Verificações:**

1. **`logs_endpoint` deve ser a URL base** (sem `/loki/api/v1/push`). A extension adiciona o path automaticamente
   * Correto: `https://loki.elvenobservability.com`
   * Errado: `https://loki.elvenobservability.com/loki/api/v1/push`
2. Confirme que `tenant` e `token` estão corretos
3. Habilite debug temporariamente na função:

```yaml
functions:
  minha-funcao:
    handler: handler.handler
    environment:
      DEBUG: "true"
```

Depois invoque e verifique os logs no CloudWatch.

#### Plugin não aparece no output do deploy

**Sintoma:** Nenhuma mensagem `[Elven]` no deploy.

**Verificações:**

1. O plugin está na seção `plugins` do `serverless.yml`
2. O pacote está instalado: `npm ls elven-instrumentation-serverless-plugin`
3. A seção `custom.elvenLayerPlugin` existe (mesmo que vazia, o plugin precisa ser listado em `plugins`)

#### Variáveis de ambiente não são injetadas

**Sintoma:** A função não tem as variáveis esperadas.

**Causa:** O plugin **não sobrescreve** variáveis já definidas na função. Se você definiu `OTEL_SERVICE_NAME` manualmente na função, o plugin não vai substituir.

**Verificação:**

```bash
npx serverless print --path functions.minhaFuncao.environment
```

***

### FAQ

**Preciso configurar algo em cada função?** Não. O plugin instrumenta **todas** as funções automaticamente. A única configuração necessária é o bloco `custom.elvenLayerPlugin` com `tenant` e `token`.

**Posso desabilitar em funções específicas?** Sim. Adicione `disableElven: true` na definição da função.

**Funciona com Serverless Framework v4?** Sim. O plugin é compatível com v3 e v4 do Serverless Framework.

**O plugin funciona com outros runtimes além de Node.js?** As layers padrão do plugin são para **Node.js**. Para outros runtimes (Python, Java, Ruby), use a opção `layers` para especificar as layers corretas. Consulte a documentação de instrumentação Lambda manual para os ARNs de cada runtime.

**O plugin sobrescreve minhas variáveis de ambiente?** Não. O plugin **só injeta** variáveis que **não existem** na função. Se você definir uma variável manualmente, ela tem prioridade.

**O plugin sobrescreve minhas layers?** Não. O plugin **adiciona** as layers sem remover as existentes. Não há duplicação — se a layer já existe, não é adicionada novamente.

**Como atualizar a versão das layers?** Altere `log_version` e `log_version_arm64` no `custom.elvenLayerPlugin`. Para a layer OTel, use a opção `layers` com os ARNs atualizados.

**Posso usar com monorepo / múltiplos `serverless.yml`?** Sim. Cada `serverless.yml` é independente. Use a mesma configuração `elvenLayerPlugin` em cada um, preferencialmente via variáveis de ambiente ou SSM para evitar duplicação.

**Qual o impacto no tamanho do deployment?** As layers **não** contam no limite de 50 MB do pacote de deploy (ZIP). Elas contam no limite total de 250 MB (código + layers descomprimidos).

**Posso usar junto com outros plugins do Serverless?** Sim. O plugin roda no hook `before:package:initialize` e não interfere com outros plugins. A ordem no `plugins` array geralmente não importa.

**Como vejo quais variáveis o plugin injetou?** Use `npx serverless print` para ver a configuração completa resolvida, ou verifique direto na AWS:

```bash
aws lambda get-function-configuration \
  --function-name {service}-{stage}-{function} \
  --query "Environment.Variables"
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.elven.works/elven-platform/elven-observability/integracao-e-instrumentacao/javascript/instrumentacao-lambda-com-serverless-framework-e-elven-plugin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
