# Instrumentando 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.
