Instrumentando AWS Lambda com Elven Observability


Sumário

  • Quando usar este guia
  • Arquitetura
  • Pré-requisitos
  • Quick start
  • Passo 1 - Coletar dados da função
  • Passo 2 - Escolher as layers
  • Passo 3 - Aplicar as layers com segurança
  • Passo 4 - Configurar variáveis de ambiente
  • Passo 5 - Validar ponta a ponta
  • Referência de versões e ARNs
  • Referência de variáveis de ambiente
  • Scripts completos
  • Migração de funções antigas com variáveis LOKI
  • Boas práticas
  • Troubleshooting
  • FAQ

Quando usar este guia

Use este guia quando você quer instrumentar funções Lambda manualmente, por AWS CLI, Console AWS, Terraform, CloudFormation, SAM, CDK ou qualquer ferramenta que permita definir layers e variáveis de ambiente.

Se o projeto usa Serverless Framework, prefira o plugin da Elven. Ele injeta layers, versões e variáveis automaticamente:

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

Este guia manual é útil para:

  • funções já existentes em produção;
  • ambientes que não usam Serverless Framework;
  • rollout controlado por função;
  • validações e troubleshooting;
  • IaC que precisa declarar explicitamente cada layer e variável.

Arquitetura

A instrumentação usa duas layers na Lambda:

Layer Responsabilidade Para onde envia
OpenTelemetry Lambda Layer Auto-instrumenta o runtime e gera traces, spans e, quando habilitado, métricas Collector OTLP do cliente
Elven Lambda Log Extension Captura stdout, stderr e logs de plataforma pela AWS Lambda Logs API Collector OTLP do cliente

Fluxo completo:

┌────────────────────────────────────────────────────────────────────┐
│                            AWS Lambda                              │
│                                                                    │
│  ┌────────────────────────────┐   ┌─────────────────────────────┐  │
│  │ OpenTelemetry Runtime Layer│   │ Elven Lambda Log Extension  │  │
│  │                            │   │                             │  │
│  │ - instrumenta HTTP/DB/etc. │   │ - assina a Lambda Logs API  │  │
│  │ - cria traces/spans        │   │ - captura stdout/stderr     │  │
│  │ - exporta OTLP             │   │ - exporta logs OTLP         │  │
│  └──────────────┬─────────────┘   └──────────────┬──────────────┘  │
│                 │                                │                 │
└─────────────────┼────────────────────────────────┼─────────────────┘
                  │                                │
                  └──────────────┬─────────────────┘
                                 ▼
                    Collector OpenTelemetry do cliente
                    OTLP HTTP :4318 ou endpoint privado equivalente
                                 │
                ┌────────────────┼────────────────┐
                ▼                ▼                ▼
              Tempo            Mimir             Loki
             traces           métricas           logs

Pontos importantes:

  • O Collector é do cliente, implantado na infraestrutura do cliente.
  • O endpoint usado na Lambda é OTEL_EXPORTER_OTLP_ENDPOINT.
  • Logs, traces e métricas seguem o mesmo caminho lógico: Lambda -> Collector do cliente -> backends.
  • Credenciais de Loki, Tempo ou Mimir ficam no Collector, não na Lambda.
  • O tenant é enviado como atributo de recurso (tenant.id) para o Collector aplicar roteamento/política.

Pré-requisitos

Na máquina de quem vai aplicar:

  • AWS CLI v2 configurado para a conta da função.
  • jq instalado.
  • Permissão para ler e atualizar configuração de Lambda:
  • lambda:GetFunctionConfiguration
  • lambda:UpdateFunctionConfiguration
  • lambda:GetLayerVersion
  • Permissão para ler logs no CloudWatch durante validação:
  • logs:FilterLogEvents

Na infraestrutura do cliente:

  • Collector OpenTelemetry implantado e acessível pela Lambda.
  • Receiver OTLP HTTP habilitado, normalmente em :4318.
  • Pipeline logs habilitado no Collector.
  • Exporters server-side configurados para os backends da Elven.
  • Roteamento de tenant configurado no Collector, por endpoint dedicado ou por tenant.id.

Na Lambda:

  • Função existente.
  • Runtime suportado pela layer OTel quando quiser traces automáticos:
  • Node.js
  • Python
  • Java
  • Ruby
  • Para logs, a Elven Lambda Log Extension é runtime-agnostic. Ela funciona também com Go, .NET, provided.al2, provided.al2023 e outros runtimes porque lê logs pela AWS Lambda Logs API.

Dados que você precisa saber:

Dado Exemplo Observação
Nome da função orders-api-prod Nome real da Lambda
Região AWS us-east-1 Região onde a função está
Runtime lógico nodejs nodejs, python, javaagent, javawrapper ou ruby
Endpoint do Collector http://otel-collector.internal:4318 Endpoint do Collector do cliente
Tenant Elven cliente-a Vai em tenant.id; não é segredo
Ambiente production Usado em OTEL_RESOURCE_ATTRIBUTES

Quick start

Para uma função Node.js em us-east-1, x86_64:

FUNCTION_NAME="minha-funcao"
REGION="us-east-1"
STAGE="production"
RUNTIME="nodejs"
ELVEN_TENANT="meu-tenant"
COLLECTOR_ENDPOINT="http://otel-collector.internal:4318"

Adicione as duas layers:

aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --layers <br>
    "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-nodejs-0_22_0:1" <br>
    "arn:aws:lambda:${REGION}:911167927290:layer:elven-lambda-log-extension:9"

Configure as variáveis principais:

ENV_PAYLOAD=$(jq -n <br>
  --arg service "$FUNCTION_NAME" <br>
  --arg stage "$STAGE" <br>
  --arg tenant "$ELVEN_TENANT" <br>
  --arg endpoint "$COLLECTOR_ENDPOINT" '
  {
    Variables: {
      AWS_LAMBDA_EXEC_WRAPPER: "/opt/otel-handler",
      OTEL_SERVICE_NAME: $service,
      OTEL_EXPORTER_OTLP_ENDPOINT: $endpoint,
      OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
      OTEL_RESOURCE_ATTRIBUTES: ("service.name=" + $service + ",environment=" + $stage + ",tenant.id=" + $tenant),
      OTEL_TRACES_SAMPLER: "always_on",
      OTEL_PROPAGATORS: "tracecontext,baggage,xray",
      OTEL_LAMBDA_TRACE_MODE: "capture",
      EXPORTER_MODE: "otlp",
      DEBUG: "false"
    }
  }
')

aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --environment "$ENV_PAYLOAD"

Esse exemplo é propositalmente curto, mas ele substitui layers e variáveis existentes. Para produção, use os scripts seguros abaixo, que fazem merge e preservam configuração existente.


Passo 1 - Coletar dados da função

Leia a configuração atual antes de alterar qualquer coisa:

FUNCTION_NAME="minha-funcao"
REGION="us-east-1"

aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --query '{
    FunctionName: FunctionName,
    Runtime: Runtime,
    Architectures: Architectures,
    Timeout: Timeout,
    MemorySize: MemorySize,
    Layers: Layers[].Arn,
    Environment: Environment.Variables
  }' <br>
  --output json

Confira especialmente:

  • Runtime: ajuda a escolher a layer OTel.
  • Architectures: x86_64 ou arm64.
  • Layers: para evitar duplicar layer antiga.
  • Environment.Variables: para preservar variáveis já existentes.
  • Timeout: se estiver muito baixo, ajuste antes do rollout.

Passo 2 - Escolher as layers

Você precisa de:

  1. Uma layer OTel oficial conforme o runtime.
  2. Uma layer Elven Log Extension conforme arquitetura e região.

Runtime -> Layer OTel

Versões verificadas nas releases oficiais do projeto open-telemetry/opentelemetry-lambda em 2026-05-15. As releases mais recentes usadas aqui foram publicadas em 2026-05-08.

Runtime Use quando ARN
Node.js Funções Node.js arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-nodejs-0_22_0:1
Python Funções Python arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-python-0_20_0:1
Java Agent Java com auto-instrumentação arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-javaagent-0_20_0:1
Java Wrapper Java com wrapper/manual arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-javawrapper-0_20_0:1
Ruby Funções Ruby arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-ruby-0_14_0:1

Fonte oficial: open-telemetry/opentelemetry-lambda releases.

Arquitetura e região -> Elven Log Extension

As versões abaixo foram publicadas pela Elven no account 911167927290 em 2026-05-15.

Região x86_64 arm64
us-east-1 arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension:9 arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension-arm64:8
us-east-2 arn:aws:lambda:us-east-2:911167927290:layer:elven-lambda-log-extension:9 arn:aws:lambda:us-east-2:911167927290:layer:elven-lambda-log-extension-arm64:8
us-west-2 arn:aws:lambda:us-west-2:911167927290:layer:elven-lambda-log-extension:9 arn:aws:lambda:us-west-2:911167927290:layer:elven-lambda-log-extension-arm64:8
eu-west-1 arn:aws:lambda:eu-west-1:911167927290:layer:elven-lambda-log-extension:8 arn:aws:lambda:eu-west-1:911167927290:layer:elven-lambda-log-extension-arm64:8
sa-east-1 arn:aws:lambda:sa-east-1:911167927290:layer:elven-lambda-log-extension:8 arn:aws:lambda:sa-east-1:911167927290:layer:elven-lambda-log-extension-arm64:8

Para uma região que não esteja na tabela, confirme com a Elven antes de aplicar. A layer de logs precisa existir na mesma região da função Lambda.

Sobre a layer "collector" oficial

As releases oficiais também publicam:

arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-collector-<amd64|arm64>-0_22_0:1

No padrão Elven, não use essa layer por padrão.

O Collector da Elven para esse fluxo é um serviço de infraestrutura no ambiente do cliente, não um Collector rodando dentro de cada Lambda. Isso reduz overhead de cold start e mantém credenciais dos backends fora das funções.

Use a layer oficial de Collector apenas em cenários avançados e explicitamente desenhados para rodar Collector dentro da Lambda.


Passo 3 - Aplicar as layers com segurança

O parâmetro --layers da AWS CLI substitui a lista inteira de layers. O script abaixo:

  • preserva layers que não são da Elven/OpenTelemetry Lambda runtime;
  • remove versões antigas da Elven Log Extension;
  • remove versões antigas das layers OTel oficiais de runtime;
  • adiciona a versão correta para runtime, região e arquitetura.
#!/usr/bin/env bash
set -euo pipefail

FUNCTION_NAME="minha-funcao"
REGION="us-east-1"
RUNTIME="nodejs" # nodejs | python | javaagent | javawrapper | ruby

function otel_layer_for_runtime() {
  case "$1" in
    nodejs)      echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-nodejs-0_22_0:1" ;;
    python)      echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-python-0_20_0:1" ;;
    javaagent)   echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-javaagent-0_20_0:1" ;;
    javawrapper) echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-javawrapper-0_20_0:1" ;;
    ruby)        echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-ruby-0_14_0:1" ;;
    *)
      echo "Runtime inválido: $1" >&2
      return 1
      ;;
  esac
}

function elven_log_layer_for_arch_region() {
  local arch="$1"

  if [ "$arch" = "arm64" ]; then
    echo "arn:aws:lambda:${REGION}:911167927290:layer:elven-lambda-log-extension-arm64:8"
    return
  fi

  case "$REGION" in
    us-east-1|us-east-2|us-west-2)
      echo "arn:aws:lambda:${REGION}:911167927290:layer:elven-lambda-log-extension:9"
      ;;
    eu-west-1|sa-east-1)
      echo "arn:aws:lambda:${REGION}:911167927290:layer:elven-lambda-log-extension:8"
      ;;
    *)
      echo "Região sem versão Elven Log Extension documentada: $REGION" >&2
      return 1
      ;;
  esac
}

CONFIG=$(aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --output json)

ARCH=$(echo "$CONFIG" | jq -r '.Architectures[0] // "x86_64"')
OTEL_LAYER=$(otel_layer_for_runtime "$RUNTIME")
LOG_LAYER=$(elven_log_layer_for_arch_region "$ARCH")

FINAL_LAYERS_JSON=$(echo "$CONFIG" | jq <br>
  --arg otel "$OTEL_LAYER" <br>
  --arg log "$LOG_LAYER" '
    def is_elven_log_layer:
      test(":911167927290:layer:elven-lambda-log-extension(-arm64)?:");
    def is_otel_runtime_layer:
      test(":184161586896:layer:opentelemetry-(nodejs|python|javaagent|javawrapper|ruby)-");

    ([.Layers[]?.Arn] // [])
    | map(select((is_elven_log_layer or is_otel_runtime_layer) | not))
    | . + [$otel, $log]
    | unique
  ')

echo "Arquitetura detectada: $ARCH"
echo "Layer OTel: $OTEL_LAYER"
echo "Layer logs: $LOG_LAYER"

aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --layers "$FINAL_LAYERS_JSON" <br>
  --output json | jq '{FunctionName, LastModified, Layers: [.Layers[].Arn]}'

Passo 4 - Configurar variáveis de ambiente

O parâmetro --environment também substitui todas as variáveis da função. Use merge seguro.

Variáveis principais

Variável Exemplo Obrigatória? Observação
AWS_LAMBDA_EXEC_WRAPPER /opt/otel-handler Sim para traces automáticos Inicializa a layer OTel antes do handler
OTEL_SERVICE_NAME orders-api-prod Sim Nome do serviço em traces e logs
OTEL_EXPORTER_OTLP_ENDPOINT http://otel-collector.internal:4318 Sim Endpoint do Collector do cliente
OTEL_EXPORTER_OTLP_PROTOCOL http/protobuf Recomendado Mantém o protocolo explícito
OTEL_RESOURCE_ATTRIBUTES service.name=orders-api-prod,environment=prod,tenant.id=cliente-a Sim tenant.id é obrigatório no modo OTLP da Elven Log Extension
OTEL_TRACES_SAMPLER always_on Recomendado Ajuste para traceidratio em alto volume
OTEL_PROPAGATORS tracecontext,baggage,xray Recomendado Mantém compatibilidade com contexto AWS/X-Ray
OTEL_LAMBDA_TRACE_MODE capture Recomendado Captura o contexto da invocação Lambda
EXPORTER_MODE otlp Sim para logs via Collector Garante que logs vão para OTLP, não Loki direto
DEBUG false Não Use true só em troubleshooting curto

O que não configurar mais na Lambda

Não configure estas variáveis em novas implantações:

LOKI_URL
LOKI_TENANT_ID
LOKI_AUTH_TOKEN

Essas credenciais e headers pertencem ao Collector do cliente. A Lambda deve conhecer apenas:

  • endpoint OTLP do Collector;
  • identidade do serviço;
  • tenant.id como atributo de roteamento;
  • opcionalmente um header de autenticação para falar com o Collector, se o cliente exigir.

Auth Lambda -> Collector

O padrão recomendado é Collector em rede privada/VPC-only, sem token na Lambda.

Se o cliente exigir autenticação entre Lambda e Collector, use:

OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer%20TOKEN"

Use headers apenas para autenticar no Collector do cliente. Não coloque token do Loki ou de backend final nessa variável.

Script seguro para aplicar envs

#!/usr/bin/env bash
set -euo pipefail

FUNCTION_NAME="minha-funcao"
REGION="us-east-1"
STAGE="production"
RUNTIME="nodejs" # nodejs | python | javaagent | javawrapper | ruby

ELVEN_TENANT="meu-tenant"
COLLECTOR_ENDPOINT="http://otel-collector.internal:4318"
SERVICE_NAME="$FUNCTION_NAME"

# Opcional. Use apenas se o Collector do cliente exigir auth.
OTEL_EXPORTER_OTLP_HEADERS="${OTEL_EXPORTER_OTLP_HEADERS:-}"

CONFIG=$(aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --output json)

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

ELVEN_VARS=$(jq -n <br>
  --arg service "$SERVICE_NAME" <br>
  --arg stage "$STAGE" <br>
  --arg tenant "$ELVEN_TENANT" <br>
  --arg endpoint "$COLLECTOR_ENDPOINT" '
  {
    AWS_LAMBDA_EXEC_WRAPPER: "/opt/otel-handler",
    OTEL_SERVICE_NAME: $service,
    OTEL_EXPORTER_OTLP_ENDPOINT: $endpoint,
    OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
    OTEL_RESOURCE_ATTRIBUTES: ("service.name=" + $service + ",environment=" + $stage + ",tenant.id=" + $tenant),
    OTEL_TRACES_SAMPLER: "always_on",
    OTEL_PROPAGATORS: "tracecontext,baggage,xray",
    OTEL_LAMBDA_TRACE_MODE: "capture",
    EXPORTER_MODE: "otlp",
    DEBUG: "false"
  }
')

case "$RUNTIME" in
  nodejs)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_NODE_ENABLED_INSTRUMENTATIONS: "http,express,graphql,grpc,hapi,ioredis,koa,mongodb,mysql,net,pg,redis,memcached,mongoose,amqplib,kafkajs,knex,mysql2,nestjs-core,pino,restify,socket.io,undici,winston"
    }')
    ;;
  python)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_PYTHON_DISABLED_INSTRUMENTATIONS: "",
      OTEL_PYTHON_LOG_CORRELATION: "true"
    }')
    ;;
  javaagent|javawrapper)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED: "true"
    }')
    ;;
  ruby)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_RUBY_DISABLED_INSTRUMENTATIONS: ""
    }')
    ;;
  *)
    echo "Runtime inválido: $RUNTIME" >&2
    exit 1
    ;;
esac

if [ -n "$OTEL_EXPORTER_OTLP_HEADERS" ]; then
  ELVEN_VARS=$(echo "$ELVEN_VARS" | jq --arg headers "$OTEL_EXPORTER_OTLP_HEADERS" '. + {
    OTEL_EXPORTER_OTLP_HEADERS: $headers
  }')
fi

ENV_FINAL=$(jq -s '.[0] * .[1]' <(echo "$ENV_ATUAL") <(echo "$ELVEN_VARS"))
ENV_PAYLOAD=$(jq -n --argjson vars "$ENV_FINAL" '{Variables: $vars}')

aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --environment "$ENV_PAYLOAD" <br>
  --output json | jq '{FunctionName, LastModified, Environment: .Environment.Variables}'

Passo 5 - Validar ponta a ponta

1. Confirme a configuração aplicada

aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --query '{
    FunctionName: FunctionName,
    Runtime: Runtime,
    Architectures: Architectures,
    Layers: Layers[].Arn,
    Env: Environment.Variables
  }' <br>
  --output json

Checklist local na configuração:

  • Existe uma layer OTel do runtime correto.
  • Existe uma layer elven-lambda-log-extension compatível com a arquitetura.
  • AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler.
  • OTEL_EXPORTER_OTLP_ENDPOINT aponta para o Collector do cliente.
  • OTEL_RESOURCE_ATTRIBUTES contém tenant.id=<tenant>.
  • EXPORTER_MODE=otlp.
  • Não há LOKI_AUTH_TOKEN na Lambda.

2. Invoque a função

aws lambda invoke <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --payload '{"elven_validation":true}' <br>
  --cli-binary-format raw-in-base64-out <br>
  /tmp/lambda-response.json

cat /tmp/lambda-response.json

3. Verifique CloudWatch

START_TIME=$((($(date +%s) - 600) * 1000))

aws logs filter-log-events <br>
  --log-group-name "/aws/lambda/$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --start-time "$START_TIME" <br>
  --query "events[?contains(message, 'Elven') || contains(message, 'elven') || contains(message, 'OTLP') || contains(message, 'Extension')].message" <br>
  --output text

Você deve ver a função executando normalmente e, em caso de erro de configuração, mensagens da extension.

4. Verifique o Collector do cliente

Do lado do Collector, procure por entrada OTLP:

  • traces em /v1/traces;
  • logs em /v1/logs;
  • tenant.id presente nos resource attributes;
  • erros de export para Loki/Tempo/Mimir.

Se o Collector usa Kubernetes:

kubectl logs -n observability deploy/otel-collector --since=10m | grep -Ei "lambda|otlp|logs|traces|error"

Se usa VM/systemd:

journalctl -u otel-collector --since "10 minutes ago" | grep -Ei "lambda|otlp|logs|traces|error"

5. Verifique na Elven Observability

No Grafana/Elven:

  • procure traces pelo service.name;
  • confirme que spans da Lambda aparecem;
  • procure logs pelo mesmo service.name ou atributos equivalentes;
  • confirme que o tenant correto vê os dados.

Com Loki OTLP nativo, os labels finais podem variar conforme a configuração do Collector/Loki. O padrão da Elven preserva os dados como resource attributes, como:

  • service.name;
  • faas.name;
  • function.name;
  • cloud.region;
  • tenant.id;
  • aws.lambda.log.type.

Não dependa de labels antigos como function_name em novas consultas, a menos que o Collector tenha uma transformação explícita para isso.


Referência de versões e ARNs

OpenTelemetry Lambda Layers

Fonte: open-telemetry/opentelemetry-lambda releases.

Componente Release ARN
Node.js layer-nodejs/0.22.0 arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-nodejs-0_22_0:1
Python layer-python/0.20.0 arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-python-0_20_0:1
Java Agent layer-javaagent/0.20.0 arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-javaagent-0_20_0:1
Java Wrapper layer-javaagent/0.20.0 arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-javawrapper-0_20_0:1
Ruby layer-ruby/0.14.0 arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-ruby-0_14_0:1
Collector, avançado layer-collector/0.22.0 arn:aws:lambda:{REGION}:184161586896:layer:opentelemetry-collector-<amd64\|arm64>-0_22_0:1

Elven Lambda Log Extension

Região Arquitetura ARN
us-east-1 x86_64 arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension:9
us-east-1 arm64 arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension-arm64:8
us-east-2 x86_64 arn:aws:lambda:us-east-2:911167927290:layer:elven-lambda-log-extension:9
us-east-2 arm64 arn:aws:lambda:us-east-2:911167927290:layer:elven-lambda-log-extension-arm64:8
us-west-2 x86_64 arn:aws:lambda:us-west-2:911167927290:layer:elven-lambda-log-extension:9
us-west-2 arm64 arn:aws:lambda:us-west-2:911167927290:layer:elven-lambda-log-extension-arm64:8
eu-west-1 x86_64 arn:aws:lambda:eu-west-1:911167927290:layer:elven-lambda-log-extension:8
eu-west-1 arm64 arn:aws:lambda:eu-west-1:911167927290:layer:elven-lambda-log-extension-arm64:8
sa-east-1 x86_64 arn:aws:lambda:sa-east-1:911167927290:layer:elven-lambda-log-extension:8
sa-east-1 arm64 arn:aws:lambda:sa-east-1:911167927290:layer:elven-lambda-log-extension-arm64:8

Referência de variáveis de ambiente

Obrigatórias no padrão Elven OTLP

Variável Valor recomendado Componente
AWS_LAMBDA_EXEC_WRAPPER /opt/otel-handler OTel layer
OTEL_SERVICE_NAME projeto-stage-funcao OTel layer e correlação
OTEL_EXPORTER_OTLP_ENDPOINT endpoint do Collector do cliente OTel layer e Elven Log Extension
OTEL_RESOURCE_ATTRIBUTES service.name=...,environment=...,tenant.id=... OTel resource e roteamento
EXPORTER_MODE otlp Elven Log Extension

Recomendadas

Variável Default recomendado Descrição
OTEL_EXPORTER_OTLP_PROTOCOL http/protobuf Protocolo OTLP explícito
OTEL_TRACES_SAMPLER always_on Captura todos os traces
OTEL_TRACES_SAMPLER_ARG vazio Use com traceidratio
OTEL_PROPAGATORS tracecontext,baggage,xray Propagação de contexto
OTEL_LAMBDA_TRACE_MODE capture Captura contexto Lambda
DEBUG false Debug da Elven Log Extension

Runtime-specific

Runtime Variável Uso
Node.js OTEL_NODE_ENABLED_INSTRUMENTATIONS Lista de instrumentações habilitadas
Node.js OTEL_NODE_DISABLED_INSTRUMENTATIONS Lista de instrumentações desabilitadas
Python OTEL_PYTHON_DISABLED_INSTRUMENTATIONS Lista de instrumentações desabilitadas
Python OTEL_PYTHON_LOG_CORRELATION Correlaciona logs com trace IDs quando aplicável
Java OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED Habilita instrumentações por padrão
Java OTEL_JAVAAGENT_DEBUG Debug do Java agent
Ruby OTEL_RUBY_DISABLED_INSTRUMENTATIONS Lista de instrumentações desabilitadas

Elven Log Extension - tuning avançado

Variável Default Descrição
BUFFER_SIZE automático por memória Tamanho do buffer interno
BATCH_SIZE automático por memória Quantidade de logs por envio
BATCH_TIMEOUT 1s Tempo máximo antes de enviar batch parcial
HTTP_TIMEOUT 10s Timeout para envio OTLP
RETRY_MAX 2 Tentativas por batch
RETRY_BACKOFF 100ms Backoff entre tentativas
CB_THRESHOLD 5 Threshold do circuit breaker legado

Defaults automáticos:

Memória da Lambda BUFFER_SIZE BATCH_SIZE
< 512 MB 1000 50
512-1023 MB 5000 500
>= 1024 MB 10000 500

Scripts completos

Script único para instrumentar uma função

Este script aplica layers e envs no padrão Elven OTLP.

#!/usr/bin/env bash
set -euo pipefail

# ============================================
# Configure estes valores
# ============================================
FUNCTION_NAME="${FUNCTION_NAME:-minha-funcao-api}"
REGION="${REGION:-us-east-1}"
STAGE="${STAGE:-production}"
RUNTIME="${RUNTIME:-nodejs}" # nodejs | python | javaagent | javawrapper | ruby
ELVEN_TENANT="${ELVEN_TENANT:-meu-tenant}"
COLLECTOR_ENDPOINT="${COLLECTOR_ENDPOINT:-http://otel-collector.internal:4318}"
SERVICE_NAME="${SERVICE_NAME:-$FUNCTION_NAME}"

# Opcional. Use somente se o Collector exigir auth.
OTEL_EXPORTER_OTLP_HEADERS="${OTEL_EXPORTER_OTLP_HEADERS:-}"
# ============================================

function otel_layer_for_runtime() {
  case "$1" in
    nodejs)      echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-nodejs-0_22_0:1" ;;
    python)      echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-python-0_20_0:1" ;;
    javaagent)   echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-javaagent-0_20_0:1" ;;
    javawrapper) echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-javawrapper-0_20_0:1" ;;
    ruby)        echo "arn:aws:lambda:${REGION}:184161586896:layer:opentelemetry-ruby-0_14_0:1" ;;
    *) echo "Runtime inválido: $1" >&2; return 1 ;;
  esac
}

function elven_log_layer_for_arch_region() {
  local arch="$1"
  if [ "$arch" = "arm64" ]; then
    echo "arn:aws:lambda:${REGION}:911167927290:layer:elven-lambda-log-extension-arm64:8"
    return
  fi

  case "$REGION" in
    us-east-1|us-east-2|us-west-2) echo "arn:aws:lambda:${REGION}:911167927290:layer:elven-lambda-log-extension:9" ;;
    eu-west-1|sa-east-1)           echo "arn:aws:lambda:${REGION}:911167927290:layer:elven-lambda-log-extension:8" ;;
    *) echo "Região sem versão Elven Log Extension documentada: $REGION" >&2; return 1 ;;
  esac
}

echo "==> Lendo configuração atual..."
CONFIG=$(aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --output json)

ARCH=$(echo "$CONFIG" | jq -r '.Architectures[0] // "x86_64"')
OTEL_LAYER=$(otel_layer_for_runtime "$RUNTIME")
LOG_LAYER=$(elven_log_layer_for_arch_region "$ARCH")

echo "    Função: $FUNCTION_NAME"
echo "    Região: $REGION"
echo "    Runtime Elven: $RUNTIME"
echo "    Arquitetura: $ARCH"
echo "    OTel layer: $OTEL_LAYER"
echo "    Log layer: $LOG_LAYER"

FINAL_LAYERS_JSON=$(echo "$CONFIG" | jq <br>
  --arg otel "$OTEL_LAYER" <br>
  --arg log "$LOG_LAYER" '
    def is_elven_log_layer:
      test(":911167927290:layer:elven-lambda-log-extension(-arm64)?:");
    def is_otel_runtime_layer:
      test(":184161586896:layer:opentelemetry-(nodejs|python|javaagent|javawrapper|ruby)-");

    ([.Layers[]?.Arn] // [])
    | map(select((is_elven_log_layer or is_otel_runtime_layer) | not))
    | . + [$otel, $log]
    | unique
  ')

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

ELVEN_VARS=$(jq -n <br>
  --arg service "$SERVICE_NAME" <br>
  --arg stage "$STAGE" <br>
  --arg tenant "$ELVEN_TENANT" <br>
  --arg endpoint "$COLLECTOR_ENDPOINT" '
  {
    AWS_LAMBDA_EXEC_WRAPPER: "/opt/otel-handler",
    OTEL_SERVICE_NAME: $service,
    OTEL_EXPORTER_OTLP_ENDPOINT: $endpoint,
    OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
    OTEL_RESOURCE_ATTRIBUTES: ("service.name=" + $service + ",environment=" + $stage + ",tenant.id=" + $tenant),
    OTEL_TRACES_SAMPLER: "always_on",
    OTEL_PROPAGATORS: "tracecontext,baggage,xray",
    OTEL_LAMBDA_TRACE_MODE: "capture",
    EXPORTER_MODE: "otlp",
    DEBUG: "false"
  }
')

case "$RUNTIME" in
  nodejs)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_NODE_ENABLED_INSTRUMENTATIONS: "http,express,graphql,grpc,hapi,ioredis,koa,mongodb,mysql,net,pg,redis,memcached,mongoose,amqplib,kafkajs,knex,mysql2,nestjs-core,pino,restify,socket.io,undici,winston"
    }')
    ;;
  python)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_PYTHON_DISABLED_INSTRUMENTATIONS: "",
      OTEL_PYTHON_LOG_CORRELATION: "true"
    }')
    ;;
  javaagent|javawrapper)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED: "true"
    }')
    ;;
  ruby)
    ELVEN_VARS=$(echo "$ELVEN_VARS" | jq '. + {
      OTEL_RUBY_DISABLED_INSTRUMENTATIONS: ""
    }')
    ;;
esac

if [ -n "$OTEL_EXPORTER_OTLP_HEADERS" ]; then
  ELVEN_VARS=$(echo "$ELVEN_VARS" | jq --arg headers "$OTEL_EXPORTER_OTLP_HEADERS" '. + {
    OTEL_EXPORTER_OTLP_HEADERS: $headers
  }')
fi

ENV_FINAL=$(jq -s '.[0] * .[1]' <(echo "$ENV_ATUAL") <(echo "$ELVEN_VARS"))
ENV_PAYLOAD=$(jq -n --argjson vars "$ENV_FINAL" '{Variables: $vars}')

echo "==> Aplicando layers e variáveis..."
aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --layers "$FINAL_LAYERS_JSON" <br>
  --environment "$ENV_PAYLOAD" <br>
  --output json | jq '{FunctionName, LastModified, Runtime, Architectures, Layers: [.Layers[].Arn]}'

echo "==> Pronto. Invoque a função e valide traces/logs na Elven Observability."

Instrumentar múltiplas funções

Use este modelo quando várias funções compartilham runtime, região, tenant e Collector:

#!/usr/bin/env bash
set -euo pipefail

REGION="us-east-1"
STAGE="production"
RUNTIME="nodejs"
ELVEN_TENANT="meu-tenant"
COLLECTOR_ENDPOINT="http://otel-collector.internal:4318"

FUNCTIONS=(
  "api-users"
  "api-orders"
  "worker-notifications"
)

for function_name in "${FUNCTIONS[@]}"; do
  echo
  echo "==> Instrumentando $function_name"

  FUNCTION_NAME="$function_name" <br>
  REGION="$REGION" <br>
  STAGE="$STAGE" <br>
  RUNTIME="$RUNTIME" <br>
  ELVEN_TENANT="$ELVEN_TENANT" <br>
  COLLECTOR_ENDPOINT="$COLLECTOR_ENDPOINT" <br>
  SERVICE_NAME="$function_name" <br>
  ./instrument-one-lambda.sh
done

Salve o script anterior como instrument-one-lambda.sh e reutilize.

Remover instrumentação Elven

Remove as layers da Elven/OTel runtime e variáveis adicionadas por este guia, preservando o resto:

#!/usr/bin/env bash
set -euo pipefail

FUNCTION_NAME="minha-funcao"
REGION="us-east-1"

CONFIG=$(aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --output json)

CLEAN_LAYERS_JSON=$(echo "$CONFIG" | jq '
  def is_elven_log_layer:
    test(":911167927290:layer:elven-lambda-log-extension(-arm64)?:");
  def is_otel_runtime_layer:
    test(":184161586896:layer:opentelemetry-(nodejs|python|javaagent|javawrapper|ruby)-");

  ([.Layers[]?.Arn] // [])
  | map(select((is_elven_log_layer or is_otel_runtime_layer) | not))
')

ENV_CLEAN=$(echo "$CONFIG" | jq '
  .Environment.Variables // {}
  | del(
      .AWS_LAMBDA_EXEC_WRAPPER,
      .OTEL_SERVICE_NAME,
      .OTEL_EXPORTER_OTLP_ENDPOINT,
      .OTEL_EXPORTER_OTLP_PROTOCOL,
      .OTEL_EXPORTER_OTLP_HEADERS,
      .OTEL_RESOURCE_ATTRIBUTES,
      .OTEL_TRACES_SAMPLER,
      .OTEL_TRACES_SAMPLER_ARG,
      .OTEL_PROPAGATORS,
      .OTEL_LAMBDA_TRACE_MODE,
      .OTEL_NODE_ENABLED_INSTRUMENTATIONS,
      .OTEL_NODE_DISABLED_INSTRUMENTATIONS,
      .OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
      .OTEL_PYTHON_LOG_CORRELATION,
      .OTEL_INSTRUMENTATION_COMMON_DEFAULT_ENABLED,
      .OTEL_JAVAAGENT_DEBUG,
      .OTEL_RUBY_DISABLED_INSTRUMENTATIONS,
      .EXPORTER_MODE,
      .DEBUG,
      .BUFFER_SIZE,
      .BATCH_SIZE,
      .BATCH_TIMEOUT,
      .HTTP_TIMEOUT,
      .RETRY_MAX,
      .RETRY_BACKOFF,
      .CB_THRESHOLD,
      .LOKI_URL,
      .LOKI_TENANT_ID,
      .LOKI_AUTH_TOKEN
    )
')

ENV_PAYLOAD=$(jq -n --argjson vars "$ENV_CLEAN" '{Variables: $vars}')

aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --layers "$CLEAN_LAYERS_JSON" <br>
  --environment "$ENV_PAYLOAD"

Migração de funções antigas com variáveis LOKI

Versões antigas da Elven Log Extension enviavam logs direto para Loki usando:

LOKI_URL
LOKI_TENANT_ID
LOKI_AUTH_TOKEN

Esse modelo foi substituído pelo padrão OTLP:

EXPORTER_MODE=otlp
OTEL_EXPORTER_OTLP_ENDPOINT=<collector-do-cliente>
OTEL_RESOURCE_ATTRIBUTES=...,tenant.id=<tenant>

Estratégia recomendada:

  1. Atualize a layer para a versão nova.
  2. Configure o Collector do cliente com pipeline logs.
  3. Em uma função piloto, use EXPORTER_MODE=both temporariamente.
  4. Compare volume e exemplos de logs por 24h.
  5. Troque para EXPORTER_MODE=otlp.
  6. Remova LOKI_AUTH_TOKEN da Lambda.
  7. Depois da janela de rollback, remova LOKI_URL e LOKI_TENANT_ID.

Rollback imediato:

EXPORTER_MODE=loki
LOKI_URL=<url-base-loki>
LOKI_TENANT_ID=<tenant>
LOKI_AUTH_TOKEN=<token>

Use rollback apenas durante a migração. O estado desejado é não manter token de Loki na Lambda.


Boas práticas

Nome de serviço

Use nomes estáveis e fáceis de filtrar:

{produto}-{ambiente}-{função}

Exemplos:

checkout-production-api
orders-staging-worker
notifications-production-sender

Mantenha OTEL_SERVICE_NAME e service.name em OTEL_RESOURCE_ATTRIBUTES com o mesmo valor.

Tenant

tenant.id é obrigatório no padrão Elven OTLP:

OTEL_RESOURCE_ATTRIBUTES=service.name=orders-api,environment=production,tenant.id=cliente-a

Ele não é segredo. Ele serve para o Collector aplicar roteamento, isolamento e política de export.

Segurança

  • O Collector deve estar em endpoint privado sempre que possível.
  • Permita acesso apenas das subnets/security groups das Lambdas esperadas.
  • Não coloque token de Loki, Tempo ou Mimir na Lambda.
  • Se precisar autenticar Lambda -> Collector, use token específico do Collector em OTEL_EXPORTER_OTLP_HEADERS.
  • Não exponha o Collector publicamente sem autenticação e allowlist.

Sampling

Para funções críticas ou baixo volume:

OTEL_TRACES_SAMPLER=always_on

Para alto volume:

OTEL_TRACES_SAMPLER=traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1

0.1 significa 10% dos traces.

Cold start

Layers aumentam o tempo de init. Para reduzir impacto:

  • habilite só instrumentações necessárias;
  • evite debug permanente;
  • prefira Collector fora da Lambda;
  • aumente memória quando a função é muito limitada;
  • use provisioned concurrency em funções sensíveis.

Logs

  • Evite console.log em loops muito intensos.
  • Prefira logs estruturados JSON.
  • Não escreva segredos em logs.
  • Se aparecer buffer full, reduza volume de logs ou aumente memória.

Troubleshooting

Função não inicia ou dá timeout no cold start

Verifique:

  • AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler.
  • A layer OTel corresponde ao runtime.
  • A layer Elven Log Extension corresponde à arquitetura.
  • O timeout da função não está baixo demais.
  • Não há duas versões da mesma extension na lista de layers.

Comando útil:

aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --query '{Runtime: Runtime, Timeout: Timeout, Architectures: Architectures, Layers: Layers[].Arn, Wrapper: Environment.Variables.AWS_LAMBDA_EXEC_WRAPPER}' <br>
  --output table

Traces não aparecem

Verifique:

  • A layer OTel está presente.
  • AWS_LAMBDA_EXEC_WRAPPER está correto.
  • OTEL_EXPORTER_OTLP_ENDPOINT aponta para o Collector do cliente.
  • A Lambda consegue acessar o Collector por rede.
  • O Collector tem receiver OTLP HTTP em 4318.
  • O Collector tem pipeline de traces exportando para Tempo.

No Collector:

grep -Ei "v1/traces|traces|exporter|error" /var/log/otel-collector.log

Logs não aparecem

Verifique:

  • A layer elven-lambda-log-extension está presente.
  • EXPORTER_MODE=otlp.
  • OTEL_EXPORTER_OTLP_ENDPOINT está definido.
  • OTEL_RESOURCE_ATTRIBUTES contém tenant.id.
  • O Collector tem pipeline logs.
  • O Collector exporta logs para Loki via configuração server-side.

Não procure LOKI_AUTH_TOKEN na Lambda. No modelo novo, ele não deve estar lá.

Erro tenant.id is required

A extension exige tenant.id em OTLP para evitar logs sem contexto de tenant.

Corrija:

OTEL_RESOURCE_ATTRIBUTES=service.name=minha-funcao,environment=production,tenant.id=meu-tenant

Erro 401/403 ao exportar logs

No modelo novo, 401/403 normalmente acontece do Collector para o backend, não da Lambda para Loki.

Verifique no Collector:

  • header X-Scope-OrgID;
  • token server-side do Loki;
  • endpoint Loki OTLP;
  • roteamento por tenant.id.

Se o erro for Lambda -> Collector, verifique OTEL_EXPORTER_OTLP_HEADERS e a política de rede/autenticação do Collector.

Logs aparecem em CloudWatch, mas não na Elven

CloudWatch sempre recebe logs da Lambda. A Elven Log Extension lê esses logs pela Lambda Logs API e envia para o Collector.

Se CloudWatch tem logs e a Elven não:

  • valide se a extension iniciou;
  • valide se /logs da extension recebeu batches;
  • valide se o Collector recebeu /v1/logs;
  • valide export do Collector para Loki.

Habilite debug por poucos minutos:

ENV_ATUAL=$(aws lambda get-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --query "Environment.Variables" <br>
  --output json)

ENV_DEBUG=$(jq -s '.[0] * {"DEBUG":"true"}' <(echo "$ENV_ATUAL"))
ENV_PAYLOAD=$(jq -n --argjson vars "$ENV_DEBUG" '{Variables: $vars}')

aws lambda update-function-configuration <br>
  --function-name "$FUNCTION_NAME" <br>
  --region "$REGION" <br>
  --environment "$ENV_PAYLOAD"

Depois da validação, volte DEBUG=false.

buffer full

Sintoma: CloudWatch mostra warning de log descartado por buffer cheio.

Causas comuns:

  • função escreve muitos logs em pouco tempo;
  • Collector está lento ou indisponível;
  • timeout/retry segurando o worker;
  • função com pouca memória.

Ações:

  • reduza volume de logs;
  • aumente memória da função;
  • reduza BATCH_SIZE para flush mais frequente;
  • valide latência e disponibilidade do Collector;
  • mantenha HTTP_TIMEOUT razoável para não bloquear a Lambda por muito tempo.

Labels antigos no Loki não funcionam

Consultas antigas podem usar labels como:

{function_name="minha-funcao"}

No fluxo OTLP nativo, use os atributos padronizados:

{service_name="minha-funcao"}

ou filtros por metadata conforme a configuração do Loki/Collector.

Se uma query antiga precisa continuar funcionando, configure transformação no Collector com cuidado para evitar cardinalidade excessiva.


FAQ

A telemetria vai para um endpoint compartilhado da Elven?

Não. A Lambda envia OTLP para o Collector do cliente. Esse Collector fica na infraestrutura do cliente e encaminha para os backends configurados pela Elven.

Preciso colocar LOKI_AUTH_TOKEN na Lambda?

Não no modelo atual. O token de Loki fica server-side no Collector.

Preciso da layer oficial opentelemetry-collector na Lambda?

Não no padrão Elven. O Collector roda como componente de infraestrutura do cliente. A layer de Collector só é indicada para casos avançados.

A Elven Log Extension funciona com Go ou .NET?

Sim para logs. Ela captura logs pela Lambda Logs API e independe do runtime.

Para traces automáticos em Go/.NET, use instrumentação adequada ao runtime ou instrumentação manual. A tabela de layers OTel oficiais deste guia cobre Node.js, Python, Java e Ruby.

Posso usar com função em VPC?

Sim. Garanta rota privada ou NAT/security group para a Lambda alcançar o Collector do cliente.

Posso usar X-Ray junto?

Sim, mantendo xray em OTEL_PROPAGATORS. Em geral, evite habilitar export duplicado para dois sistemas sem necessidade.

Quando a versão mudar, atualize os ARNs da layer OTel no IaC ou scripts de rollout.