Instrumentação Lambda com Serverless Framework e Elven Plugin
Sumário
- Visão geral
- Pré-requisitos
- Instalação
- Quick Start
- Configuração do plugin
- Referência de opções
- Controle por função
- Variáveis de ambiente injetadas
- Layers aplicadas
- Exemplos completos
- Boas práticas
- Troubleshooting
- FAQ
Visão geral
O elven-instrumentation-serverless-plugin automatiza toda a instrumentação das suas funções Lambda. Com uma única configuração no serverless.yml, o plugin:
- Adiciona as layers de OpenTelemetry e Elven Log Extension em cada função
- Injeta todas as variáveis de ambiente necessárias para traces e logs
- Detecta a arquitetura (x86_64/arm64) e aplica as layers corretas
- Gera nomes de serviço padronizados automaticamente (
{service}_{stage}_{function})
┌──────────────────────────────────────────────────────────────┐
│ serverless.yml │
│ │
│ plugins: │
│ - elven-instrumentation-serverless-plugin │
│ │
│ custom: │
│ elvenLayerPlugin: │
│ tenant: "meu-tenant" ──────┐ │
│ token: "meu-token" │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Plugin injeta: │ │
│ │ - 2 layers │ │
│ │ - 14+ env vars │ │
│ └────────┬────────┘ │
│ │ │
│ functions: ▼ │
│ api: ← layers + envs injetados automaticamente │
│ worker: ← layers + envs injetados automaticamente │
│ cron: ← layers + envs injetados automaticamente │
└──────────────────────────────────────────────────────────────┘
| Componente | O que faz |
|---|---|
| Plugin Serverless | Injeta layers e variáveis de ambiente automaticamente no deploy |
| Layer OpenTelemetry | Instrumenta o runtime Node.js — gera traces e spans automaticamente |
| Layer Elven Log Extension | Captura logs da função via Lambda Logs API e envia para Loki |
Pré-requisitos
- Serverless Framework v3 ou v4 instalado
- Node.js 18+
- 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
- API Token
- Endpoint do seu Collector OTLP
Instalação
npm install --save-dev elven-instrumentation-serverless-plugin
Adicione o plugin no serverless.yml:
plugins:
- elven-instrumentation-serverless-plugin
Verificar instalação:
npx serverless print 2>&1 | grep -i elven
Quick Start
1. Configure o plugin no serverless.yml
service: minha-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
plugins:
- elven-instrumentation-serverless-plugin
custom:
elvenLayerPlugin:
tenant: "seu-tenant-id"
token: "seu-api-token"
functions:
hello:
handler: handler.hello
events:
- http:
path: /hello
method: get
2. Deploy normalmente
npx serverless deploy
O plugin automaticamente:
- Adiciona as layers de OTel e Log Extension na função
- Injeta as variáveis de ambiente com nome de serviço
minha-api_dev_hello - Configura o endpoint OTLP e Loki da Elven
Pronto! Ao invocar a função, traces e logs já aparecem na Elven Observability.
Output do plugin no deploy
✅ [Elven] Instrumented function: minha-api_dev_hello (x86_64)
🏗️ [Elven] Architecture: x86_64
📍 [Elven] Region: us-east-1
📦 [Elven] Default layers applied:
- arn:aws:lambda:us-east-1:184161586896:layer:opentelemetry-nodejs-0_20_0:1
- arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension:8
Configuração do plugin
Todas as opções ficam dentro de custom.elvenLayerPlugin no serverless.yml:
custom:
elvenLayerPlugin:
# === Obrigatórios ===
tenant: "seu-tenant-id"
token: "seu-api-token"
# === Opcionais ===
region: "us-east-1" # default: us-east-1
collector_endpoint: "https://otel-collector.minha-infra.com:4318" # URL do seu collector
logs_endpoint: "https://loki.elvenobservability.com" # default: endpoint Elven
architecture: "x86_64" # default: x86_64
log_version: "8" # versão da layer de logs (x86_64)
log_version_arm64: "7" # versão da layer de logs (arm64)
enabled_instrumentations: "http,express,pg" # default: todas
disabled_instrumentations: "" # default: nenhuma
layers: # override completo das layers
- "arn:aws:lambda:us-east-1:..."
- "arn:aws:lambda:us-east-1:..."
Referência de opções
Opções obrigatórias
| Opção | Descrição | Exemplo |
|---|---|---|
tenant |
Tenant ID na Elven Observability | "meu-tenant" |
token |
API Token de autenticação | "eyJhbGci..." |
Opções opcionais
| Opção | Default | Descrição |
|---|---|---|
region |
us-east-1 |
Região AWS das layers |
collector_endpoint |
— (obrigatório) | URL do seu coletor OTLP (ex: https://otel-collector.minha-infra.com:4318) |
logs_endpoint |
https://loki.elvenobservability.com |
URL base do Loki (sem /loki/api/v1/push) |
architecture |
x86_64 |
Arquitetura padrão: x86_64 ou arm64 |
log_version |
8 |
Versão da layer de logs para x86_64 |
log_version_arm64 |
7 |
Versão da layer de logs para arm64 |
enabled_instrumentations |
todas | Instrumentações OTel habilitadas (separadas por vírgula) |
disabled_instrumentations |
nenhuma | Instrumentações OTel a desabilitar |
layers |
auto | Override completo da lista de layers (substitui as layers padrão) |
Ordem de resolução da arquitetura
A arquitetura é resolvida nesta ordem de precedência:
custom.elvenLayerPlugin.architecture- Variável de ambiente
LAMBDA_ARCHITECTURE provider.architecturedo Serverless- Default:
x86_64
Para funções individuais, function.architecture tem prioridade sobre o valor global.
Controle por função
Desabilitar instrumentação em uma função específica
Adicione disableElven: true na função:
functions:
api:
handler: handler.api
# instrumentada normalmente
internal:
handler: handler.internal
disableElven: true
# NÃO será instrumentada
worker:
handler: handler.worker
# instrumentada normalmente
Output no deploy:
✅ [Elven] Instrumented function: minha-api_dev_api (x86_64)
⚠️ [Elven] Skipping instrumentation for function: internal
✅ [Elven] Instrumented function: minha-api_dev_worker (x86_64)
Sobrescrever variáveis por função
O plugin não sobrescreve variáveis de ambiente já definidas na função. Isso permite customizar por função:
functions:
api:
handler: handler.api
# Usa configuração padrão do plugin
heavy-worker:
handler: handler.worker
environment:
OTEL_TRACES_SAMPLER: "traceidratio"
OTEL_TRACES_SAMPLER_ARG: "0.1"
# Sampling de 10% — demais variáveis são injetadas pelo plugin
Funções com arquiteturas diferentes
O plugin detecta a arquitetura por função e aplica as layers corretas:
provider:
architecture: x86_64 # padrão para todas
functions:
api:
handler: handler.api
# usa x86_64 (do provider)
ml-inference:
handler: handler.inference
architecture: arm64
# usa arm64 — layers arm64 aplicadas automaticamente
Variáveis de ambiente injetadas
O plugin injeta as seguintes variáveis em cada função:
OpenTelemetry (Traces)
| Variável | Valor | Descrição |
|---|---|---|
AWS_LAMBDA_EXEC_WRAPPER |
/opt/otel-handler |
Ativa o wrapper OTel no runtime |
OTEL_SERVICE_NAME |
{service}_{stage}_{function} |
Nome do serviço nos traces |
OTEL_TRACES_SAMPLER |
always_on |
Estratégia de sampling |
OTEL_LAMBDA_TRACE_MODE |
capture |
Modo de captura de traces |
OTEL_PROPAGATORS |
tracecontext,baggage,xray |
Propagadores de contexto |
OTEL_RESOURCE_ATTRIBUTES |
service.name=...,environment=... |
Atributos de recurso |
OTEL_EXPORTER_OTLP_ENDPOINT |
collector_endpoint |
Endpoint OTLP |
OTEL_NODE_ENABLED_INSTRUMENTATIONS |
lista de instrumentações | Instrumentações habilitadas |
OTEL_NODE_DISABLED_INSTRUMENTATIONS |
(condicional) | Instrumentações desabilitadas |
Elven Log Extension (Logs)
| Variável | Valor | Descrição |
|---|---|---|
LOKI_URL |
logs_endpoint |
URL base do Loki |
LOKI_TENANT_ID |
tenant |
Tenant ID para o Loki |
LOKI_AUTH_TOKEN |
token |
Token de autenticação |
DEBUG |
false |
Debug da log extension |
Nota sobre
LOKI_URL: O plugin envia apenas a URL base (ex:https://loki.elvenobservability.com). A log extension appenda/loki/api/v1/pushautomaticamente.
Naming convention automática
O OTEL_SERVICE_NAME segue o padrão:
{serviceName}_{stage}_{functionName}
Exemplo: serviço ecommerce, stage prod, função checkout → ecommerce_prod_checkout
Layers aplicadas
O plugin adiciona automaticamente duas layers:
x86_64
| Layer | ARN |
|---|---|
| OpenTelemetry Node.js | arn:aws:lambda:{region}:184161586896:layer:opentelemetry-nodejs-0_20_0:1 |
| Elven Log Extension | arn:aws:lambda:{region}:911167927290:layer:elven-lambda-log-extension:{version} |
arm64
| Layer | ARN |
|---|---|
| OpenTelemetry Node.js | arn:aws:lambda:{region}:184161586896:layer:opentelemetry-nodejs-0_20_0:1 |
| Elven Log Extension | arn:aws:lambda:{region}:911167927290:layer:elven-lambda-log-extension-arm64:{version} |
Override de layers
Para usar layers customizadas (ex: versão diferente ou layer própria):
custom:
elvenLayerPlugin:
tenant: "meu-tenant"
token: "meu-token"
layers:
- "arn:aws:lambda:us-east-1:184161586896:layer:opentelemetry-nodejs-0_20_0:1"
- "arn:aws:lambda:us-east-1:911167927290:layer:elven-lambda-log-extension:8"
Ao definir
layers, a seleção automática por arquitetura é desabilitada. Certifique-se de usar as layers corretas para a arquitetura das suas funções.
Exemplos completos
Exemplo 1 — API REST simples
service: users-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
stage: ${opt:stage, 'dev'}
plugins:
- elven-instrumentation-serverless-plugin
custom:
elvenLayerPlugin:
tenant: "meu-tenant"
token: "meu-api-token"
functions:
getUsers:
handler: src/handlers/users.list
events:
- http:
path: /users
method: get
getUser:
handler: src/handlers/users.get
events:
- http:
path: /users/{id}
method: get
createUser:
handler: src/handlers/users.create
events:
- http:
path: /users
method: post
Exemplo 2 — Microserviço com múltiplas arquiteturas
service: ml-pipeline
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
architecture: x86_64
plugins:
- elven-instrumentation-serverless-plugin
custom:
elvenLayerPlugin:
tenant: "meu-tenant"
token: "meu-api-token"
region: "us-east-1"
functions:
api:
handler: src/api.handler
events:
- http:
path: /predict
method: post
inference:
handler: src/inference.handler
architecture: arm64
memorySize: 1024
timeout: 30
events:
- sqs:
arn: !GetAtt InferenceQueue.Arn
data-ingestion:
handler: src/ingestion.handler
timeout: 300
events:
- schedule: rate(5 minutes)
Exemplo 3 — Instrumentações seletivas com funções excluídas
service: ecommerce
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
plugins:
- elven-instrumentation-serverless-plugin
custom:
elvenLayerPlugin:
tenant: "meu-tenant"
token: "meu-api-token"
enabled_instrumentations: "http,express,pg,redis,undici"
functions:
checkout:
handler: src/checkout.handler
events:
- http:
path: /checkout
method: post
payments:
handler: src/payments.handler
events:
- sqs:
arn: !GetAtt PaymentsQueue.Arn
# Função interna — sem necessidade de observabilidade
migrations:
handler: src/migrations.handler
disableElven: true
events:
- schedule: rate(1 day)
Exemplo 4 — Usando variáveis do Serverless para credenciais
Para não hardcodar credenciais no serverless.yml:
custom:
elvenLayerPlugin:
tenant: ${env:ELVEN_TENANT}
token: ${env:ELVEN_TOKEN}
region: ${self:provider.region}
Deploy passando as variáveis:
ELVEN_TENANT=meu-tenant ELVEN_TOKEN=meu-token npx serverless deploy
Ou usando SSM Parameter Store:
custom:
elvenLayerPlugin:
tenant: ${ssm:/elven/tenant}
token: ${ssm:/elven/token}
Exemplo 5 — Customizar sampling por função
custom:
elvenLayerPlugin:
tenant: "meu-tenant"
token: "meu-token"
functions:
# Função com alto volume → sampling de 10%
high-traffic-api:
handler: src/api.handler
environment:
OTEL_TRACES_SAMPLER: "traceidratio"
OTEL_TRACES_SAMPLER_ARG: "0.1"
events:
- http:
path: /search
method: get
# Função crítica → capturar tudo (default do plugin)
checkout:
handler: src/checkout.handler
events:
- http:
path: /checkout
method: post
Boas práticas
Credenciais seguras
Nunca hardcode credenciais no serverless.yml. Use uma das opções:
# Opção 1: Variáveis de ambiente
tenant: ${env:ELVEN_TENANT}
token: ${env:ELVEN_TOKEN}
# Opção 2: SSM Parameter Store
tenant: ${ssm:/elven/tenant}
token: ${ssm:/elven/token~true} # ~true para decrypt SecureString
# Opção 3: Secrets Manager
tenant: ${ssm:/aws/reference/secretsmanager/elven-tenant}
Instrumentações seletivas
Habilite apenas as instrumentações que o projeto realmente usa. Menos instrumentações = cold start mais rápido:
custom:
elvenLayerPlugin:
enabled_instrumentations: "http,pg,redis,undici"
Instrumentações 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
Timeout da função
As layers adicionam \~200-400ms de overhead no cold start. Certifique-se de que o timeout acomoda isso:
provider:
timeout: 10 # mínimo recomendado
Para funções com múltiplas instrumentações, considere 30 segundos.
Memória
Mais memória = mais CPU = cold start mais rápido. Para funções instrumentadas, recomendamos pelo menos 256 MB:
provider:
memorySize: 256
Stages
O plugin usa o stage do Serverless para compor o nome do serviço e o atributo environment:
# Deploy em staging → nomes: ecommerce_staging_checkout
npx serverless deploy --stage staging
# Deploy em production → nomes: ecommerce_production_checkout
npx serverless deploy --stage production
Troubleshooting
O deploy falha com erro de layer
Sintoma: Erro Layer version arn:aws:lambda:... does not exist.
Causas possíveis:
- Região incorreta — A layer não está disponível na região configurada. Verifique
regionno plugin - Versão inexistente — Verifique se
log_version/log_version_arm64estão corretos
# Verificar se a layer existe na região
aws lambda get-layer-version <br>
--layer-name opentelemetry-nodejs-0_20_0 <br>
--version-number 1 <br>
--region us-east-1 <br>
--query "LayerVersionArn"
A função falha ao iniciar (timeout ou crash)
Sintoma: Runtime.ExitError ou timeout no cold start.
Verificações:
- Timeout muito baixo — Aumente para pelo menos 10 segundos
- Layer incompatível com a arquitetura — Verifique se a função arm64 está recebendo a layer arm64
- Wrapper incorreto — O plugin seta
AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler. Se a layer OTel não estiver presente, isso causa crash
# Verificar configuração da função após deploy
npx serverless info --verbose
Traces não aparecem na Elven
Sintoma: Função executa normalmente, mas nenhum trace aparece.
Verificações:
- Confirme que
collector_endpointestá correto - Confirme que o
tokené válido - Se sobrescreveu
OTEL_TRACES_SAMPLERna função, verifique o valor
# Ver as variáveis de ambiente da função
aws lambda get-function-configuration <br>
--function-name minha-api-dev-hello <br>
--query "Environment.Variables" <br>
--output table
Logs não aparecem no Loki
Sintoma: Traces funcionam, mas logs não aparecem.
Verificações:
logs_endpointdeve 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
tenantetokenestão corretos - Habilite debug temporariamente na função:
functions:
minha-funcao:
handler: handler.handler
environment:
DEBUG: "true"
Depois invoque e verifique os logs no CloudWatch.
Plugin não aparece no output do deploy
Sintoma: Nenhuma mensagem [Elven] no deploy.
Verificações:
- O plugin está na seção
pluginsdoserverless.yml - O pacote está instalado:
npm ls elven-instrumentation-serverless-plugin - A seção
custom.elvenLayerPluginexiste (mesmo que vazia, o plugin precisa ser listado emplugins)
Variáveis de ambiente não são injetadas
Sintoma: A função não tem as variáveis esperadas.
Causa: O plugin não sobrescreve variáveis já definidas na função. Se você definiu OTEL_SERVICE_NAME manualmente na função, o plugin não vai substituir.
Verificação:
npx serverless print --path functions.minhaFuncao.environment
FAQ
Preciso configurar algo em cada função? Não. O plugin instrumenta todas as funções automaticamente. A única configuração necessária é o bloco custom.elvenLayerPlugin com tenant e token.
Posso desabilitar em funções específicas? Sim. Adicione disableElven: true na definição da função.
Funciona com Serverless Framework v4? Sim. O plugin é compatível com v3 e v4 do Serverless Framework.
O plugin funciona com outros runtimes além de Node.js? As layers padrão do plugin são para Node.js. Para outros runtimes (Python, Java, Ruby), use a opção layers para especificar as layers corretas. Consulte a documentação de instrumentação Lambda manual para os ARNs de cada runtime.
O plugin sobrescreve minhas variáveis de ambiente? Não. O plugin só injeta variáveis que não existem na função. Se você definir uma variável manualmente, ela tem prioridade.
O plugin sobrescreve minhas layers? Não. O plugin adiciona as layers sem remover as existentes. Não há duplicação — se a layer já existe, não é adicionada novamente.
Como atualizar a versão das layers? Altere log_version e log_version_arm64 no custom.elvenLayerPlugin. Para a layer OTel, use a opção layers com os ARNs atualizados.
Posso usar com monorepo / múltiplos serverless.yml? Sim. Cada serverless.yml é independente. Use a mesma configuração elvenLayerPlugin em cada um, preferencialmente via variáveis de ambiente ou SSM para evitar duplicação.
Qual o impacto no tamanho do deployment? As layers não contam no limite de 50 MB do pacote de deploy (ZIP). Elas contam no limite total de 250 MB (código + layers descomprimidos).
Posso usar junto com outros plugins do Serverless? Sim. O plugin roda no hook before:package:initialize e não interfere com outros plugins. A ordem no plugins array geralmente não importa.
Como vejo quais variáveis o plugin injetou? Use npx serverless print para ver a configuração completa resolvida, ou verifique direto na AWS:
aws lambda get-function-configuration <br>
--function-name {service}-{stage}-{function} <br>
--query "Environment.Variables"