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 forarm64, 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
javaagentpara auto-instrumentação (recomendado) oujavawrapperse 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
--layersda 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
--environmentda 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.namecorreto - [ ] Logs aparecem no Loki com os labels
function_nameeregion - [ ] 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_SIZEeBATCH_SIZEsã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_SIZEmenor 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:
AWS_LAMBDA_EXEC_WRAPPERincorreto — Deve ser exatamente/opt/otel-handler. Verifique se a layer OTel está adicionada.- Layer incompatível com a arquitetura — Verifique se a layer corresponde à arquitetura da função (x86_64 vs arm64).
- 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:
- Confirme que
AWS_LAMBDA_EXEC_WRAPPERestá definido como/opt/otel-handler - Confirme que
OTEL_EXPORTER_OTLP_ENDPOINTestá apontando para o seu coletor OTLP - Verifique se
OTEL_SERVICE_NAMEnão está vazio - 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:
LOKI_URLdeve 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- Confirme que
LOKI_TENANT_IDeLOKI_AUTH_TOKENestão preenchidos - Habilite
DEBUG=truee 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:
- Aumentar a memória da função (melhora buffer e batch defaults)
- Ou ajustar manualmente:
BUFFER_SIZE=5000eBATCH_SIZE=200 - Reduzir o volume de logs na função (evitar
console.logem loops)
Cold start muito lento
Sintoma: O cold start aumentou mais de 1 segundo após adicionar as layers.
Soluções:
- Reduzir as instrumentações habilitadas (
OTEL_NODE_ENABLED_INSTRUMENTATIONS) - Aumentar a memória da função (mais CPU = init mais rápido)
- 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:
- Confirme o valor de
LOKI_AUTH_TOKENno painel da Elven - Confirme o valor de
LOKI_TENANT_ID - 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.