Vés al contingut

Observabilitat: logs, mètriques i alarmes que serveixen

Una aplicació serverless no té cap servidor on entrar a mirar què passa. No hi ha top, ni tail -f d'un fitxer, ni una màquina que puguis reiniciar. L'única finestra que tens són les dades que la mateixa aplicació deixa: logs, mètriques i traces.

Si no les prepares abans, el primer cop que alguna cosa falli en producció t'assabentaràs per un usuari, i quan vagis a mirar no hi haurà res útil.


Logs: de console.log a JSON​

A les lliçons anteriors hem escrit console.log i hem llegit la línia REPORT. Per desenvolupar, prou. Per a producció, un log en text lliure té un problema: no el pots consultar. «Producte afegit: 4f9a… per a l'usuari 81c…» és fàcil de llegir per a una persona i impossible de filtrar per a una màquina.

Un log estructurat és un objecte JSON per línia:

{"level":"INFO","message":"producte afegit","producte":"4f9a…","cold_start":false,"function_request_id":"c6af…","timestamp":"2026-10-21T18:02:11.483Z"}

La manera més directa de tenir-los a Lambda és Powertools for AWS Lambda, la llibreria oficial:

// src/api.ts
import { Logger } from "@aws-lambda-powertools/logger";
import type { Context } from "aws-lambda";

const logger = new Logger({ serviceName: "llista-compra" });

export const handler = async (event: APIGatewayProxyEventV2WithJWTAuthorizer, context: Context) => {
logger.addContext(context); // afegeix request id, nom de la funció i cold start
const usuari = event.requestContext.authorizer.jwt.claims.sub as string;
logger.appendKeys({ ruta: event.routeKey });

try {
// ...
logger.info("producte afegit", { producte: element.producte });
} catch (error) {
logger.error("no s'ha pogut afegir", error as Error);
throw error;
}
};

Per a la configuració de la funció a CDK, digues-li a Lambda que els seus propis missatges (START, REPORT) també surtin en JSON:

loggingFormat: lambda.LoggingFormat.JSON,

Què no s'escriu mai en un log​

A totes les pràctiques del curs presencial, la primera línia de cada funció era console.log(event). Per depurar és molt còmode. Però l'esdeveniment d'API Gateway porta les capçaleres, i amb Cognito, la capçalera Authorization porta el token de l'usuari. Tothom que pugui llegir els logs pot fer servir aquells tokens mentre no caduquen.

Regla: als logs hi van identificadors (el sub, l'identificador del producte, el request id), mai tokens, contrasenyes, correus ni l'esdeveniment sencer.


Consultar: CloudWatch Logs Insights​

Amb logs en JSON, Logs Insights els consulta com una base de dades. Els últims errors de la funció:

fields @timestamp, message, ruta, error.message
| filter level = "ERROR"
| sort @timestamp desc
| limit 50

La durada, per franges de cinc minuts, a partir de les línies REPORT:

filter @type = "REPORT"
| stats count() as invocacions, avg(@duration) as mitjana, pct(@duration, 99) as p99 by bin(5m)

Logs Insights cobra pels gigabytes que escaneja. Limita sempre l'interval de temps al que necessites: una consulta sobre els últims trenta dies d'una funció xerraire pot costar més que la funció.


Mètriques que val la pena mirar​

Lambda i API Gateway ja publiquen mètriques a CloudWatch sense que facis res. N'hi ha desenes; les que importen són poques:

MètricaQuè et diuMira-la com a…
Errors de LambdaInvocacions que han acabat amb excepció o timeoutTaxa: errors ÷ invocacions
ThrottlesPeticions rebutjades per falta de concurrènciaQualsevol valor per sobre de 0
DurationQuant trigap99, no la mitjana
5XX d'API GatewayErrors que veu l'usuariTaxa
Missatges a la cua de lletres mortesFeina que ha fallat i ningú ha reintentatQualsevol valor per sobre de 0

Per què p99 i no la mitjana: si la mitjana és 120 ms i el p99 és 4 segons, un de cada cent usuaris espera 4 segons. La mitjana t'ho amaga.

Sobre la cua de lletres mortes (DLQ): a les pràctiques de cues del curs, quan un missatge fallava cinc vegades anava a una DLQ, i una Lambda enviava un correu amb el contingut. Funciona, però fa falta una funció, permisos de SES i codi per mantenir. Una alarma sobre la mida de la DLQ fa el mateix amb tres línies i sense codi.

Mètriques pròpies sense pagar crides​

Si vols comptar coses del negoci (productes afegits, registres), el format EMF (Embedded Metric Format) converteix línies de log en mètriques, sense cap crida a l'API de CloudWatch:

import { Metrics, MetricUnit } from "@aws-lambda-powertools/metrics";

const metriques = new Metrics({ namespace: "LlistaCompra", serviceName: "api" });

// dins del handler
metriques.addMetric("ProducteAfegit", MetricUnit.Count, 1);
metriques.publishStoredMetrics();

Compte amb les dimensions: cada combinació diferent de valors és una mètrica que es paga a part. Una dimensió usuari amb deu mil usuaris són deu mil mètriques.


Alarmes que no silencies​

Una alarma que salta cada dia per no res acaba silenciada, i llavors és pitjor que no tenir-ne cap: et dona una falsa sensació de control. Quatre principis:

  1. Alerta pel símptoma, no per la causa. «Més del 5 % de peticions fallen» importa. «Un error» no: en una aplicació amb tràfic, sempre n'hi ha algun.
  2. Taxes, no comptes. Deu errors amb cent peticions és una catàstrofe; deu errors amb un milió és soroll.
  3. Que no salti per un sol punt. Dos de tres períodes de cinc minuts, per exemple.
  4. Cada alarma té algú que la rep i sap què fer. Si no hi ha resposta possible, no és una alarma, és un gràfic.

Amb CDK, una alarma de taxa d'errors i una de lentitud que envien un correu:

// lib/llista-stack.ts (afegit)
import * as cloudwatch from "aws-cdk-lib/aws-cloudwatch";
import * as accions from "aws-cdk-lib/aws-cloudwatch-actions";
import * as sns from "aws-cdk-lib/aws-sns";
import * as subscripcions from "aws-cdk-lib/aws-sns-subscriptions";

const avisos = new sns.Topic(this, "Avisos");
avisos.addSubscription(new subscripcions.EmailSubscription("tu@exemple.cat"));

const taxaErrors = new cloudwatch.MathExpression({
expression: "IF(invocacions > 0, 100 * errors / invocacions, 0)",
usingMetrics: {
errors: funcio.metricErrors({ statistic: "Sum" }),
invocacions: funcio.metricInvocations({ statistic: "Sum" }),
},
period: Duration.minutes(5),
});

taxaErrors
.createAlarm(this, "TaxaErrors", {
alarmDescription: "Més del 5 % de les peticions de la llista fallen",
threshold: 5,
evaluationPeriods: 3,
datapointsToAlarm: 2,
treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
})
.addAlarmAction(new accions.SnsAction(avisos));

funcio
.metricDuration({ statistic: "p99", period: Duration.minutes(5) })
.createAlarm(this, "Lentitud", {
alarmDescription: "El p99 de la llista passa de 3 segons",
threshold: 3000,
evaluationPeriods: 3,
datapointsToAlarm: 2,
treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
})
.addAlarmAction(new accions.SnsAction(avisos));

NOT_BREACHING vol dir que, si no hi ha dades (ningú ha fet servir l'aplicació en una hora), l'alarma no salta. Sense, una aplicació sense tràfic de nit et despertaria amb un estat INSUFFICIENT_DATA cada matí.

Després del deploy t'arriba un correu de SNS per confirmar la subscripció. Fins que no hi cliques, no t'arriba cap alarma.


Traces: on s'ha perdut el temps​

Quan una petició és lenta, els logs et diuen que ho és. Una traça et diu on: quants mil·lisegons ha passat a la Lambda, quants esperant DynamoDB, quants en un cold start.

A Lambda, activar X-Ray és una propietat de la funció:

tracing: lambda.Tracing.ACTIVE,

Amb Powertools Tracer, a més, cada crida a l'SDK d'AWS surt com un segment propi. El mapa de servei de la consola et dibuixa la petició: API Gateway, Lambda, DynamoDB, amb el temps de cada tram.

Per a una aplicació petita, els logs ben fets i les dues alarmes d'aquesta lliçó et cobreixen el 90 % dels casos. Les traces són per al dia que tinguis cinc serveis encadenats i no sàpigues quin és el lent. AWS recomana cada vegada més fer-ho amb OpenTelemetry, que et deixa canviar de proveïdor sense tornar a instrumentar.


Què costa això​

ConceptePreu aproximatAlways free cada mes
Logs: ingesta~0,50 $ per GB5 GB (ingesta, emmagatzematge i consultes, junts)
Logs: emmagatzematge~0,03 $ per GB i mes—
Logs Insights~0,005 $ per GB escanejat—
Mètriques pròpies~0,30 $ per mètrica i mes10 mètriques
Alarmes estàndard~0,10 $ per alarma i mes10 alarmes
Taulers3 $ per tauler i mes3 taulers
Traces d'X-Ray~5 $ per milió100.000 traces

Preus de l'octubre de 2026, en dòlars.

El cost real de l'observabilitat gairebé sempre és la ingesta de logs. Una funció que fa console.log(event) d'un esdeveniment de 3 KB, amb un milió de peticions al mes, són 3 GB només d'aquesta línia. Escriu menys i millor, i posa retenció a tots els grups.


Errors habituals​

  • console.log(event) en producció. Tokens a la vista de qualsevol que llegeixi logs i gigabytes que pagues. Registra els camps que necessites, no l'objecte sencer.
  • Grups de logs sense retenció. Per defecte, per sempre. A CDK, logGroup amb retention; a mà, put-retention-policy (lliçó 02).
  • Una alarma per a cada error. Salta deu cops al dia, la silencies, i quan arriba el problema de debò no la mira ningú.
  • Mirar la mitjana de la durada. El problema dels usuaris lents és al p99.
  • Una DLQ que no mira ningú. Els missatges hi queden catorze dies com a molt i després desapareixen. Si la DLQ no té alarma, és una paperera amb temporitzador.
  • No confirmar la subscripció del tema SNS. L'alarma salta, l'acció s'executa, i el correu no surt perquè la subscripció continua pendent.

Exercici​

  1. Canvia els console.log de la llista de la compra per Powertools Logger. Comprova que cap log porta tokens ni l'esdeveniment sencer.
  2. Afegeix una mètrica EMF ProducteAfegit i mira-la a CloudWatch.
  3. Crea les dues alarmes d'aquesta lliçó i confirma la subscripció del correu.
  4. Provoca errors a posta (per exemple, una ruta que llanci una excepció) i fes-ne prou per passar del 5 %. Comprova que l'alarma salta i que t'arriba el correu.
  5. Escriu a Logs Insights la consulta que et doni el p99 de la durada de l'última hora.
Repàs: quatre preguntes
  1. Per què s'alerta sobre una taxa d'errors i no sobre el nombre d'errors? Perquè el nombre depèn del tràfic; la taxa diu si el servei funciona.
  2. Què vol dir treatMissingData: NOT_BREACHING? Que sense dades l'alarma es considera correcta i no salta.
  3. Què és el format EMF? Logs en un format concret que CloudWatch converteix en mètriques, sense crides a l'API.
  4. Per què no s'ha de fer console.log(event) en una API amb Cognito? Perquè l'esdeveniment porta la capçalera Authorization amb el token de l'usuari.

La IA a l'empresa, amb criteri

Cada setmana, una sola cosa que pots fer servir: la notícia que toca la teva empresa i què en faria jo, una pregunta de client, un cas real o un exercici de quinze minuts. Un correu per setmana com a molt, i te'n pots donar de baixa amb un clic.