Resumen de alertas de log por servicio y severidad

Gana FxDart

Requisito

A partir de un flujo de logs de servicio (los datos están en el código), quédate solo con las líneas WARN y ERROR y genera un resumen con sangría: los servicios ordenados por número de alertas, bajo cada servicio la severidad con su recuento y, bajo cada severidad, los mensajes distintos. La línea de cabecera lleva los totales generales de ERROR/WARN. Ambas versiones deben imprimir el resumen que aparece bajo Salida esperada.

Salida esperada
Alert digest — ERROR 4, WARN 5
api (4)
  ERROR x2
    - timeout calling billing
  WARN x2
    - slow query 1.2s
    - disk 85% full
auth (3)
  ERROR x1
    - bad signature
  WARN x2
    - token near expiry
billing (2)
  ERROR x1
    - invoice 442 failed
  WARN x1
    - retrying charge

Lado a lado

Dart nativo

FxDart

Por qué difieren

Un resumen con sangría es un árbol aplanado en líneas, y flatMap es exactamente ese aplanado: el groupBy + sortBy + flatMap exterior emite una cabecera más sus hijos por cada servicio, el flatMap interior hace lo mismo por cada severidad, y uniq se encarga de los mensajes repetidos allí donde aparecen. countBy da los totales de la cabecera en una sola palabra. La versión nativa son tres bucles for anidados que escriben en una misma lista body compartida, más un mapa de conteo hecho a mano y un conjunto seen para deduplicar —la forma de árbol es real en ambas versiones, pero solo una de ellas te deja leerla en la propia sangría del código.

Benchmark

Apple M1 Max, 32 GB de RAM · Dart 3.12.2 (compilado AOT) · 2026-08-24

N = 100

Tiempo Empate

Dart nativo 21 µs
FxDart 25 µs

Memoria pico Empate

Dart nativo 16.5 MB
FxDart 16.5 MB

N = 10,000

Tiempo Empate

Dart nativo 1.46 ms
FxDart 1.39 ms

Memoria pico Empate

Dart nativo 17.9 MB
FxDart 18.1 MB

N = 1,000,000

Tiempo Empate

Dart nativo 174.1 ms
FxDart 174.4 ms

Memoria pico Gana nativo

Dart nativo 213.2 MB
FxDart 239.8 MB

Las barras son medianas de iteraciones cronometradas repetidas en procesos nuevos por lado (los N pequeños se agrupan por resolución del temporizador). Dos lados a menos del 5% entre sí — o a menos de 0.6 ms, una diferencia que nadie puede percibir — cuentan como empate; las carreras relativas ajustadas se vuelven a medir hasta 5 veces. En una app, cualquier cosa por debajo de unos pocos milisegundos es invisible para el usuario, gane la barra que gane. La memoria es el RSS pico del proceso. La VM de Dart y el dataset son idénticos en ambos lados, así que la diferencia entre las dos barras es lo que retiene el pipeline en sí.