AWS Builder Center

Observabilidad desde los logs: como construir SLIs sin esperar a que alguien instrumente el codigo

Leiste los 5 blogs anteriores. Sabes cual estadistica usar, que rompe un SLO, como computar el SLI, como visualizarlo, y que hacer cuando la alarma suena. Pero cuando abres Amazon CloudWatch, tu servicio solo tiene las metricas que AWS publica por defecto. El dato que realmente necesitas esta en los logs.

El equipo que tiene logs pero no metricas

Conoces este equipo. Llevan meses con una aplicacion en produccion. Tienen logs estructurados en JSON: cada request deja un registro con timestamp, status code, duracion en milisegundos, endpoint, y user ID. CloudWatch Logs tiene todo. Pero cuando alguien pregunta "cual es nuestro p99 de latencia esta semana?" la respuesta es: "no sabemos, no tenemos esa metrica."
La ironia: el dato esta ahi. Cada request registro su duracion. Pero nadie conecto ese dato con una metrica de CloudWatch, lo cual significa que no hay SLI, no hay SLO, no hay presupuesto de error, no hay alarma. Toda la cadena que construimos en los blogs anteriores no existe porque falta el primer eslabon: convertir un log en una metrica.
Este post cubre tres formas de hacerlo, cada una con sus ventajas y trampas.
Los cinco blogs anteriores:

Camino 1: Metric Filters -- extraer metricas de patrones en logs

Un metric filter observa un log group, busca patrones que coincidan con tu filtro, y publica un data point en CloudWatch cada vez que encuentra uno. Desde ese momento, la metrica existe como cualquier otra metrica de CloudWatch: puedes graficarla, alarmarla, y usarla en Metric Math.

Ejemplo: tasa de errores 5xx desde access logs

Tus logs tienen lineas como:
JSON
{"timestamp": "2026-09-01T12:00:00Z", "status": 503, "duration_ms": 1200, "endpoint": "/api/orders"}

Creas un metric filter en el log group de tu aplicacion:
Plain text
Filter pattern: { $.status >= 500 }
Metric name: 5xxErrors
Metric value: 1
Default value: 0

Cada vez que un log con status >= 500 llega, CloudWatch publica un data point de valor 1 en la metrica  5xxErrors . La Sum de esa metrica en un periodo de 5 minutos te da el conteo total de errores 5xx.
Para la tasa de errores necesitas dos metricas: errores y total de requests. Crea un segundo metric filter:
Plain text
Filter pattern: { $.status = * }
Metric name: TotalRequests
Metric value: 1
Default value: 0

Ahora puedes usar la Formula 1 del Blog 3:
Plain text
m1 = 5xxErrors [Sum]
m2 = TotalRequests [Sum]
e1 = (m1 / m2) * 100

Tasa de errores. SLI de disponibilidad. La cadena empieza a existir.

Ejemplo: latencia p99 desde logs estructurados

Pero metric filters tambien pueden extraer valores numericos de los logs, no solo contar ocurrencias:
Plain text
Filter pattern: { $.duration_ms = * }
Metric name: RequestDuration
Metric value: $.duration_ms
Default value: (dejar vacio)

Cada log event publica su valor de  duration_ms  como un data point individual. Esto es clave: al publicar data points individuales (no agregados), tienes acceso a las 12 estadisticas del Blog 1, incluyendo percentiles y TrimmedMean.
"You can use any type of CloudWatch statistic, including percentile statistics, when viewing these metrics or setting alarms." -  Creating metrics from log events using filters 
La unica restriccion: si los valores pueden ser negativos, las percentile statistics no estan disponibles para esa metrica.
Ahora puedes construir la Formula 3 del Blog 3 (cumplimiento de latencia) directamente sobre esta metrica:
Plain text
m1 = RequestDuration [p99]
e1 = IF(m1 <= 500, 1, 0)
e2 = RUNNING_SUM(e1)
e3 = TIME_SERIES(DATAPOINT_COUNT(m1))
e4 = (e2 / e3) * 100

SLI de latencia p99. Desde logs. Sin tocar el codigo de la aplicacion.

Cuando metric filters no alcanzan

  • Solo Standard log class. Metric filters no funcionan con log groups en la clase Infrequent Access. Si tus logs usan esa clase para reducir costos, necesitas EMF o Logs Insights.
  • No son retroactivos. Solo procesan logs que llegan despues de crear el filtro. Si necesitas analizar datos historicos, usa Logs Insights (Camino 3).
  • Un filtro, una metrica. No puedes extraer latencia Y status code del mismo log con un solo filtro. Necesitas filtros separados.
  • Hasta 3 dimensiones. Puedes extraer valores de campos del log como dimensiones para JSON y logs delimitados por espacios. Pero el limite es 3 dimensiones por metric filter, y cuando usas dimensiones no puedes asignar un default value a la metrica. Si necesitas mas de 3 dimensiones o combinaciones de alta cardinalidad, EMF (Camino 2) escala mejor.

Camino 2: Embedded Metric Format -- metricas dentro de tus logs

El Embedded Metric Format (EMF) es un esquema JSON que le dice a CloudWatch Logs: "este log contiene metricas, extrae las y publicalas automaticamente." No haces llamadas a PutMetricData. No pagas por API calls adicionales. Simplemente escribes un JSON estructurado a stdout y CloudWatch hace el resto.
"The CloudWatch embedded metric format allows you to generate custom metrics asynchronously in the form of logs written to CloudWatch Logs." -  Embedding metrics within logs 

Ejemplo: latencia y errores por endpoint

JSON
{
"_aws": {
"Timestamp": 1725192000000,
"CloudWatchMetrics": [
{
"Namespace": "MyApp/SLIs",
"Dimensions": [["Endpoint"]],
"Metrics": [
{ "Name": "Duration", "Unit": "Milliseconds" },
{ "Name": "ErrorCount", "Unit": "Count" }
]
}
]
},
"Endpoint": "/api/orders",
"Duration": 245,
"ErrorCount": 0,
"requestId": "abc-123",
"userId": "usr-456"
}

CloudWatch Logs recibe este log, reconoce el bloque  _aws , y publica dos metricas:  Duration  y  ErrorCount  bajo el namespace  MyApp/SLIs  con la dimension  Endpoint=/api/orders . Los campos  requestId  y  userId  se quedan en el log para busquedas futuras pero no generan metricas ni dimensiones.
Cada valor se publica como un data point individual. Igual que con metric filters, esto te da acceso a las 12 estadisticas del Blog 1. Puedes pedir p99 de  Duration  filtrado por  Endpoint=/api/orders .

Donde EMF gana sobre metric filters

Mas dimensiones, mas flexible. Metric filters soportan hasta 3 dimensiones. EMF soporta hasta 30 por DimensionSet. Si necesitas filtrar por endpoint, region, stage, y version simultaneamente, EMF no tiene el limite de 3.
Multiples metricas en un log. Un solo log event publica  Duration  y  ErrorCount . Con metric filters necesitarias dos filtros separados.
Arrays de valores. Si un log agrupa multiples mediciones (por ejemplo, un batch de 50 requests), puedes publicar un array:
JSON
{
"Duration": [120, 340, 89, 450, 210, 95, 780, 110, 200, 315]
}

Cada valor del array se publica como un data point individual. 10 data points de una sola linea de log.

La trampa de EMF: explosion de cardinalidad

Cada combinacion unica de dimensiones crea una metrica nueva en CloudWatch. Si usas  userId  como dimension y tienes 100,000 usuarios, creas 100,000 metricas. El costo se dispara.
"If you unintentionally create metrics based on high-cardinality dimensions (such as requestId), the embedded metric format will by design create a custom metric corresponding to each unique dimension combination." -  Embedded metric format specification 
La regla: usa como dimensiones solo los campos que quieres como ejes de filtrado en tu SLI. Endpoint, region, stage, service name. No uses request ID, user ID, transaction ID, o cualquier campo que sea unico por request.

Lo que EMF requiere

EMF necesita cambios en el codigo de tu aplicacion. Tu aplicacion tiene que emitir el JSON con el bloque  _aws . Existen librerias oficiales para varios lenguajes (Python, Node.js, Java, .NET) que simplifican esto, pero la instrumentacion no es transparente como un metric filter. Si no puedes modificar el codigo (vendor, legacy, imagen de contenedor que no controlas), EMF no es una opcion.

Camino 3: CloudWatch Logs Insights -- SLIs ad-hoc sin crear metricas

A veces no necesitas una metrica persistente. Necesitas una respuesta ahora: "cual fue el p99 de latencia del endpoint /api/orders en las ultimas 2 horas?" Logs Insights te da eso con una consulta.

Ejemplo: p99 de latencia por endpoint

Plain text
fields @timestamp, duration_ms, endpoint
| filter endpoint like /api/
| stats pct(duration_ms, 99) as p99,
pct(duration_ms, 50) as p50,
avg(duration_ms) as promedio,
count(*) as total
by bin(5m)

Esta consulta te da una serie de tiempo con p99, p50, promedio y conteo total por ventanas de 5 minutos. Puedes visualizarla directamente en la consola.

Ejemplo: disponibilidad por hora

Plain text
fields @timestamp, status, status >= 500 as is_5xx
| stats count(*) as total,
sum(is_5xx) as errores_5xx
by bin(1h)
| display total, errores_5xx,
((total - errores_5xx) / total) * 100 as disponibilidad

Disponibilidad por hora. La misma Formula 2 del Blog 3, pero calculada directamente sobre los logs sin crear metricas intermedias.

Cuando Logs Insights es la herramienta correcta

Investigacion durante incidentes. Cuando la alarma de burn rate suena (Blog 5) y necesitas entender que paso en los ultimos 30 minutos, una consulta de Logs Insights te da respuestas sin esperar a que una metrica acumule suficientes data points.
Validar antes de instrumentar. Antes de crear un metric filter o agregar EMF, usa Logs Insights para confirmar que el dato existe en tus logs y que el SLI que quieres construir tiene sentido con tus datos reales.
Analisis historico. Logs Insights consulta datos historicos. Metric filters no son retroactivos. Si necesitas saber "cual fue nuestro p99 el mes pasado" y no tenias una metrica publicada, Logs Insights es tu unica opcion.

Cuando Logs Insights no alcanza

No genera alarmas. No puedes crear una alarma de CloudWatch directamente sobre una consulta de Logs Insights. Si necesitas alarmar, necesitas una metrica persistente (metric filter o EMF).
Costo por consulta. Logs Insights cobra por la cantidad de datos escaneados. Una consulta sobre 30 dias de logs de alta volumetria puede ser costosa. Las metricas publicadas (metric filter o EMF) se consultan sin costo adicional despues de la publicacion.
No alimenta dashboards automaticamente. Puedes agregar una consulta de Logs Insights a un dashboard de CloudWatch, pero se ejecuta cada vez que abres el dashboard. Para un dashboard que alguien mira 20 veces al dia (la Fila 1 del Blog 4), eso suma en costo y latencia.

Matriz de decision: cual camino usar


Metric filter
EMF
Logs Insights
Requiere cambiar codigo?
No
Si
No
Retroactivo?
No
No
Si
Genera metrica persistente?
Si
Si
No
Soporta percentiles (p99, TM)?
Si
Si
Si (en query)
Soporta las 12 estadisticas del Blog 1?
Si (valores no negativos)
Si
Parcial (depende de funciones disponibles)
Dimensiones desde log fields?
Si (hasta 3)
Si (hasta 30)
Si (por campo de log)
Alimenta alarmas?
Si
Si
No
Alimenta Fila 1 del dashboard (Blog 4)?
Si
Si
Con limitaciones
Costo incremental
Bajo (metrica custom)
Bajo (metrica custom)
Por consulta
Mejor para...
Patrones simples, sin tocar codigo
Metricas de aplicacion con dimensiones
Investigacion, validacion, historico

El camino mas comun

La mayoria de los equipos empieza con metric filters porque no requieren cambios en codigo. Creas dos filtros (errores y total), construyes el SLI de disponibilidad con Metric Math (Blog 3, Formula 1), lo agregas al dashboard (Blog 4, Fila 1), y configuras la alarma de burn rate (Blog 5).
Cuando necesitan mas de 3 dimensiones o metricas de negocio con combinaciones complejas, migran a EMF. Cuando necesitan investigar un incidente o validar una hipotesis, usan Logs Insights.
No son mutuamente excluyentes. Un equipo maduro usa los tres.

La trampa silenciosa: PutMetricData con StatisticValues

Hay un cuarto camino que algunos equipos toman y que este blog deliberadamente no recomienda: llamar a PutMetricData con el parametro  StatisticValues  en vez de  Value .
 StatisticValues  permite publicar datos pre-agregados: Sum, SampleCount, Minimum, Maximum. Es tentador porque reduces el numero de API calls (un solo call en vez de uno por request). Pero al publicar datos pre-agregados pierdes acceso a las extended statistics del Blog 1: percentiles (p99, p50), TrimmedMean (TM), Winsorized Mean (WM), Percentile Rank (PR), Trimmed Count (TC), Trimmed Sum (TS).
"If you published your data using a statistic set instead, you can retrieve only these statistics (Average, Sum, Minimum, Maximum, SampleCount). You cannot retrieve percentile statistics for this data because CloudWatch needs the raw data points in order to calculate percentiles." -  CloudWatch statistics definitions 
Si tu SLI usa p99 (que deberia, si leiste el Blog 1), publicar con  StatisticValues  lo destruye desde la raiz. Toda la cadena SLO del Blog 2 se construye sobre una estadistica que ya no existe.
Tanto metric filters como EMF publican data points individuales. Ese es el camino correcto.

Lo que este blog no cubre (limitaciones honestas)

No cubre el diseno de logs. Asume que tus logs ya tienen los campos que necesitas (status, duration, endpoint). Si tus logs son texto plano sin estructura, el primer paso es migrar a logs estructurados en JSON. Eso es un proyecto separado.
No cubre costos en detalle. Metric filters y EMF crean metricas custom que tienen un costo por metrica por mes. El calculo depende de cuantas metricas, cuantas dimensiones, y cual resolucion. La pagina de  precios de Amazon CloudWatch  tiene las tarifas actuales.
No cubre OpenTelemetry. Si tu aplicacion ya esta instrumentada con OpenTelemetry y usas CloudWatch Application Signals, las metricas de SLI se generan automaticamente. EMF y metric filters son para equipos que aun no han llegado a esa etapa.

Conclusion

Los cinco blogs anteriores construyeron una cadena: estadistica (Blog 1) que alimenta un SLI (Blog 2) calculado con Metric Math (Blog 3), visualizado en un dashboard SLO-first (Blog 4), con un runbook para cuando la alarma suena (Blog 5). Este blog es el paso cero: como crear la metrica que alimenta esa cadena a partir de los datos que ya tienes en tus logs.
Tres caminos: metric filters para lo simple e inmediato, EMF para cuando necesitas dimensiones y control, Logs Insights para investigar y validar. Los tres publican data points individuales (o los consultan directamente), lo cual preserva las 12 estadisticas que la cadena necesita.
Si tu equipo tiene logs pero no tiene SLIs, ya no hay excusa. El dato esta ahi. Solo falta conectarlo.

Recursos

Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article