Appearance
Resilience & Observability
Tratamento de erros, logs estruturados, alarmes e tracing.
Error Boundary
Arquivo: src/common/error-boundary.ts
Toda invocação do Lambda passa por withErrorBoundary:
typescript
export const handler = (event: APIGatewayProxyEventV2) =>
withErrorBoundary(logger, () => route(event))Tradução exceção → HTTP:
| Exceção | Status | Body |
|---|---|---|
ValidationError | 400 | { "error": <message> } |
UnauthorizedError | 401 | { "error": <message ou 'Unauthorized'> } |
BotError(message, statusCode) | statusCode | { "error": <message> } |
Error genérico | 500 | { "error": "Internal server error" } (sem leak) |
| Não-Error lançado (string, number) | 500 | { "error": "Internal server error" } |
Quando lançar cada exceção
ValidationError: payload externo malformado (body vazio, schema quebrado)UnauthorizedError: reservado para Fase 4+BotError: falhas em integrações controladas (ex.:ZapiClientlança 502)Errorcru, bugs ou estados inesperados; mascarado automaticamente
Erros da API NestJS → Mensagem ao Usuário
ContrasyncApiClient faz uma distinção importante:
| HTTP status do upstream | Exceção lançada | Tratamento no BotRouterService |
|---|---|---|
4xx (400, 401, 403, 404, 409) | ValidationError com mensagem do upstream | replyApiError envia a mensagem como resposta via Z-API e devolve 200 |
| 5xx, timeout, network | BotError 502 com mensagem genérica | Bubble up para o boundary → 500 mascarado |
Isso garante que erros de negócio (ex.: "Nenhum usuário encontrado para esse email") cheguem ao usuário final em linguagem clara, enquanto erros operacionais ficam invisíveis para ele e visíveis para a operação no CloudWatch.
Logger
Arquivo: src/common/logger.ts
Formato: JSON Lines via console.log/warn/error. CloudWatch indexa cada linha.
| Nível | Quando usar | Canal |
|---|---|---|
info | Eventos esperados (dispatch, reply) | console.log |
warn | Payloads ignorados, falhas previstas antes de throw | console.warn |
error | Erros inesperados capturados pelo boundary | console.error |
Regra: warn antes de throw
Todo throw deve ser precedido por logger.warn/error com contexto. A mensagem do log contém detalhes do upstream; a exceção que sobe tem mensagem genérica.
Loggers existentes e amostras de output
handler (src/handler.ts)
Erro inesperado pego pelo boundary:
json
{"level":"ERROR","logger":"handler","message":"Unhandled error","error":{"name":"SyntaxError","message":"Unexpected token 'o', \"not-json{{{\" is not valid JSON","stack":"..."}}BotError traduzido pelo boundary:
json
{"level":"WARN","logger":"handler","message":"502 Falha ao comunicar com a API do Contrasync"}InboundWebhookHandler (src/handlers/inbound-webhook.handler.ts)
json
{"level":"WARN","logger":"InboundWebhookHandler","message":"Webhook payload sem messageId ou phone ignorado"}
{"level":"INFO","logger":"InboundWebhookHandler","message":"Mensagem do próprio número ignorada","messageId":"MSG-001"}
{"level":"INFO","logger":"InboundWebhookHandler","message":"Mensagem duplicada ignorada","messageId":"MSG-001"}BotRouterService (src/services/bot-router.service.ts)
json
{"level":"INFO","logger":"BotRouterService","message":"Routing reply","phone":"5511999998888"}ContrasyncApiClient (src/adapters/contrasync-api/contrasync-api.client.ts)
json
{"level":"WARN","logger":"ContrasyncApiClient","message":"Falha em issueOtp: Nenhum usuário encontrado para esse email.","status":404}
{"level":"WARN","logger":"ContrasyncApiClient","message":"Falha em verifyOtp: network down","status":null}ZapiClient (src/adapters/zapi/zapi.client.ts)
json
{"level":"WARN","logger":"ZapiClient","message":"Falha ao enviar mensagem para 5511999998888: network down"}Como ler logs em produção
bash
aws logs tail /aws/lambda/contrasync-whatsapp-bot-prod --follow --format shortFiltrar por número:
bash
aws logs tail /aws/lambda/contrasync-whatsapp-bot-prod --follow \
--filter-pattern '{ $.phone = "5511999998888" }'Filtrar por erro:
bash
aws logs tail /aws/lambda/contrasync-whatsapp-bot-prod --follow \
--filter-pattern '{ $.level = "ERROR" }'Filtrar por messageId (debug de duplicação):
bash
aws logs tail /aws/lambda/contrasync-whatsapp-bot-prod --follow \
--filter-pattern '{ $.messageId = "MSG-001" }'Tracing (X-Ray)
NodejsFunction configurada com tracing: Tracing.ACTIVE. CDK aplica as IAM policies automaticamente.
X-Ray gera segments para:
- A invocação do Lambda
- Cada chamada axios para Z-API e Contrasync API (precisa adicionar
aws-xray-sdkno futuro para enriquecer, hoje está apenas no nível do Lambda)
Útil para correlacionar latência entre API Gateway, Lambda, Z-API e Contrasync API.
CloudWatch Alarms
Provisionados em infra/whatsapp-bot-stack.ts:
| Alarm | Métrica | Threshold | Janela |
|---|---|---|---|
BotErrorsAlarm | AWS/Lambda/Errors | ≥ 3 | 5 min |
BotThrottlesAlarm | AWS/Lambda/Throttles | ≥ 1 | 5 min |
Ambos com treatMissingData: NOT_BREACHING para não disparar em janelas sem invocação.
Sem subscription SNS hoje. Os alarms aparecem no console do CloudWatch; integração com Slack/SNS é trabalho futuro.
Source Maps em Produção
sourceMap: true no esbuild + NODE_OPTIONS=--enable-source-maps em runtime → stack traces no CloudWatch apontam para arquivos .ts originais, não para o bundle minificado.
Documento atualizado em Maio 2026