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.

aws lambda get-function-configuration <br>
  --function-name SUA_FUNCAO <br>
  --query "Architectures" <br>
  --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 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.

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 <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --query "Layers[].Arn" <br>
  --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" <br>
  '. + [$otel, $log] | unique')

# Aplicar
aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --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

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 <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --query "Environment.Variables" <br>
  --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 <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --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

aws lambda invoke <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --payload '{"test": true}' <br>
  --cli-binary-format raw-in-base64-out <br>
  /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:

aws logs filter-log-events <br>
  --log-group-name "/aws/lambda/$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --filter-pattern "elven-lambda-log-extension" <br>
  --start-time $(date -d '5 minutes ago' +%s000 2>/dev/null || date -v-5M +%s000) <br>
  --query "events[].message" <br>
  --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 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:

#!/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 <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --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 <br>
  --arg otel "$OTEL_LAYER" --arg log "$LOG_LAYER" <br>
  '. + [$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 <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --layers $TODAS_LAYERS <br>
  --environment "{\"Variables\": $ENV_FINAL}" <br>
  --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

#!/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 <br>
    --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 <br>
    --arg otel "$OTEL_LAYER" --arg log "$LOG_LAYER" <br>
    '. + [$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 <br>
    --function-name "$FUNC" <br>
    --region "$REGIAO" <br>
    --layers $TODAS_LAYERS <br>
    --environment "{\"Variables\": $ENV_FINAL}" <br>
    --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:

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 <br>
  --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 <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGIAO" <br>
  --layers $LAYERS_LIMPAS <br>
  --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:

# 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:

# 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:

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

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

"OTEL_INSTRUMENTATION_<NOME>_ENABLED": "false"

Ruby — use OTEL_RUBY_DISABLED_INSTRUMENTATIONS:

"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.
# Verificar layers e wrapper
aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --query "{Layers: Layers[].Arn, Wrapper: Environment.Variables.AWS_LAMBDA_EXEC_WRAPPER, Timeout: Timeout, Arch: Architectures}" <br>
  --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
# 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
# Habilitar debug temporariamente
ENV_ATUAL=$(aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" --region "$REGIAO" <br>
  --query "Environment.Variables" --output json)

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

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

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

# Verificar logs de debug da extension
aws logs filter-log-events <br>
  --log-group-name "/aws/lambda/$FUNCTION_NAME" <br>
  --filter-pattern "elven" <br>
  --start-time $(date -v-5M +%s000 2>/dev/null || date -d '5 minutes ago' +%s000) <br>
  --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.