# AWS Lambda com Elven Observability

***

### Sumário

* Pré-requisitos
* Visão geral da arquitetura
* Passo 1 — Identificar a arquitetura da função
* Passo 2 — Adicionar as layers
* Passo 3 — Configurar as variáveis de ambiente
* Passo 4 — Validar a instrumentação
* Referência de variáveis de ambiente
* Referência de ARNs das layers
* Exemplos completos via CLI
* Boas práticas
* Troubleshooting

***

### Pré-requisitos

* **AWS CLI v2** instalado e configurado (`aws configure`)
* **jq** instalado (para manipulação segura de JSON)
* **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` (identificador do seu tenant)
  * `API_TOKEN` (token de autenticação)
* Função Lambda existente com um dos runtimes suportados:
  * **Node.js** (18.x, 20.x, 22.x)
  * **Python** (3.9, 3.10, 3.11, 3.12)
  * **Java** (11, 17, 21)
  * **Ruby** (3.2, 3.3)

***

### Visão geral da arquitetura

A instrumentação utiliza **duas Lambda Layers** que funcionam de forma complementar:

```
┌─────────────────────────────────────────────────────────┐
│                     AWS Lambda                          │
│                                                         │
│  ┌──────────────────────┐  ┌─────────────────────────┐  │
│  │  Layer 1: OTel SDK   │  │  Layer 2: Log Extension │  │
│  │  (OpenTelemetry)     │  │  (Elven)                │  │
│  │                      │  │                         │  │
│  │  - Auto-instrumenta  │  │  - Captura stdout/      │  │
│  │    o seu código      │  │    stderr da função     │  │
│  │  - Gera traces/spans │  │  - Envia logs para Loki │  │
│  │  - Exporta via OTLP  │  │  - Batching inteligente │  │
│  └──────────┬───────────┘  └────────────┬────────────┘  │
│             │                           │               │
└─────────────┼───────────────────────────┼───────────────┘
              │                           │
              ▼                           ▼
   Elven OTLP Collector          Elven Loki Gateway
   (traces & metrics)               (logs)
```

| Layer                   | Função           | O que faz                                                                                               |
| ----------------------- | ---------------- | ------------------------------------------------------------------------------------------------------- |
| **OpenTelemetry SDK**   | Traces e metrics | Intercepta chamadas HTTP, DB, etc. e envia spans via OTLP. Disponível para Node.js, Python, Java e Ruby |
| **Elven Log Extension** | Logs             | Captura logs da função via Lambda Logs API e envia para Loki. Funciona com **qualquer runtime**         |

***

### Passo 1 — Identificar a arquitetura da função

Antes de começar, verifique a arquitetura da sua função Lambda. Os ARNs das layers variam entre `x86_64` e `arm64`.

```bash
aws lambda get-function-configuration \
  --function-name SUA_FUNCAO \
  --query "Architectures" \
  --output text
```

> Se o retorno for vazio ou `x86_64`, use as layers **x86\_64**. Se o retorno for `arm64`, use as layers **arm64**.

***

### Passo 2 — Adicionar as layers

#### ARNs das layers

Substitua `{REGIAO}` pela região da sua função (ex: `us-east-1`).

**Layer de OpenTelemetry (escolha conforme seu runtime)**

| Runtime                 | ARN da Layer                                                                        |
| ----------------------- | ----------------------------------------------------------------------------------- |
| **Node.js**             | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-nodejs-0_20_0:1`          |
| **Python**              | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-python-0_18_0:2`          |
| **Java (Agent)**        | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-javaagent-0_18_0:1`       |
| **Java (Wrapper)**      | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-javawrapper-0_18_0:1`     |
| **Ruby**                | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-ruby-0_12_0:1`            |
| **Collector (x86\_64)** | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-collector-amd64-0_20_0:1` |
| **Collector (arm64)**   | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-collector-arm64-0_20_0:1` |

> **Nota:** As versões acima são as mais recentes no momento da escrita. Consulte as [releases oficiais](https://github.com/open-telemetry/opentelemetry-lambda/releases) para verificar se há atualizações.

> **Java:** Use `javaagent` para auto-instrumentação (recomendado) ou `javawrapper` se preferir instrumentação manual. Não use ambos ao mesmo tempo.

**Layer de Logs (Elven Log Extension)**

| Arquitetura | ARN da Layer                                                                    |
| ----------- | ------------------------------------------------------------------------------- |
| **x86\_64** | `arn:aws:lambda:{REGIAO}:911167927290:layer:elven-lambda-log-extension:8`       |
| **arm64**   | `arn:aws:lambda:{REGIAO}:911167927290:layer:elven-lambda-log-extension-arm64:7` |

**Exemplo combinado**

Para uma função **Node.js em x86\_64**:

```
arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-nodejs-0_20_0:1
arn:aws:lambda:{REGIAO}:911167927290:layer:elven-lambda-log-extension:8
```

Para uma função **Python em arm64**:

```
arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-python-0_18_0:2
arn:aws:lambda:{REGIAO}:911167927290:layer:elven-lambda-log-extension-arm64:7
```

#### Adicionar layers preservando as existentes

> **Atenção:** O comando `--layers` da AWS CLI **substitui** todas as layers da função. O script abaixo preserva as layers já configuradas.

```bash
FUNCTION_NAME="sua-funcao"
REGIAO="us-east-1"
ARCH="x86_64"  # ou "arm64"
RUNTIME="nodejs" # nodejs | python | javaagent | javawrapper | ruby

# Mapa de layers OTel por runtime
declare -A OTEL_LAYERS=(
  ["nodejs"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-nodejs-0_20_0:1"
  ["python"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-python-0_18_0:2"
  ["javaagent"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-javaagent-0_18_0:1"
  ["javawrapper"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-javawrapper-0_18_0:1"
  ["ruby"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-ruby-0_12_0:1"
)

OTEL_LAYER="${OTEL_LAYERS[$RUNTIME]}"

# Obter layers atuais
LAYERS_ATUAIS=$(aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --query "Layers[].Arn" \
  --output json)

# Definir layer de logs conforme arquitetura
if [ "$ARCH" = "arm64" ]; then
  LOG_LAYER="arn:aws:lambda:${REGIAO}:911167927290:layer:elven-lambda-log-extension-arm64:7"
else
  LOG_LAYER="arn:aws:lambda:${REGIAO}:911167927290:layer:elven-lambda-log-extension:8"
fi

# Mesclar layers existentes com as novas (sem duplicar)
TODAS_LAYERS=$(echo "$LAYERS_ATUAIS" | jq -r --arg otel "$OTEL_LAYER" --arg log "$LOG_LAYER" \
  '. + [$otel, $log] | unique')

# Aplicar
aws lambda update-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --layers $(echo "$TODAS_LAYERS" | jq -r '.[]' | tr '\n' ' ')
```

***

### Passo 3 — Configurar as variáveis de ambiente

> **Atenção:** O parâmetro `--environment` da AWS CLI **substitui todas** as variáveis de ambiente. O script abaixo faz o merge com as existentes para não perder nenhuma configuração.

#### Script seguro para configurar as variáveis

```bash
FUNCTION_NAME="sua-funcao"
REGIAO="us-east-1"
STAGE="production"

# --- Credenciais Elven (substitua pelos seus valores) ---
ELVEN_TENANT="seu-tenant-id"
ELVEN_TOKEN="seu-api-token"

# --- Collector OTLP (implantado na sua infraestrutura) ---
COLLECTOR_ENDPOINT="${COLLECTOR_ENDPOINT}"

# Nome do serviço (convenção: projeto_stage_funcao)
SERVICE_NAME="${FUNCTION_NAME}"
RUNTIME="nodejs"  # nodejs | python | java | ruby

# 1. Obter variáveis de ambiente atuais
ENV_ATUAL=$(aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --query "Environment.Variables" \
  --output json)

# Se não houver variáveis, iniciar com objeto vazio
if [ "$ENV_ATUAL" = "null" ] || [ -z "$ENV_ATUAL" ]; then
  ENV_ATUAL="{}"
fi

# 2. Definir as variáveis da Elven (comuns a todos os runtimes)
ELVEN_VARS=$(cat <<EOF
{
  "AWS_LAMBDA_EXEC_WRAPPER": "/opt/otel-handler",
  "OTEL_SERVICE_NAME": "${SERVICE_NAME}",
  "OTEL_TRACES_SAMPLER": "always_on",
  "OTEL_PROPAGATORS": "tracecontext,baggage,xray",
  "OTEL_RESOURCE_ATTRIBUTES": "service.name=${SERVICE_NAME},environment=${STAGE}",
  "OTEL_EXPORTER_OTLP_ENDPOINT": "${COLLECTOR_ENDPOINT}",
  "LOKI_URL": "https://loki.elvenobservability.com",
  "LOKI_TENANT_ID": "${ELVEN_TENANT}",
  "LOKI_AUTH_TOKEN": "${ELVEN_TOKEN}",
  "DEBUG": "false"
}
EOF
)

# 2b. Adicionar variáveis específicas do runtime
case "$RUNTIME" in
  nodejs)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      "OTEL_NODE_ENABLED_INSTRUMENTATIONS": "http,express,graphql,grpc,ioredis,koa,mongodb,mysql,net,pg,redis,mongoose,mysql2,knex,undici"
    }')
    ;;
  python)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      "OTEL_PYTHON_DISABLED_INSTRUMENTATIONS": ""
    }')
    ;;
  java)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      "OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED": "true"
    }')
    ;;
  ruby)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      "OTEL_RUBY_DISABLED_INSTRUMENTATIONS": ""
    }')
    ;;
esac

# 3. Mesclar: variáveis da Elven têm prioridade, mas preserva as demais
ENV_FINAL=$(echo "$ENV_ATUAL" "$ELVEN_VARS" | jq -s '.[0] * .[1]')

# 4. Aplicar
aws lambda update-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --environment "{\"Variables\": $ENV_FINAL}"

echo "Variáveis de ambiente configuradas com sucesso."
```

#### Descrição das variáveis

**Variáveis comuns (todos os runtimes)**

| Variável                      | Valor                                       | Descrição                                                      |
| ----------------------------- | ------------------------------------------- | -------------------------------------------------------------- |
| `AWS_LAMBDA_EXEC_WRAPPER`     | `/opt/otel-handler`                         | Ativa o wrapper OTel que instrumenta o runtime automaticamente |
| `OTEL_SERVICE_NAME`           | `nome-do-servico`                           | Nome do serviço que aparece nos traces                         |
| `OTEL_TRACES_SAMPLER`         | `always_on`                                 | Estratégia de sampling (ver Boas práticas)                     |
| `OTEL_PROPAGATORS`            | `tracecontext,baggage,xray`                 | Propagadores de contexto habilitados                           |
| `OTEL_RESOURCE_ATTRIBUTES`    | `service.name=...,environment=...`          | Atributos de recurso OTel para correlação                      |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `https://otel-collector.sua-infra.com:4318` | URL do seu coletor OTLP (implantado na sua infraestrutura)     |
| `LOKI_URL`                    | `https://loki.elvenobservability.com`       | URL **base** do Loki (sem o path `/loki/api/v1/push`)          |
| `LOKI_TENANT_ID`              | `seu-tenant-id`                             | Identificador do tenant no Loki                                |
| `LOKI_AUTH_TOKEN`             | `seu-api-token`                             | Token de autenticação para o Loki                              |
| `DEBUG`                       | `false`                                     | Habilita logs de debug da extension (`true` ou `false`)        |

**Variáveis específicas por runtime**

**Node.js:**

| Variável                              | Descrição                                                                  |
| ------------------------------------- | -------------------------------------------------------------------------- |
| `OTEL_NODE_ENABLED_INSTRUMENTATIONS`  | Instrumentações a habilitar, separadas por vírgula (ex: `http,express,pg`) |
| `OTEL_NODE_DISABLED_INSTRUMENTATIONS` | Instrumentações a desabilitar (tem prioridade sobre enabled)               |

**Python:**

| Variável                                | Descrição                                            |
| --------------------------------------- | ---------------------------------------------------- |
| `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` | Instrumentações a desabilitar, separadas por vírgula |
| `OTEL_PYTHON_LOG_CORRELATION`           | `true` para correlacionar logs com trace IDs         |

**Java:**

| Variável                                      | Descrição                                                                                           |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED` | `true` para habilitar todas as instrumentações por padrão                                           |
| `OTEL_JAVAAGENT_DEBUG`                        | `true` para logs de debug do agent                                                                  |
| `JAVA_TOOL_OPTIONS`                           | Definido automaticamente pela layer. **Não sobrescreva** a menos que precise adicionar flags extras |

**Ruby:**

| Variável                              | Descrição                                            |
| ------------------------------------- | ---------------------------------------------------- |
| `OTEL_RUBY_DISABLED_INSTRUMENTATIONS` | Instrumentações a desabilitar, separadas por vírgula |

***

### Passo 4 — Validar a instrumentação

#### 4.1. Invocar a função

```bash
aws lambda invoke \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --payload '{"test": true}' \
  --cli-binary-format raw-in-base64-out \
  /dev/stdout
```

#### 4.2. Verificar nos logs do CloudWatch

A extension da Elven escreve logs de diagnóstico no CloudWatch. Procure por mensagens com prefixo `elven-lambda-log-extension`:

```bash
aws logs filter-log-events \
  --log-group-name "/aws/lambda/$FUNCTION_NAME" \
  --region "$REGIAO" \
  --filter-pattern "elven-lambda-log-extension" \
  --start-time $(date -d '5 minutes ago' +%s000 2>/dev/null || date -v-5M +%s000) \
  --query "events[].message" \
  --output text
```

#### 4.3. Checklist de validação

* [ ] A função executa sem erros (sem timeout adicional inesperado)
* [ ] Traces aparecem na Elven Observability com o `service.name` correto
* [ ] Logs aparecem no Loki com os labels `function_name` e `region`
* [ ] O cold start aumentou no máximo \~200-400ms (overhead normal das layers)

***

### Referência de variáveis de ambiente

#### Variáveis obrigatórias

| Variável                      | Componente    | Exemplo                                     |
| ----------------------------- | ------------- | ------------------------------------------- |
| `AWS_LAMBDA_EXEC_WRAPPER`     | OTel Layer    | `/opt/otel-handler`                         |
| `OTEL_SERVICE_NAME`           | OTel Layer    | `meu-servico-prod`                          |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTel Layer    | `https://otel-collector.sua-infra.com:4318` |
| `LOKI_URL`                    | Log Extension | `https://loki.elvenobservability.com`       |
| `LOKI_TENANT_ID`              | Log Extension | `meu-tenant`                                |
| `LOKI_AUTH_TOKEN`             | Log Extension | `token-aqui`                                |

#### Variáveis opcionais — OpenTelemetry (todos os runtimes)

| Variável                   | Default                 | Descrição                                                                                                                  |
| -------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `OTEL_TRACES_SAMPLER`      | `parentbased_always_on` | Estratégia de sampling. Use `always_on` para capturar tudo ou `traceidratio` com `OTEL_TRACES_SAMPLER_ARG` para amostragem |
| `OTEL_TRACES_SAMPLER_ARG`  | —                       | Argumento do sampler (ex: `0.1` para 10% com `traceidratio`)                                                               |
| `OTEL_PROPAGATORS`         | `tracecontext,baggage`  | Propagadores de contexto. Adicione `xray` se usar X-Ray                                                                    |
| `OTEL_RESOURCE_ATTRIBUTES` | —                       | Atributos adicionais (ex: `service.name=x,environment=prod`)                                                               |

#### Variáveis opcionais — OpenTelemetry (específicas por runtime)

| Variável                                      | Runtime | Default | Descrição                                                |
| --------------------------------------------- | ------- | ------- | -------------------------------------------------------- |
| `OTEL_NODE_ENABLED_INSTRUMENTATIONS`          | Node.js | todas   | Instrumentações a habilitar (separadas por vírgula)      |
| `OTEL_NODE_DISABLED_INSTRUMENTATIONS`         | Node.js | —       | Instrumentações a desabilitar (prioridade sobre enabled) |
| `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS`       | Python  | —       | Instrumentações a desabilitar (separadas por vírgula)    |
| `OTEL_PYTHON_LOG_CORRELATION`                 | Python  | `false` | Correlacionar logs com trace IDs                         |
| `OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED` | Java    | `true`  | Habilitar todas as instrumentações por padrão            |
| `OTEL_JAVAAGENT_DEBUG`                        | Java    | `false` | Logs de debug do Java agent                              |
| `OTEL_RUBY_DISABLED_INSTRUMENTATIONS`         | Ruby    | —       | Instrumentações a desabilitar (separadas por vírgula)    |

#### Variáveis opcionais — Log Extension

| Variável        | Default                   | Descrição                                     |
| --------------- | ------------------------- | --------------------------------------------- |
| `DEBUG`         | `false`                   | Logs de debug da extension no CloudWatch      |
| `BUFFER_SIZE`   | Auto (baseado na memória) | Tamanho do buffer interno de logs             |
| `BATCH_SIZE`    | Auto (baseado na memória) | Quantidade de logs por batch enviado ao Loki  |
| `BATCH_TIMEOUT` | `1s`                      | Tempo máximo antes de enviar um batch parcial |
| `HTTP_TIMEOUT`  | `10s`                     | Timeout das requisições HTTP para o Loki      |
| `RETRY_MAX`     | `2`                       | Número de retentativas em caso de falha       |
| `RETRY_BACKOFF` | `100ms`                   | Backoff entre retentativas                    |

> **Defaults automáticos de buffer/batch:** Os valores de `BUFFER_SIZE` e `BATCH_SIZE` são calculados automaticamente com base na memória da função. Geralmente não é necessário alterar.

| Memória     | Buffer Size | Batch Size |
| ----------- | ----------- | ---------- |
| < 512 MB    | 1.000       | 50         |
| 512–1023 MB | 5.000       | 500        |
| >= 1024 MB  | 10.000      | 500        |

***

### Referência de ARNs das layers

> Substitua `{REGIAO}` pela região da sua função. As layers estão disponíveis em todas as regiões comerciais da AWS. Consulte as [releases oficiais](https://github.com/open-telemetry/opentelemetry-lambda/releases) para versões atualizadas.

#### OpenTelemetry — por runtime

| Runtime             | Layer                           | ARN                                                                                 |
| ------------------- | ------------------------------- | ----------------------------------------------------------------------------------- |
| Node.js             | `opentelemetry-nodejs`          | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-nodejs-0_20_0:1`          |
| Python              | `opentelemetry-python`          | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-python-0_18_0:2`          |
| Java (Agent)        | `opentelemetry-javaagent`       | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-javaagent-0_18_0:1`       |
| Java (Wrapper)      | `opentelemetry-javawrapper`     | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-javawrapper-0_18_0:1`     |
| Ruby                | `opentelemetry-ruby`            | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-ruby-0_12_0:1`            |
| Collector (x86\_64) | `opentelemetry-collector-amd64` | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-collector-amd64-0_20_0:1` |
| Collector (arm64)   | `opentelemetry-collector-arm64` | `arn:aws:lambda:{REGIAO}:184161586896:layer:opentelemetry-collector-arm64-0_20_0:1` |

> **Quando usar o Collector?** O Collector é uma alternativa para cenários avançados onde você precisa de processamento, filtragem ou roteamento de telemetria antes de enviar ao backend. Para a maioria dos casos, as layers de runtime (Node.js, Python, etc.) são suficientes.

#### Elven Log Extension

| Arquitetura | ARN                                                                             |
| ----------- | ------------------------------------------------------------------------------- |
| x86\_64     | `arn:aws:lambda:{REGIAO}:911167927290:layer:elven-lambda-log-extension:8`       |
| arm64       | `arn:aws:lambda:{REGIAO}:911167927290:layer:elven-lambda-log-extension-arm64:7` |

***

### Exemplos completos via CLI

#### Exemplo 1 — Função simples

Script completo copy-paste. Substitua as 5 variáveis no topo:

```bash
#!/bin/bash
set -euo pipefail

# ============================================
# CONFIGURAÇÃO — Altere estes valores
# ============================================
FUNCTION_NAME="minha-funcao-api"
REGIAO="us-east-1"
ELVEN_TENANT="meu-tenant"
ELVEN_TOKEN="meu-token"
COLLECTOR_ENDPOINT="${COLLECTOR_ENDPOINT}"
RUNTIME="nodejs"  # nodejs | python | javaagent | javawrapper | ruby
# ============================================

STAGE="production"
SERVICE_NAME="$FUNCTION_NAME"

# Mapa de layers OTel por runtime
declare -A OTEL_LAYERS=(
  ["nodejs"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-nodejs-0_20_0:1"
  ["python"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-python-0_18_0:2"
  ["javaagent"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-javaagent-0_18_0:1"
  ["javawrapper"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-javawrapper-0_18_0:1"
  ["ruby"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-ruby-0_12_0:1"
)

OTEL_LAYER="${OTEL_LAYERS[$RUNTIME]}"

echo "==> Verificando função $FUNCTION_NAME..."
FUNC_CONFIG=$(aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --output json)

ARCH=$(echo "$FUNC_CONFIG" | jq -r '.Architectures[0] // "x86_64"')
echo "    Arquitetura: $ARCH"
echo "    Runtime: $RUNTIME"

# Selecionar layer de logs conforme arquitetura
if [ "$ARCH" = "arm64" ]; then
  LOG_LAYER="arn:aws:lambda:${REGIAO}:911167927290:layer:elven-lambda-log-extension-arm64:7"
else
  LOG_LAYER="arn:aws:lambda:${REGIAO}:911167927290:layer:elven-lambda-log-extension:8"
fi

# Obter e mesclar layers
LAYERS_ATUAIS=$(echo "$FUNC_CONFIG" | jq -r '[.Layers[]?.Arn] // []')
TODAS_LAYERS=$(echo "$LAYERS_ATUAIS" | jq -r \
  --arg otel "$OTEL_LAYER" --arg log "$LOG_LAYER" \
  '. + [$otel, $log] | unique | .[]' | tr '\n' ' ')

echo "==> Layers a aplicar:"
echo "    - $OTEL_LAYER"
echo "    - $LOG_LAYER"

# Obter e mesclar variáveis de ambiente
ENV_ATUAL=$(echo "$FUNC_CONFIG" | jq '.Environment.Variables // {}')

ELVEN_VARS=$(cat <<EOF
{
  "AWS_LAMBDA_EXEC_WRAPPER": "/opt/otel-handler",
  "OTEL_SERVICE_NAME": "${SERVICE_NAME}",
  "OTEL_TRACES_SAMPLER": "always_on",
  "OTEL_PROPAGATORS": "tracecontext,baggage,xray",
  "OTEL_RESOURCE_ATTRIBUTES": "service.name=${SERVICE_NAME},environment=${STAGE}",
  "OTEL_EXPORTER_OTLP_ENDPOINT": "${COLLECTOR_ENDPOINT}",
  "LOKI_URL": "https://loki.elvenobservability.com",
  "LOKI_TENANT_ID": "${ELVEN_TENANT}",
  "LOKI_AUTH_TOKEN": "${ELVEN_TOKEN}",
  "DEBUG": "false"
}
EOF
)

# Adicionar variáveis específicas do runtime
if [ "$RUNTIME" = "nodejs" ]; then
  ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
    "OTEL_NODE_ENABLED_INSTRUMENTATIONS": "http,express,graphql,grpc,ioredis,koa,mongodb,mysql,net,pg,redis,mongoose,mysql2,knex,undici"
  }')
fi

ENV_FINAL=$(echo "$ENV_ATUAL" "$ELVEN_VARS" | jq -s '.[0] * .[1]')

echo "==> Aplicando configuração..."
aws lambda update-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --layers $TODAS_LAYERS \
  --environment "{\"Variables\": $ENV_FINAL}" \
  --output json | jq '{FunctionName, LastModified, Layers: [.Layers[].Arn]}'

echo ""
echo "==> Instrumentação aplicada com sucesso!"
echo "    Invoque a função e verifique traces e logs na Elven Observability."
```

#### Exemplo 2 — Instrumentar múltiplas funções de uma vez

```bash
#!/bin/bash
set -euo pipefail

REGIAO="us-east-1"
ELVEN_TENANT="meu-tenant"
ELVEN_TOKEN="meu-token"
COLLECTOR_ENDPOINT="${COLLECTOR_ENDPOINT}"
STAGE="production"
RUNTIME="nodejs"  # nodejs | python | javaagent | javawrapper | ruby

# Lista de funções a instrumentar
FUNCOES=(
  "api-users"
  "api-orders"
  "worker-notifications"
)

# Mapa de layers OTel por runtime
declare -A OTEL_LAYERS=(
  ["nodejs"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-nodejs-0_20_0:1"
  ["python"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-python-0_18_0:2"
  ["javaagent"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-javaagent-0_18_0:1"
  ["javawrapper"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-javawrapper-0_18_0:1"
  ["ruby"]="arn:aws:lambda:${REGIAO}:184161586896:layer:opentelemetry-ruby-0_12_0:1"
)

OTEL_LAYER="${OTEL_LAYERS[$RUNTIME]}"

for FUNC in "${FUNCOES[@]}"; do
  echo ""
  echo "=========================================="
  echo "Instrumentando: $FUNC ($RUNTIME)"
  echo "=========================================="

  FUNC_CONFIG=$(aws lambda get-function-configuration \
    --function-name "$FUNC" --region "$REGIAO" --output json)

  ARCH=$(echo "$FUNC_CONFIG" | jq -r '.Architectures[0] // "x86_64"')

  if [ "$ARCH" = "arm64" ]; then
    LOG_LAYER="arn:aws:lambda:${REGIAO}:911167927290:layer:elven-lambda-log-extension-arm64:7"
  else
    LOG_LAYER="arn:aws:lambda:${REGIAO}:911167927290:layer:elven-lambda-log-extension:8"
  fi

  LAYERS_ATUAIS=$(echo "$FUNC_CONFIG" | jq -r '[.Layers[]?.Arn] // []')
  TODAS_LAYERS=$(echo "$LAYERS_ATUAIS" | jq -r \
    --arg otel "$OTEL_LAYER" --arg log "$LOG_LAYER" \
    '. + [$otel, $log] | unique | .[]' | tr '\n' ' ')

  ENV_ATUAL=$(echo "$FUNC_CONFIG" | jq '.Environment.Variables // {}')

  ELVEN_VARS=$(cat <<EOF
{
  "AWS_LAMBDA_EXEC_WRAPPER": "/opt/otel-handler",
  "OTEL_SERVICE_NAME": "${FUNC}",
  "OTEL_TRACES_SAMPLER": "always_on",
  "OTEL_PROPAGATORS": "tracecontext,baggage,xray",
  "OTEL_RESOURCE_ATTRIBUTES": "service.name=${FUNC},environment=${STAGE}",
  "OTEL_EXPORTER_OTLP_ENDPOINT": "${COLLECTOR_ENDPOINT}",
  "LOKI_URL": "https://loki.elvenobservability.com",
  "LOKI_TENANT_ID": "${ELVEN_TENANT}",
  "LOKI_AUTH_TOKEN": "${ELVEN_TOKEN}",
  "DEBUG": "false"
}
EOF
)

  if [ "$RUNTIME" = "nodejs" ]; then
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      "OTEL_NODE_ENABLED_INSTRUMENTATIONS": "http,express,graphql,grpc,ioredis,koa,mongodb,mysql,net,pg,redis,mongoose,mysql2,knex,undici"
    }')
  fi

  ENV_FINAL=$(echo "$ENV_ATUAL" "$ELVEN_VARS" | jq -s '.[0] * .[1]')

  aws lambda update-function-configuration \
    --function-name "$FUNC" \
    --region "$REGIAO" \
    --layers $TODAS_LAYERS \
    --environment "{\"Variables\": $ENV_FINAL}" \
    --output json | jq '{FunctionName, LastModified}'

  echo "OK: $FUNC instrumentada."
done

echo ""
echo "Todas as funções foram instrumentadas."
```

#### Exemplo 3 — Remover instrumentação

Para remover a instrumentação de uma função, é necessário remover as layers e as variáveis de ambiente da Elven:

```bash
FUNCTION_NAME="minha-funcao-api"
REGIAO="us-east-1"

# Variáveis de ambiente adicionadas pela Elven
ELVEN_KEYS=(
  "AWS_LAMBDA_EXEC_WRAPPER"
  "OTEL_SERVICE_NAME"
  "OTEL_TRACES_SAMPLER"
  "OTEL_PROPAGATORS"
  "OTEL_RESOURCE_ATTRIBUTES"
  "OTEL_EXPORTER_OTLP_ENDPOINT"
  "OTEL_NODE_ENABLED_INSTRUMENTATIONS"
  "OTEL_NODE_DISABLED_INSTRUMENTATIONS"
  "LOKI_URL"
  "LOKI_TENANT_ID"
  "LOKI_AUTH_TOKEN"
  "DEBUG"
)

FUNC_CONFIG=$(aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" --region "$REGIAO" --output json)

# Remover layers da Elven (manter as demais)
LAYERS_LIMPAS=$(echo "$FUNC_CONFIG" | jq -r '
  [.Layers[]?.Arn] // [] |
  map(select(
    (contains("184161586896") | not) and
    (contains("911167927290") | not)
  )) | .[]' | tr '\n' ' ')

# Remover variáveis de ambiente da Elven (manter as demais)
DELETE_FILTER=$(printf '%s\n' "${ELVEN_KEYS[@]}" | jq -R . | jq -s '.')
ENV_LIMPO=$(echo "$FUNC_CONFIG" | jq --argjson keys "$DELETE_FILTER" '
  .Environment.Variables // {} | to_entries |
  map(select(.key as $k | $keys | index($k) | not)) |
  from_entries')

aws lambda update-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --layers $LAYERS_LIMPAS \
  --environment "{\"Variables\": $ENV_LIMPO}"

echo "Instrumentação removida de $FUNCTION_NAME."
```

***

### Boas práticas

#### Naming convention para `OTEL_SERVICE_NAME`

Use um padrão consistente que facilite filtrar no painel:

```
{projeto}_{stage}_{funcao}
```

Exemplos: `ecommerce_prod_checkout`, `notifications_staging_sender`.

#### Sampling em produção

Para funções com alto volume de invocações, considere usar amostragem para reduzir custos:

```bash
# 10% das requisições
"OTEL_TRACES_SAMPLER": "traceidratio",
"OTEL_TRACES_SAMPLER_ARG": "0.1"
```

Para funções críticas ou com baixo volume, mantenha `always_on`.

#### Instrumentações habilitadas

Habilite apenas as instrumentações que a função realmente utiliza. Isso reduz o overhead de cold start.

**Node.js** — use `OTEL_NODE_ENABLED_INSTRUMENTATIONS`:

```bash
# Função que só usa HTTP + PostgreSQL
"OTEL_NODE_ENABLED_INSTRUMENTATIONS": "http,pg,undici"
```

Instrumentações Node.js 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`

**Python** — use `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` para desabilitar o que não precisa:

```bash
# Desabilitar instrumentações que não usa
"OTEL_PYTHON_DISABLED_INSTRUMENTATIONS": "django,flask,celery"
```

**Java** — o agent instrumenta automaticamente. Para desabilitar algo específico:

```bash
"OTEL_INSTRUMENTATION_<NOME>_ENABLED": "false"
```

**Ruby** — use `OTEL_RUBY_DISABLED_INSTRUMENTATIONS`:

```bash
"OTEL_RUBY_DISABLED_INSTRUMENTATIONS": "sinatra,sidekiq"
```

#### Timeout da função

As layers adicionam um overhead no cold start (\~200-400ms). Certifique-se de que o timeout da função acomoda esse overhead. Recomendação mínima: **10 segundos** para funções leves, **30 segundos** para funções com múltiplas instrumentações.

#### Memória

A log extension usa um buffer em memória proporcional à configuração da função. Para funções com **128 MB**, o buffer é conservador. Se você observar mensagens de "buffer full" nos logs do CloudWatch, considere:

* Aumentar a memória da função
* Ou definir `BATCH_SIZE` menor para flush mais frequente

***

### Troubleshooting

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

**Sintoma:** A função retorna timeout no cold start ou erro `Runtime.ExitError`.

**Causas possíveis:**

1. **`AWS_LAMBDA_EXEC_WRAPPER` incorreto** — Deve ser exatamente `/opt/otel-handler`. Verifique se a layer OTel está adicionada.
2. **Layer incompatível com a arquitetura** — Verifique se a layer corresponde à arquitetura da função (x86\_64 vs arm64).
3. **Timeout muito baixo** — Aumente o timeout da função para pelo menos 10 segundos.

```bash
# Verificar layers e wrapper
aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --query "{Layers: Layers[].Arn, Wrapper: Environment.Variables.AWS_LAMBDA_EXEC_WRAPPER, Timeout: Timeout, Arch: Architectures}" \
  --output table
```

#### Traces não aparecem na Elven

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

**Verificações:**

1. Confirme que `AWS_LAMBDA_EXEC_WRAPPER` está definido como `/opt/otel-handler`
2. Confirme que `OTEL_EXPORTER_OTLP_ENDPOINT` está apontando para o seu coletor OTLP
3. Verifique se `OTEL_SERVICE_NAME` não está vazio
4. Confirme que a layer OpenTelemetry está na lista de layers

```bash
# Testar conectividade com o endpoint (rode de dentro da VPC se aplicável)
curl -v https://<seu-collector>:4318/v1/traces
```

#### Logs não aparecem no Loki

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

**Verificações:**

1. **`LOKI_URL` 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 `LOKI_TENANT_ID` e `LOKI_AUTH_TOKEN` estão preenchidos
3. Habilite `DEBUG=true` e verifique os logs da extension no CloudWatch

```bash
# Habilitar debug temporariamente
ENV_ATUAL=$(aws lambda get-function-configuration \
  --function-name "$FUNCTION_NAME" --region "$REGIAO" \
  --query "Environment.Variables" --output json)

ENV_DEBUG=$(echo "$ENV_ATUAL" '{"DEBUG":"true"}' | jq -s '.[0] * .[1]')

aws lambda update-function-configuration \
  --function-name "$FUNCTION_NAME" \
  --region "$REGIAO" \
  --environment "{\"Variables\": $ENV_DEBUG}"

# Invocar e verificar logs
aws lambda invoke --function-name "$FUNCTION_NAME" --payload '{}' \
  --cli-binary-format raw-in-base64-out /dev/stdout

# Verificar logs de debug da extension
aws logs filter-log-events \
  --log-group-name "/aws/lambda/$FUNCTION_NAME" \
  --filter-pattern "elven" \
  --start-time $(date -v-5M +%s000 2>/dev/null || date -d '5 minutes ago' +%s000) \
  --query "events[].message" --output text
```

#### Logs com mensagem "buffer full"

**Sintoma:** Nos logs do CloudWatch aparecem warnings com "buffer full".

**Causa:** A função produz logs mais rápido do que a extension consegue enviar. Isso acontece geralmente em funções com pouca memória (128 MB) e logging intenso.

**Soluções:**

1. Aumentar a memória da função (melhora buffer e batch defaults)
2. Ou ajustar manualmente: `BUFFER_SIZE=5000` e `BATCH_SIZE=200`
3. Reduzir o volume de logs na função (evitar `console.log` em loops)

#### Cold start muito lento

**Sintoma:** O cold start aumentou mais de 1 segundo após adicionar as layers.

**Soluções:**

1. Reduzir as instrumentações habilitadas (`OTEL_NODE_ENABLED_INSTRUMENTATIONS`)
2. Aumentar a memória da função (mais CPU = init mais rápido)
3. Usar Provisioned Concurrency para funções críticas

#### Erro 401/403 no Loki

**Sintoma:** Debug mode mostra `server error 401` ou `server error 403`.

**Causa:** Token de autenticação inválido ou tenant incorreto.

**Verificações:**

1. Confirme o valor de `LOKI_AUTH_TOKEN` no painel da Elven
2. Confirme o valor de `LOKI_TENANT_ID`
3. Verifique se o token não expirou

***

### FAQ

**Quais runtimes são suportados?** A Elven suporta **Node.js**, **Python**, **Java** e **Ruby** com as layers oficiais do OpenTelemetry. A layer de logs (Elven Log Extension) funciona com **qualquer runtime**, incluindo Go e .NET, pois captura logs via Lambda Logs API independente da linguagem.

**Posso usar com funções em VPC?** Sim. Certifique-se de que a VPC tem acesso à internet (NAT Gateway) ou VPC Endpoints para que as layers consigam enviar dados para os endpoints da Elven.

**Qual o impacto no custo da Lambda?** As layers adicionam \~10-30ms de overhead por invocação (warm) e \~200-400ms no cold start. O tamanho combinado das layers é de aproximadamente 15-20 MB, que conta no limite de 250 MB de deployment.

**Posso usar junto com o AWS X-Ray?** Sim. Mantenha `xray` na lista de `OTEL_PROPAGATORS` para que os traces sejam correlacionados. No entanto, recomendamos desabilitar o X-Ray Tracing nativo da Lambda para evitar duplicação de dados.


---

# 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/aws-lambda-com-elven-observability.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.
