Introducción: sin métricas, el problema se ve como «todo nos va lento»

Una escena conocida. En el chat de trabajo aparece un mensaje: «los proxies van lentos». Cinco minutos después, el segundo: «y hay muchos errores». Ahí empieza lo que los ingenieros llaman entre ellos arqueología. Alguien abre la consola y ejecuta curl a mano contra tres direcciones al azar. Alguien mira el gráfico de carga del procesador, que no tiene nada que ver con los proxies. Alguien le escribe a soporte del proveedor un correo del tipo «algo anda mal con ustedes», sin tener un solo número en la mano. Pasa una hora. Se descubre que solo se degradó un segmento del pool, solo en un país, y que empezó no «ahora», sino hace dos días, simplemente nadie lo notó.

La diferencia entre un equipo que tarda una hora en una historia así y un equipo que tarda un minuto no está en la experiencia ni en el talento. Está en que el segundo equipo decidió de antemano qué iba a medir, qué iba a escribir en los logs y sobre qué iba a disparar alertas. Cuando tienes un gráfico de la tasa de éxito desglosado por país y operador, la frase «todo va lento» se convierte en una afirmación concreta: «desde las 14:32, el p95 del tiempo hasta el primer byte en los proxies móviles del operador A en la región B subió de 1,8 a 6,4 segundos, la tasa de errores no cambió, la tasa de reintentos se triplicó». Con una afirmación así ya se puede trabajar: redistribuir la carga, abrir un ticket al proveedor con datos y no con emociones, o entender que el problema no está en los proxies sino en el recurso destino.

En esta guía vamos a desarmar la observabilidad alrededor de un pool de proxies de principio a fin. Vas a ver qué cuatro señales dan casi toda la información útil, qué campos son obligatorios en cada línea de log y cuáles no deben aparecer nunca, por qué el tiempo de respuesta promedio miente y cómo calcular percentiles correctamente, cómo etiquetar los datos por dimensiones para ver la degradación de un corte específico, cómo construir alertas que se disparen poco pero con razón, y cómo distinguir en un dashboard, en un minuto, tres patrones típicos de problemas. Al final repasamos un stack tecnológico mínimo, casos reales con números y respondemos las preguntas frecuentes.

Marquemos los límites desde ya. La selección de una IP concreta del pool, los chequeos de salud de los proxies y la cuarentena de direcciones falladas no los tratamos en detalle aquí: a eso está dedicado un material aparte. Aquí hablamos solo de dos cosas: medición y alertas. Todo lo que sigue aplica a escenarios de ingeniería legítimos: monitoreo de precios en sitios públicos, verificación de despliegue de anuncios en distintas regiones, pruebas de contenido geodependiente de tus propios servicios, recolección de datos abiertos respetando las reglas de los recursos.

Un poco de bases: qué es la observabilidad aplicada a proxies

La definición clásica dice que la observabilidad (observability) es la propiedad de un sistema que permite entender su estado interno a partir de sus salidas externas. Traducido a lenguaje de ingeniería: puedes hacerle al sistema una pregunta nueva, que no previste de antemano, y obtener respuesta sin desplegar código nuevo. El monitoreo responde preguntas conocidas de antemano («¿el servicio está vivo?»), la observabilidad responde a preguntas desconocidas («¿por qué las solicitudes a este dominio a través de direcciones móviles se volvieron más lentas justo los martes?»).

Los tres pilares de la observabilidad son bien conocidos: métricas (series temporales numéricas, baratas de almacenar, cómodas de agregar), logs (eventos con contexto, caros de almacenar pero con detalle) y trazas (el recorrido de una solicitud por los componentes). Para un pool de proxies el panorama es un poco específico. Entre tu código y el sitio destino hay una infraestructura externa que no controlas: la dirección de salida, el operador de red, la ruta hasta el destino. No puedes poner un agente en una antena móvil. Por eso el único punto donde mides todo es tu propio cliente, que envía la solicitud a través del proxy. Cada solicitud a través del pool debe dejar dos rastros: un incremento de métricas y una línea de log estructurado. Las trazas son útiles, pero para la mayoría de los equipos resultan excesivas, y solo volvemos a ellas en las preguntas frecuentes.

¿Por qué esto importa justo en proxies? Porque la degradación de un proxy casi nunca se parece a una caída. Un servidor que no responde es simple. Un proxy, en cambio, está «un poco más lento», «falla un poco más seguido la conexión», «a veces corta la descarga a la mitad». Cada uno de esos síntomas, por separado, está por debajo del umbral de atención de una persona. En conjunto, duplican el costo de la recolección de datos y rompen los plazos. La degradación lenta solo se detecta midiendo.

Cuatro señales para proxies: éxito, latencia, reintentos, tráfico

En la práctica de la ingeniería de confiabilidad existe el concepto de «señales doradas»: latencia, tráfico, errores y saturación. Para un pool de proxies adaptamos esa idea y obtenemos cuatro señales, suficientes para el 90% de las situaciones. ¿Por qué cuatro y no veinte? Porque cada gráfico adicional en el dashboard reduce la probabilidad de que el de guardia mire el correcto. Cuatro señales son las que caben en una fila de pantalla y se leen en diez segundos.

Señal 1: tasa de solicitudes exitosas

La tasa de solicitudes exitosas (success rate) es la relación entre las solicitudes que terminaron con un resultado útil y el total de solicitudes en una ventana de tiempo. La palabra clave es «útil». El éxito para un proxy no es solo un HTTP 200. El éxito es cuando el recurso destino respondió con lo que esperabas y la respuesta llegó completa. Cortar la conexión a mitad del cuerpo no es éxito, aunque las cabeceras hayan venido con código 200. Un código 403 del sitio destino tampoco es éxito, pero probablemente no sea culpa del proxy, y eso lo distinguimos mediante la clasificación del resultado.

Regla práctica: clasifica el resultado de cada solicitud en una de un número reducido de categorías. Por ejemplo: ok (se obtuvo la respuesta esperada), proxy_connect_error (no se pudo establecer conexión con el proxy), proxy_auth_error (el proxy rechazó la autenticación), upstream_timeout (el proxy respondió, pero el recurso destino no respondió a tiempo), upstream_4xx, upstream_5xx, truncated (conexión cortada antes del final del cuerpo). Esta clasificación es la base de todo el análisis posterior. La tasa de éxito se calcula como ok sobre el total, pero por separado miras las tasas de cada categoría de error, porque señalan culpables distintos.

Señal 2: latencia por percentiles

La latencia de un proxy tiene sentido medirla en tres puntos: tiempo de establecimiento de conexión con el proxy, tiempo hasta el primer byte de la respuesta del recurso destino (TTFB, time to first byte) y tiempo total de la solicitud. El primero habla de la disponibilidad del propio nodo proxy. El segundo, de la calidad de la ruta desde el proxy hasta el destino y de la velocidad de respuesta del destino. El tercero, del ancho de banda del canal. Si solo crece el tercero y los dos primeros están estables, el problema es de ancho de banda, no de disponibilidad. Si crece el primero, el problema está entre tú y el proxy. La latencia hay que calcularla por percentiles, no por promedio, y a eso le dedicamos una sección aparte más abajo.

Señal 3: tasa de reintentos

La tasa de reintentos (retry rate) es la señal más subestimada. La mayoría de los clientes están escritos de modo que, ante un error, repiten la solicitud automáticamente a través de otra dirección. Eso es correcto desde la confiabilidad y desastroso desde la observabilidad: la tasa de éxito se mantiene en 99%, pero por debajo cada solicitud se ejecuta tres veces. Pagas el triple por tráfico, el recurso destino recibe el triple de carga, y en el dashboard todo está verde. La tasa de reintentos revela la degradación oculta antes de que se vuelva degradación visible. Se calcula como la relación entre el número de intentos y el número de solicitudes lógicas: un valor de 1,0 significa que no hay reintentos; 1,5, que una de cada dos solicitudes se repitió.

Señal 4: consumo de tráfico

El consumo de tráfico es la señal que se relaciona directamente con el dinero, porque muchos planes, incluidos los de Proxeon, facturan por volumen de datos transferidos. Pero también tiene un rol diagnóstico. Un aumento brusco de bytes por solicitud exitosa significa que el recurso destino empezó a devolver otras páginas: páginas de relleno pesadas, redirecciones a otras secciones, maquetación modificada. Una caída brusca significa que en lugar de páginas completas estás recibiendo respuestas cortas de error. Cuenta el tráfico en ambas direcciones y normalízalo siempre por el número de solicitudes exitosas: «kilobytes por solicitud exitosa» es una métrica muchísimo más informativa que «gigabytes por hora».

Cómo se ve esto en el código

Abajo hay un conjunto mínimo de métricas para Prometheus en Python. Fíjate en el conjunto de etiquetas: pool, país, operador, tipo de proxy. La dirección del proxy concreto no está en las etiquetas de forma deliberada, y lo explicamos en la sección sobre dimensiones.

from prometheus_client import Counter, Histogram

LABELS = ["pool", "country", "operator", "proxy_type"]

REQUESTS = Counter("proxy_requests_total", "Логические запросы через пул", LABELS + ["outcome"])
ATTEMPTS = Counter("proxy_attempts_total", "Попытки, включая ретраи", LABELS + ["attempt_kind"])
TTFB = Histogram("proxy_ttfb_seconds", "Время до первого байта", LABELS, buckets=(0.1, 0.25, 0.5, 1, 2, 4, 8, 16, 30))
CONNECT = Histogram("proxy_connect_seconds", "Установление соединения с прокси", LABELS, buckets=(0.05, 0.1, 0.25, 0.5, 1, 2, 5))
BYTES = Counter("proxy_bytes_total", "Байты через пул", LABELS + ["direction"])

def observe(ctx, outcome, ttfb, connect, bytes_in, bytes_out, attempt):
base = dict(pool=ctx.pool, country=ctx.country, operator=ctx.operator, proxy_type=ctx.proxy_type)
kind = "first" if attempt == 1 else "retry"
ATTEMPTS.labels(**base, attempt_kind=kind).inc()
if outcome in ("ok", "upstream_4xx", "upstream_5xx", "truncated", "upstream_timeout"):
TTFB.labels(**base).observe(ttfb)
CONNECT.labels(**base).observe(connect)
BYTES.labels(**base, direction="in").inc(bytes_in)
BYTES.labels(**base, direction="out").inc(bytes_out)
if outcome == "ok" or attempt == ctx.max_attempts:
REQUESTS.labels(**base, outcome=outcome).inc()

Fíjate en un detalle: el contador de solicitudes lógicas se incrementa solo al éxito o al agotar los intentos. Así la tasa de éxito refleja el resultado para el usuario de tu código, y el contador de intentos, el trabajo real del pool. La diferencia entre ambos es la tasa de reintentos.

Tabla: métrica, qué significa su aumento, primera acción

MétricaQué significa su aumentoPrimera acción
Tasa de proxy_connect_errorLos nodos proxy no están disponibles o están sobrecargados; problema entre tú y el proveedorVerificar tu canal de salida y DNS; luego comparar por cortes: si es todo el pool, buscar la causa de tu lado; si es un solo corte, avisar al proveedor con datos
Tasa de proxy_auth_errorExpiró el acceso, cambiaron las credenciales, se superó el límite de conexionesVerificar la vigencia de credenciales y los límites en el panel del proveedor; no reintentar esas solicitudes
Tasa de upstream_timeoutEl recurso destino responde lento por esa ruta, o el proxy no da abastoComparar el TTFB por host destino: si es un solo host, el problema es de la meta; si son todos, es la ruta del proxy
Tasa de upstream_4xx (excepto 404)El recurso destino está limitando la intensidad o cambió las reglas de accesoReducir la frecuencia de solicitudes al recurso, verificar el cumplimiento de sus reglas, no aumentar los reintentos
Tasa de upstream_5xxProblema del lado del recurso destinoEsperar, bajar la carga; el proxy probablemente no tiene nada que ver
Tasa de truncatedSe cortan las conexiones a mitad: canal inestable, timeouts de lectura demasiado cortosVerificar los timeouts de lectura y el tamaño de las respuestas; comparar por operador
p95 del tiempo de conexión con el proxyDegradación de la disponibilidad de los nodos proxy o de tu propio stack de redVerificar el número de conexiones simultáneas y el agotamiento de puertos locales
p95 del TTFB con tiempo de conexión estableLa ruta del proxy al destino se volvió más lenta o el destino se ralentizóDesglosar por hosts destino y por cortes del pool
p99 del tiempo total con TTFB estableCayó el ancho de banda del canal del proxy o crecieron las respuestasMirar bytes por solicitud; si el tamaño no cambió, el problema es de ancho de banda
Tasa de reintentosDegradación oculta: el éxito se sostiene con repeticiones, crece el costoEncontrar qué categoría de error provoca los reintentos y trabajar sobre ella
Bytes por solicitud exitosaEl recurso destino devuelve otro contenido: páginas de relleno, redirecciones, estructura modificadaVerificar el contenido de las respuestas en una muestra, actualizar el parser y la validación
Tráfico total con número de solicitudes estableAumento de reintentos o del tamaño de las respuestas; golpe directo al presupuestoDesglosar en dos factores: número de intentos y bytes por intento

Qué escribir en el log por cada solicitud y qué no se puede escribir

Las métricas responden a la pregunta «qué está pasando»; los logs, a «con qué exactamente». Cuando el dashboard muestra un aumento de errores en el corte «país X, operador Y», necesitas entender si todas las direcciones de ese corte son culpables o solo dos de treinta. Las métricas con etiqueta por dirección harían explotar el almacenamiento; los logs no, porque no los agregas de forma permanente sino que los filtras por consulta.

Campos obligatorios de la línea de log

Cada intento de solicitud a través del pool debe generar un registro estructurado, preferiblemente en formato JSON. Aquí está el conjunto de campos que consideramos mínimo suficiente:

  • ts — marca de tiempo en UTC con milisegundos. No hora local. Nunca.
  • request_id — identificador de la solicitud lógica, común a todos sus intentos. Permite reconstruir toda la historia de una solicitud con un solo comando.
  • attempt y max_attempts — número de intento y límite. Sin el número de intento no distingues el primer error del tercero.
  • proxy_id — identificador interno del proxy. No la dirección con puerto ni la cadena de conexión. Un identificador interno que puedas asociar a la dirección real mediante una tabla protegida aparte.
  • proxy_type, country, operator, pool — las mismas dimensiones que en las métricas. Esto permite saltar entre el dashboard y los logs sin recalcular.
  • target_host — nombre de dominio del destino. No la URL completa: en la cadena de consulta a veces hay tokens y datos personales.
  • method — método HTTP.
  • status — código HTTP de la respuesta, si se obtuvo; si no, null.
  • outcome — esa clasificación del resultado de la que hablábamos arriba.
  • error_class — clase de excepción o código corto de error de transporte, si el resultado no es ok.
  • connect_ms, ttfb_ms, total_ms — las tres latencias en milisegundos.
  • bytes_in, bytes_out — tamaño del cuerpo de la respuesta y de la solicitud.
  • session_id — si usas sesiones pegajosas, el identificador de sesión; así verás que los errores se concentran en sesiones largas.
  • retry_reason — para los intentos a partir del segundo: el resultado del intento anterior.

Ejemplo de línea:

{"ts":"2026-03-14T11:32:07.412Z","request_id":"7f3a9c1e","attempt":2,"max_attempts":3,"retry_reason":"upstream_timeout","proxy_id":"px-2841","proxy_type":"mobile","country":"RU","operator":"op_a","pool":"pricing","target_host":"shop.example","method":"GET","status":200,"outcome":"ok","error_class":null,"connect_ms":184,"ttfb_ms":1420,"total_ms":2310,"bytes_in":48211,"bytes_out":612,"session_id":"s-19b"}

Una línea así pesa unos 400 bytes. A 50 solicitudes por segundo son aproximadamente 1,7 gigabytes por día sin comprimir y 200–300 megabytes comprimidos. Perfectamente manejable incluso con un presupuesto modesto.

Qué no debe estar en los logs

Aquí pasamos de la ingeniería a la responsabilidad. La lista es corta, pero cada punto importa:

  1. Credenciales de acceso al proxy. La fuga más frecuente es la cadena de conexión tipo http://usuario:contraseña@host:puerto, que entra completa al log porque el desarrollador logueó la configuración «para depurar». Los logs se copian en tickets, se reenvían a colegas, se guardan durante años. Las credenciales que están ahí son credenciales disponibles para todos los que alguna vez vieron el log.
  2. El encabezado Proxy-Authorization y cualquier otro encabezado de autorización, incluidos los encabezados hacia el recurso destino.
  3. El cuerpo de la respuesta. Es enorme, puede contener datos personales de terceros y casi nunca hace falta para diagnosticar proxies. Si necesitas entender qué devolvió el recurso destino, guarda el hash del cuerpo y su longitud, y la respuesta en sí, en un almacenamiento aparte con vida corta y solo por muestra.
  4. Cookies en ambas direcciones.
  5. La URL completa con parámetros de consulta. Basta el host y, si hace falta, la ruta sin parámetros.
  6. La dirección real del proxy en texto abierto, si el log va a un almacenamiento común. Con el identificador interno es suficiente.

La práctica que recomendamos: implementa el redactado en una única función por la que pase cualquier registro relacionado con proxies. No confíes en la disciplina de los desarrolladores.

import re

CRED_IN_URL = re.compile(r"(https?|socks5h?)://[^/@:]+:[^/@]+@")
SENSITIVE_HEADERS = {"proxy-authorization", "authorization", "cookie", "set-cookie"}

def redact(record: dict) -> dict:
for key, value in list(record.items()):
if key.lower() in SENSITIVE_HEADERS:
record[key] = "[redacted]"
elif isinstance(value, str):
record[key] = CRED_IN_URL.sub(lambda m: m.group(1) + "://[redacted]@", value)
record.pop("body", None)
return record

Muestreo y almacenamiento

¿Hace falta escribir cada intento? Los errores, sí, todos. Los intentos exitosos se pueden muestrear: con una intensidad superior a cientos de solicitudes por segundo basta con un 5–10% de líneas exitosas para estimar latencias, porque las métricas ya contienen el panorama completo. Es importante que la decisión de muestreo se tome por request_id y no al azar en cada intento: si no, vas a obtener el segundo intento sin el primero. El tiempo de retención de logs detallados suele ser de 7 a 14 días; los agregados y las métricas viven más.

Percentiles en lugar del promedio: por qué el promedio esconde el problema

Imagina un pool de cien direcciones. Noventa responden en 0,8 segundos. Diez responden en 9 segundos porque cayeron en una estación base congestionada. El tiempo de respuesta promedio es 1,62 segundos. Hace una semana, cuando todo iba bien, el promedio era 1,4. Un aumento del 15%. ¿Quién va a reaccionar a eso? Nadie. Mientras tanto, una de cada diez solicitudes tuyas espera nueve segundos, y si tienes un timeout de ocho, una de cada diez solicitudes falla con error.

La media aritmética describe bien las distribuciones parecidas a una campana. Las latencias a través de proxies nunca se parecen a una campana. Es una distribución con cola derecha larga: la mayoría de las solicitudes son rápidas, pero algunas son muy lentas, y justo esas determinan la experiencia del usuario y el costo. Más aún, las latencias a través de un pool suelen ser bimodales: hay una joroba «normal» y hay una joroba de «direcciones problemáticas». El promedio cae entre las dos, en un punto donde casi no hay solicitudes reales.

Qué son p50, p95 y p99

El percentil es el valor por debajo del cual queda una proporción dada de las observaciones. p50 (la mediana) es el valor por debajo del cual queda la mitad de las solicitudes. p95: el 95% son más rápidas, el 5% más lentas. p99: solo el uno por ciento es más lento. Para un pool de proxies recomendamos mirar tres percentiles con objetivos distintos:

  • p50 muestra la solicitud «típica». Si crece el p50, se degradó la mayoría. Es una señal rara y seria.
  • p95 es la métrica de trabajo para alertas. Es sensible a los segmentos problemáticos (en el ejemplo de arriba el p95 mostraría de inmediato 9 segundos), pero resistente a valores atípicos aislados.
  • p99 es la métrica para timeouts y planificación. Tu timeout debe estar un poco por encima del p99 «sano»; si no, vas a cortar solicitudes normales y convertirlas en reintentos.

Cómo calcular con exactitud

Si hay pocos datos (miles de valores en la ventana), calcula con exactitud: ordena y toma el elemento por índice.

def percentile(values, p):
if not values:
return None
ordered = sorted(values)
k = (len(ordered) - 1) * p / 100.0
lo = int(k)
hi = min(lo + 1, len(ordered) - 1)
return ordered[lo] + (ordered[hi] - ordered[lo]) * (k - lo)

ttfb_window = [0.8, 0.9, 0.7, 9.1, 0.85, 8.7, 0.78, 0.92, 0.81, 0.79]
print(percentile(ttfb_window, 50), percentile(ttfb_window, 95))

El cálculo exacto no escala: para calcular el p95 de una semana con millones de solicitudes hay que guardar todos los valores. Por eso los sistemas de producción usan estructuras aproximadas.

Cómo calcular a escala: histogramas

Prometheus y los sistemas compatibles guardan el histograma como un conjunto de contadores por intervalos: cuántas observaciones fueron menores a 0,1 s, menores a 0,25 s, menores a 0,5 s y así. El percentil se reconstruye por interpolación lineal dentro del intervalo. La precisión depende de cuán bien elegidos estén los límites de los intervalos. Para latencias a través de proxies funciona bien una escala logarítmica de 100 milisegundos a 30 segundos: 0,1; 0,25; 0,5; 1; 2; 4; 8; 16; 30. Si tu p95 sano está en torno a 2 segundos y el timeout es 15, esa grilla da un error de entre el 20% y el 30% en la cola, suficiente para alertas y diagnóstico.

histogram_quantile(0.95,
sum by (le, country, operator) (
rate(proxy_ttfb_seconds_bucket{pool="pricing"}[5m])
)
)

Fíjate en el sum by (le, ...) de adentro. Es un detalle crítico: los histogramas de distintas instancias de tu cliente hay que sumarlos primero y calcular el cuantil después. La operación inversa —calcular el p95 en cada instancia y promediar— es matemáticamente absurda. El promedio de percentiles no es un percentil. Es uno de los errores más frecuentes y puede esconder un problema con la misma eficacia que un promedio común.

Si necesitas alta precisión en la cola (por ejemplo, si vendes SLA de latencia a clientes internos), usa estructuras tipo t-digest o histogramas HDR, que dan precisión de fracciones de porcentaje con memoria fija. Para la mayoría de los equipos, los histogramas estándar alcanzan.

Desglose por dimensiones: país, operador, tipo de proxy

Aquí va un escenario que ilustra para qué sirven las dimensiones. Tienes un pool de 400 direcciones, el 5% son direcciones móviles de un operador en una región. Ese segmento se degradó por completo: la tasa de éxito allí cayó al 10%. ¿Qué mostraría el gráfico general de tasa de éxito del pool? Una caída del 99% al 94,5%. Se nota, pero no es crítico. El de guardia pensará «la red está fallando» y seguirá. Pero el cliente de datos de esa región va a recibir vacío.

El desglose por dimensiones (en terminología de Prometheus, etiquetas o labels) resuelve justo ese problema: permite descomponer el agregado en cortes y ver que el problema está localizado. Pero el desglose tiene un costo, y hay que entenderlo.

Qué dimensiones elegir

Una buena dimensión tiene dos propiedades: un número pequeño y acotado de valores, y un desglose que responde a una pregunta real de diagnóstico. Para un pool de proxies recomendamos cuatro dimensiones en las métricas:

  • proxy_type — móviles, residenciales, de centro de datos. Distintos tipos se degradan de forma distinta y tienen distinto costo.
  • country — país de la dirección de salida. Normalmente entre 1 y 30 valores.
  • operator — operador de red o sistema autónomo por el que sale la dirección. Los problemas suelen estar localizados justo aquí: una estación base congestionada o un mal peering de un operador.
  • pool — propósito lógico: qué producto o tarea usa ese segmento. Permite separar incidentes por impacto en el negocio.

Una quinta dimensión a considerar es target_host, el host destino. Hace falta para distinguir problemas del proxy de problemas de la meta. Pero si vas a miles de dominios distintos, la cardinalidad explota. El compromiso: deja en la etiqueta solo los 20 hosts principales por volumen y colapsa el resto en el valor other.

Aritmética de la cardinalidad

Cada combinación única de valores de etiquetas es una serie temporal aparte. Calculemos: 3 tipos × 10 países × 15 operadores × 4 pools × 7 resultados = 12 600 series solo para el contador de solicitudes. Un histograma con 9 intervalos multiplicará su número de series por 10. En total, unos 50–60 mil series. Para Prometheus eso es cómodo. Ahora imagina que agregas una etiqueta con la dirección del proxy (400 valores). El número de series crece 400 veces: hasta 20 millones. Eso ya no es «cómodo», eso es la muerte del almacenamiento.

De ahí la regla: la dirección de un proxy concreto vive en los logs, no en las métricas. Las métricas responden a la pregunta «qué corte se degradó»; los logs, «qué direcciones exactamente dentro del corte». Esa división del trabajo da velocidad y detalle sin un crecimiento explosivo de costos.

Consultas que encuentran el corte

La pregunta principal del diagnóstico: «¿dónde está peor?». La responde una ordenación de cortes por tasa de éxito:

bottomk(5,
sum by (country, operator, proxy_type) (rate(proxy_requests_total{outcome="ok"}[10m]))
/
sum by (country, operator, proxy_type) (rate(proxy_requests_total[10m]))
)

La segunda pregunta: «¿es un problema nuevo o siempre fue así?». La responde la comparación con el pasado:

(
sum by (operator) (rate(proxy_requests_total{outcome="ok"}[10m])) / sum by (operator) (rate(proxy_requests_total[10m]))
)
-
(
sum by (operator) (rate(proxy_requests_total{outcome="ok"}[10m] offset 1d)) / sum by (operator) (rate(proxy_requests_total[10m] offset 1d))
)

Un valor negativo muestra cuánto empeoró el corte respecto de hace un día. Un gráfico así en el dashboard convierte «en algún lugar algo anda mal» en «operador B en el país C, menos 12 puntos respecto de ayer».

De dónde salen los valores de las dimensiones

El país y el tipo de proxy normalmente se conocen por la configuración del pool. El operador se puede obtener de los metadatos del proveedor: por ejemplo, en el panel y la API de Proxeon las direcciones vienen con información de geografía y operador, que cargas al inicializar el pool y almacenas en caché junto al identificador interno del proxy. No conviene determinar el operador en el momento de la solicitud a través de bases externas: agrega latencia y crea una dependencia innecesaria.

Alertas que no hacen ruido: umbrales, ventanas, histéresis

Lo peor que le puede pasar a un sistema de alertas no es un incidente no detectado. Lo peor es que el de guardia reciba 40 notificaciones al día, de las cuales 39 no significan nada, y deje de leer la cuadragésima. Una alerta que se ignora es peor que la ausencia de alerta, porque crea la ilusión de control.

Principio 1: alerta sobre el síntoma, no sobre la causa

No pongas una alerta sobre «el proxy px-2841 devolvió un error». Una dirección concreta tiene derecho a enfermarse; de eso se encarga el mecanismo de cuarentena dentro del pool, que aquí no tratamos. Pon la alerta sobre lo que siente el consumidor de tus datos: tasa de éxito del pool por debajo del objetivo, p95 por encima del objetivo, reintentos por encima del objetivo. La causa la vas a encontrar en el dashboard en un minuto si hiciste bien las dimensiones. La tarea de la alerta es despertar, no explicar.

Principio 2: volumen mínimo de tráfico

Una tasa de éxito del 50% con dos solicitudes en cinco minutos es una solicitud fallida, no un incidente. Cualquier regla sobre una tasa debe incluir una condición de número mínimo de solicitudes en la ventana. Si no, de noche, cuando el tráfico baja, te vas a ahogar en falsos positivos.

Principio 3: ventanas y velocidad de consumo del presupuesto

Si definiste un nivel objetivo de confiabilidad (por ejemplo, 98% de solicitudes exitosas al mes), tienes un presupuesto de error: 2% de las solicitudes. Una buena alerta reacciona a la velocidad de consumo del presupuesto, no al valor instantáneo. La práctica que se volvió estándar en la industria de la confiabilidad son las alertas multi-ventana: la ventana corta (5 minutos) detecta caídas bruscas, la larga (1 hora), la degradación lenta, y ambas deben superarse al mismo tiempo para excluir picos breves.

Principio 4: histéresis

La histéresis son umbrales distintos para disparar y para resolver la alerta. Si una alerta se dispara con tasa de éxito por debajo del 95% y se resuelve cuando vuelve por encima del 95%, una métrica que oscila alrededor del 95% generará una serie de notificaciones «se disparó, se resolvió, se disparó» cada pocos minutos. Solución: disparar al 95%, resolver al 97%. O, más simple, exigir que la condición se mantenga de forma continua durante un tiempo dado (en Prometheus, la palabra clave for), y además definir un retardo para la resolución.

Principio 5: niveles de severidad y enrutamiento

No todo hay que entregarlo por llamada. Divide las alertas en dos o tres niveles: críticas (impacto en los consumidores de datos ahora mismo: llamada o push al de guardia), advertencias (degradación de un corte, aumento de reintentos, anomalía de tráfico: mensaje al canal, se resuelve en horario laboral), informativas (falta de datos de una instancia, acercamiento al límite de tráfico: resumen diario).

Ejemplo de conjunto de reglas

groups:
- name: proxy-pool
rules:
- record: pool:success_ratio:5m
expr: sum by (pool) (rate(proxy_requests_total{outcome="ok"}[5m])) / sum by (pool) (rate(proxy_requests_total[5m]))
- record: pool:success_ratio:1h
expr: sum by (pool) (rate(proxy_requests_total{outcome="ok"}[1h])) / sum by (pool) (rate(proxy_requests_total[1h]))
- record: pool:request_rate:5m
expr: sum by (pool) (rate(proxy_requests_total[5m]))

- alert: ProxyPoolSuccessLow
expr: pool:success_ratio:5m < 0.95 and pool:success_ratio:1h < 0.97 and pool:request_rate:5m > 0.5
for: 5m
labels: { severity: critical }
annotations:
summary: "Доля успеха по пулу {{ $labels.pool }} ниже цели"

- alert: ProxyPoolLatencyHigh
expr: histogram_quantile(0.95, sum by (le, pool) (rate(proxy_ttfb_seconds_bucket[10m]))) > 4 and pool:request_rate:5m > 0.5
for: 10m
labels: { severity: warning }

- alert: ProxyPoolRetryRateHigh
expr: sum by (pool) (rate(proxy_attempts_total[15m])) / sum by (pool) (rate(proxy_requests_total[15m])) > 1.4
for: 15m
labels: { severity: warning }

- alert: ProxyPoolSliceDegraded
expr: (sum by (pool, country, operator) (rate(proxy_requests_total{outcome="ok"}[15m])) / sum by (pool, country, operator) (rate(proxy_requests_total[15m]))) < 0.85 and sum by (pool, country, operator) (rate(proxy_requests_total[15m])) > 0.2
for: 15m
labels: { severity: warning }

- alert: ProxyPoolNoData
expr: absent(proxy_requests_total) or pool:request_rate:5m == 0
for: 10m
labels: { severity: warning }

Fíjate en la última regla. La falta de datos es el fallo más traicionero: el cliente se cayó, no hay métricas, los gráficos están planos y verdes, todos tranquilos. La alerta por silencio es obligatoria.

Checklist para cada alerta nueva

  1. ¿Está escrito en la anotación qué debe hacer quien la recibe? Si no, la regla no está lista.
  2. ¿Tiene condición de tráfico mínimo?
  3. ¿Tiene retardo for o histéresis?
  4. ¿Se probó la regla sobre el histórico del último mes: cuántas veces habría disparado y cuántas de esas eran incidentes reales?
  5. ¿Está claro a qué nivel de severidad pertenece y a dónde se enruta?
  6. ¿Hay un proceso inverso: si la alerta se disparó y fue falsa, quién y cuándo la revisa?

Diagnóstico rápido por dashboard: tres patrones típicos

El dashboard no es una galería de gráficos, es una herramienta de toma de decisiones. Un buen dashboard para un pool de proxies está armado como un embudo. Fila superior: las cuatro señales de todo el pool en las últimas 6 horas, superpuestas con la línea fina del día anterior. Segunda fila: las mismas señales desglosadas por país, operador y tipo, en forma de tabla o mapa de calor ordenado por el peor valor. Tercera fila: tasa de éxito y TTFB por los 10 hosts destino principales. Cuarta: relación entre tiempo de conexión, TTFB y tiempo total. Más marcadores verticales de despliegues y cambios de configuración del pool.

Con un dashboard así, el diagnóstico se reduce a responder tres preguntas: «¿se degradó todo el pool o un corte?», «¿se degradaron todos los destinos o uno?», «¿cuál de las tres latencias creció?». Las combinaciones de respuestas dan tres patrones típicos.

Patrón 1: se degradaron todos los cortes al mismo tiempo

Todos los países, todos los operadores, todos los tipos de proxy muestran un aumento de errores o latencias de forma sincronizada, con precisión de minutos. Los proveedores y operadores externos no se rompen así: no tienen un punto de falla común del lado de ellos. Entonces el problema es tuyo: tu canal de salida, tu DNS, agotamiento de descriptores de archivos o de puertos locales, un despliegue reciente que cambió timeouts o el tamaño del pool de conexiones, competencia por CPU con otro proceso en la misma máquina. Primera acción: mirar el marcador de despliegue y el tiempo de conexión con el proxy. Si el tiempo de conexión creció y el TTFB (menos la conexión) está estable, el problema está entre tú y los nodos proxy, y casi con seguridad es tu lado o tu proveedor de red.

Patrón 2: se degradó un solo corte

Un operador en un país, o un tipo de proxy, muestra caída, y los demás cortes están estables. Es el patrón más frecuente. La causa es del operador de red (congestión de un segmento de red, trabajos programados) o de un segmento de la infraestructura del proveedor de proxies. Primera acción: bajar temporalmente el peso de ese corte en la distribución de carga (el mecanismo queda fuera de este artículo) y escribir a soporte del proveedor un mensaje con datos concretos: corte, hora de inicio, métricas antes y después, ejemplos de request_id. Soporte de Proxeon responde a esas consultas más rápido que a un «ustedes están mal», simplemente porque recibe la localización ya hecha. Segunda acción: mirar los logs del corte para entender si se degradaron todas las direcciones de adentro o solo algunas.

Patrón 3: cayó la tasa de éxito para un solo host destino, las latencias están normales

Todos los cortes del pool siguen llegando bien a todos los hosts menos a uno. Para ese host crece la tasa de upstream_4xx o cambió el volumen de bytes por solicitud. Los proxies aquí no tienen nada que ver. El recurso destino cambió su comportamiento: introdujo límite de frecuencia, cambió la estructura de la página, empezó a devolver otro contenido a una parte de las solicitudes. Primera acción: bajar la intensidad de solicitudes a ese recurso y verificar si tu recolección cumple sus reglas de uso. Segunda: revisar el contenido en una muestra y actualizar la validación de respuestas. Categóricamente no conviene en esta situación aumentar el número de reintentos ni cambiar de direcciones más seguido: eso solo aumenta la carga sobre el recurso y tus costos de tráfico.

Patrón 4 (bonus): los reintentos crecen, el éxito se sostiene

Tasa de éxito del 99%, latencias tolerables, pero la tasa de reintentos creció de 1,1 a 1,8. Es degradación oculta. Ya te está costando dinero y se va a volver degradación visible en cuanto se agote el margen de intentos. Primera acción: mirar qué resultado causó las repeticiones (el campo retry_reason en los logs) y luego ir por uno de los tres patrones anteriores.

Checklist de diagnóstico en 60 segundos

  1. Segundos 0–10: fila superior del dashboard. ¿Cuál de las cuatro señales se apartó de la línea de ayer? ¿Cuándo exactamente?
  2. Segundos 10–25: tabla por cortes. ¿Un corte en rojo o todos? Si son todos, patrón 1, ir a los marcadores de despliegue y a tu propia infraestructura.
  3. Segundos 25–40: tabla por hosts destino. ¿Un host o todos? Si es uno, patrón 3, los proxies no tienen nada que ver.
  4. Segundos 40–50: relación de latencias. ¿Creció la conexión, el TTFB o el tiempo total?
  5. Segundos 50–60: formular una sola frase: «desde [hora] en el corte [corte] para [destino] creció [métrica] de [antes] a [después], presumiblemente por [hipótesis]». Esa frase va al canal del incidente y al ticket al proveedor.

Errores típicos que anulan el beneficio de la observabilidad

Hemos visto decenas de implementaciones de monitoreo alrededor de proxies. Los errores se repiten con una constancia asombrosa.

  • El tiempo de respuesta promedio como métrica principal. Ya lo dijimos: el promedio esconde la cola, y la cola es el problema.
  • Promediar percentiles entre instancias. Suma los histogramas, no los p95 ya calculados.
  • La dirección del proxy como etiqueta de métrica. Explosión de cardinalidad y caída del almacenamiento en un mes.
  • Credenciales de acceso en los logs. Una línea «for debug» y la contraseña queda en un almacenamiento común de logs por un año.
  • Los reintentos se cuentan como éxito. Si cuentas solo el resultado final, nunca verás que el pool trabaja al triple de la intensidad necesaria.
  • Los códigos 429 y 403 del recurso destino se registran como error de proxy. Es un error de clasificación que desvía la investigación hacia el lado equivocado y daña la relación con el proveedor.
  • Una sola latencia en lugar de tres. Sin medir por separado conexión, TTFB y tiempo total, no distingues un problema de disponibilidad de uno de ancho de banda.
  • Alerta sin tráfico mínimo. Ruido nocturno y pérdida de confianza en las alertas.
  • Alerta por cada dirección individual. Es tarea de la cuarentena, no de una notificación humana.
  • No hay alerta por falta de datos. El cliente se cayó y los gráficos están verdes.
  • Hora local en los logs. La correlación con los datos del proveedor y de colegas en otra zona horaria se vuelve una tortura.
  • Dashboard sin marcadores de despliegue. La mitad de los incidentes coincide con un cambio de código o de configuración. Sin marcador vas a buscar la causa afuera cuando está adentro.
  • Guardar logs detallados para siempre. Caro e inútil: a las dos semanas los detalles ya no hacen falta y los agregados ya están en las métricas.

Stack mínimo sin sobreingeniería

Es fácil convertir la observabilidad en un proyecto por el proyecto mismo. Un equipo de tres personas despliega una cola de mensajes distribuida, un clúster de base columnar y un sistema de trazas para un cliente que hace 20 solicitudes por segundo. No hay que hacer eso. El stack debe corresponder a la escala.

Nivel 1: un servicio, hasta 50 solicitudes por segundo

Basta con logs JSON estructurados en archivo o salida estándar y un script que cada minuto los agregue y escriba las cuatro señales en una base simple o incluso en el mismo log. El dashboard, cualquier herramienta que sepa graficar una tabla. Las alertas, un script de cron que compare los agregados con umbrales y mande un mensaje al mensajero. Ese stack se levanta en un día y cubre el 80% de las necesidades.

Nivel 2: varios servicios, 50–1000 solicitudes por segundo

El conjunto estándar: Prometheus para métricas, Grafana para dashboards, Alertmanager para enrutar alertas, Loki o un almacenamiento similar para logs con índice por etiquetas. Los clientes exportan métricas por un endpoint HTTP incorporado y escriben logs JSON en salida estándar; un agente los recolecta. Las reglas de grabación (recording rules) precalculan las tasas de éxito para que los dashboards abran al instante. Esta es la configuración descrita en los ejemplos de arriba, y cubre a la gran mayoría de los equipos.

Nivel 3: decenas de servicios, miles de solicitudes por segundo

Aquí tiene sentido OpenTelemetry como capa única de recolección, un almacenamiento columnar para logs con capacidad de consultas analíticas arbitrarias sobre miles de millones de filas y, quizás, trazas para cadenas complejas. Pero incluso en este nivel las cuatro señales y la estructura de logs no cambian. Solo cambia el transporte y el almacenamiento.

Los datos del proveedor como complemento

El proveedor de proxies ve su lado: consumo de tráfico por cada puerto, número de conexiones activas, hora de la última rotación. El panel y la API de Proxeon entregan esas estadísticas, y es útil tenerlas al lado de tus propias métricas, aunque sea para contrastar el consumo de tráfico: si tu contador de bytes y el del proveedor difieren más de un 10–15%, tienes un problema de medición (lo más común es que no se cuenten encabezados, handshakes TLS o respuestas cortadas). Pero los datos del proveedor no reemplazan los tuyos: solo tú sabes qué solicitud fue exitosa desde el punto de vista de la tarea.

El wrapper del cliente como punto único de medición

Independientemente del nivel del stack, todas las mediciones deben pasar por un único wrapper sobre el cliente HTTP. Nada de «aquí medimos, aquí nos olvidamos». Esquemáticamente:

import time, json, sys, requests

def fetch_via_proxy(ctx, url, timeout=(5, 20)):
t0 = time.perf_counter()
outcome, status, err = "ok", None, None
ttfb = connect = total = 0.0
bytes_in = 0
try:
session = ctx.session # ya настроена на прокси по внутреннему proxy_id
resp = session.get(url, timeout=timeout, stream=True)
ttfb = time.perf_counter() - t0
status = resp.status_code
body = resp.content
bytes_in = len(body)
total = time.perf_counter() - t0
if status >= 500: outcome = "upstream_5xx"
elif status >= 400: outcome = "upstream_4xx"
elif not ctx.validate(body): outcome = "invalid_content"
except requests.exceptions.ProxyError as e:
outcome, err = "proxy_connect_error", type(e).__name__
except requests.exceptions.ReadTimeout as e:
outcome, err = "upstream_timeout", type(e).__name__
except requests.exceptions.ChunkedEncodingError as e:
outcome, err = "truncated", type(e).__name__
finally:
total = total or (time.perf_counter() - t0)
observe(ctx, outcome, ttfb, connect, bytes_in, len(url), ctx.attempt)
record = redact({"ts": ctx.now_utc(), "request_id": ctx.request_id, "attempt": ctx.attempt,
"proxy_id": ctx.proxy_id, "proxy_type": ctx.proxy_type, "country": ctx.country,
"operator": ctx.operator, "pool": ctx.pool, "target_host": ctx.host(url),
"status": status, "outcome": outcome, "error_class": err,
"ttfb_ms": int(ttfb * 1000), "total_ms": int(total * 1000), "bytes_in": bytes_in})
sys.stdout.write(json.dumps(record, ensure_ascii=False) + chr(10))
return outcome, status

Fíjate en el resultado invalid_content: una respuesta con código 200 que no pasó tu validación (por ejemplo, una página sin el bloque de datos esperado) no es un éxito. Justo esas respuestas son las que más frecuentemente enmascaran un cambio de comportamiento del recurso destino.

Casos: cómo se ve la observabilidad en la práctica

Caso 1: un operador, un país, menos dos horas de inactividad por semana

Un equipo de monitoreo de precios en sitios públicos trabajaba a través de un pool de unas 300 direcciones móviles y residenciales con una intensidad de unas 40 solicitudes por segundo. Antes de introducir el desglose por operador, veían una sola cosa: el p95 general del TTFB se disparaba cada tanto de 1,6 a 3–4 segundos durante unas horas, y la tasa de éxito bajaba 3–5 puntos. Investigar cada episodio tomaba entre una y dos horas y casi siempre terminaba con las palabras «se pasó solo». Después de agregar las etiquetas country y operator, el primer episodio se localizó en dos minutos: direcciones móviles de un operador en una región, con p95 de 11 segundos contra una norma de 2. El resto del pool, 85%, estaba en perfecto orden. El equipo bajó el peso del corte en la distribución, le envió al proveedor un informe con los intervalos de tiempo y las métricas, y a las pocas horas recibió la confirmación de trabajos programados del operador. Resultado del trimestre: el tiempo de diagnóstico bajó de 90 minutos a 5; el número de episodios no cambió, pero su impacto en el p95 general se volvió indistinguible.

Caso 2: reintentos ocultos y +38% en la factura de tráfico

Otro equipo detectó un aumento del consumo de tráfico del 38% en un mes con el mismo número de tareas y una tasa de éxito estable del 98,5%. La primera hipótesis fue que las páginas destino se volvieron más pesadas. La métrica de bytes por solicitud exitosa la refutó: 46 kilobytes, igual que antes. En cambio, la tasa de reintentos, que nadie miraba, había crecido de 1,08 a 1,52. El análisis del campo retry_reason en los logs mostró que casi todas las repeticiones las causaba el resultado upstream_timeout para un host destino que había empezado a responder más lento, pero entraba en el timeout al segundo intento. Los proxies no eran la causa en absoluto. El equipo subió el timeout de lectura para ese host de 8 a 15 segundos (el p99 sano era 11), bajó la intensidad de solicitudes hacia él y devolvió el consumo de tráfico al nivel anterior en tres días. Moraleja: la tasa de reintentos es una métrica sobre dinero.

Caso 3: de 200 notificaciones por semana a seis

Un tercer equipo heredó un sistema de alertas con la regla «tasa de éxito de cualquier corte en 1 minuto por debajo del 90%», sin límite de tráfico. De noche, cuando por algunos cortes pasaban dos o tres solicitudes por minuto, la regla se disparaba decenas de veces. Los de guardia silenciaron el sonido. Un incidente real —la caída de disponibilidad de todo un segmento durante cuatro horas— se detectó por la mañana, por reclamos. La reforma de las reglas tomó un día: se agregó la condición de tráfico mínimo, se introdujeron dos ventanas (5 minutos y 1 hora), se agregó un retardo for de 10 minutos, los cortes pasaron al nivel warning y en el nivel de pool quedó una única alerta crítica. Resultado: unas seis notificaciones por semana, de las cuales cuatro o cinco exigían acción real. Los de guardia volvieron a activar el sonido.

FAQ: preguntas frecuentes sobre la observabilidad de un pool de proxies

¿Hacen falta trazas si ya hay métricas y logs?

Para la mayoría de los equipos, no. Una solicitud a través de un proxy no pasa por diez microservicios; tiene un solo salto. El rol de la traza aquí lo cumple el request_id en los logs: con él reconstruyes todos los intentos de una solicitud. Las trazas se vuelven útiles cuando el cliente de proxies es solo una parte de una cadena larga (cola de tareas, parser, validador, cargador a base de datos) y necesitas entender dónde exactamente se pierde el tiempo.

¿Cómo medir el TTFB si el cliente HTTP no lo entrega directamente?

La mayoría de los clientes devuelven el control después de recibir los encabezados si pides lectura por streaming (stream=True o equivalente). Ese momento de retorno es la aproximación al TTFB. El tiempo de conexión se mide con más precisión a nivel de transporte: muchas bibliotecas tienen hooks de eventos de conexión, otras traen información lista sobre las fases de la solicitud. Si no hay hooks, se puede medir el tiempo hasta el envío del primer byte de la solicitud con una medición de bajo nivel aparte, pero para fines prácticos alcanza con la aproximación por streaming.

¿Cómo distinguir un error del proxy de un error del recurso destino?

Por el lugar donde se origina. Si la conexión con el proxy no se estableció, o el proxy devolvió un código propio (por ejemplo, 407 con problemas de autorización o una respuesta específica de la pasarela), es error del proxy. Si el proxy aceptó la solicitud y después obtuviste un código del servidor destino o un timeout esperando la respuesta, es error entre el proxy y el destino, o del propio destino. Clasifica en esa frontera y luego compara por cortes: los errores del destino se ven en todos los cortes al mismo tiempo; los del proxy, en cortes específicos.

¿Cuál debería ser un nivel sano de tasa de reintentos?

Depende de los objetivos y del tipo de proxy. Para direcciones móviles se puede considerar normal 1,05–1,15: la red inalámbrica es por naturaleza menos predecible. Para las de centro de datos, 1,01–1,05. Más importante que el valor absoluto es su estabilidad: un aumento del 30% respecto del nivel base semanal es motivo para investigar, aunque el valor absoluto todavía parezca modesto.

¿Cómo elegir los umbrales de alerta si no hay histórico?

Empieza observando sin alertas: recolecta dos semanas de datos, mira la distribución de la tasa de éxito y del p95 por días y horas. El umbral para critical ponlo de modo que en esas dos semanas se habría disparado no más de una o dos veces, y en ambos casos habrías estado de acuerdo con que era un incidente. El umbral para warning, de modo que se dispare varias veces por semana. Después revisa cada mes: una alerta que nunca llevó a una acción se endurece o se elimina.

¿Vale la pena guardar el cuerpo de la respuesta al menos para los errores?

Para errores de transporte no hay cuerpo. Para respuestas con contenido inesperado (invalid_content) es útil guardar los primeros cientos de bytes o el hash y la longitud, en un almacenamiento aparte con vida de uno o dos días, con limpieza obligatoria de datos personales. Los cuerpos completos no hace falta guardarlos: son pesados, pueden contener datos de terceros y para diagnosticar proxies casi siempre basta con entender qué tipo de página llegó.

¿Cómo contrastar tu conteo de tráfico con los datos del proveedor?

Cuenta bytes en ambas direcciones, incluidos los encabezados, y compara los totales diarios por puerto o por sesión con lo que muestra el panel del proveedor. Una diferencia del 5–10% es normal: de tu lado no se cuentan los costos de TLS ni las conexiones cortadas. Una diferencia mayor al 20% es motivo para revisar si estás contando solo el cuerpo de la respuesta y si estás perdiendo intentos que terminaron en excepción antes de leer el cuerpo.

¿Cuánto tiempo lleva implementar el enfoque descrito desde cero?

Para un cliente con un wrapper ya existente sobre HTTP: un día para las métricas y la estructura de logs, un día para el dashboard, un día para las reglas de alerta, y dos semanas más de observación antes de activar las alertas critical. Si tienes varios clientes en lenguajes distintos, el plazo principal se va en acordar un conjunto único de resultados y etiquetas; aquí es importante no apurarse, porque renombrar una métrica después de un año de histórico duele mucho.

¿Qué hacer si el proveedor no tiene datos de operador para una parte de las direcciones?

Introduce el valor unknown y vigila su proporción. Si unknown supera el 10–15% del pool, tus cortes por operador pierden sentido: pídele al proveedor los metadatos completos o etiqueta las direcciones a mano una vez al agregarlas al pool.

Conclusión: un minuto en lugar de una hora

Volvamos a la escena del principio. «Los proxies van lentos» es una frase que no contiene información. Todo lo descrito en esta guía sirve a un solo objetivo: convertirla en una oración con números, corte, hora e hipótesis. Las cuatro señales —tasa de éxito, latencia por percentiles, tasa de reintentos, consumo de tráfico— dan el panorama del estado del pool. El log estructurado con identificador de solicitud, identificador de proxy, número de intento, código, tres latencias y tamaño da el detalle para investigar. Los percentiles en lugar del promedio muestran la cola, donde viven los problemas reales. El desglose por país, operador y tipo localiza la degradación hasta el corte. Las alertas con tráfico mínimo, dos ventanas e histéresis se disparan poco y con razón. El dashboard-embudo responde tres preguntas en un minuto.

¿Por dónde empezar mañana? Primer paso: el wrapper sobre el cliente con una clasificación única de resultados y una sola función de registro. Segundo: el histograma de TTFB y el contador de intentos con etiquetas por país, operador y tipo. Tercero: la función de redactado de logs, por la que pasa todo sin excepción. Cuarto: un dashboard de cuatro gráficos y una tabla por cortes. Quinto: dos semanas de observación y solo después las alertas. En un mes vas a mirar el gráfico de la tasa de reintentos y, muy probablemente, te vas a sorprender: el pool estaba trabajando bastante más intensamente de lo que creías. Y en un trimestre, la frase «todo nos va lento» va a desaparecer de tu chat, no porque no haya problemas, sino porque a cada uno lo vas a llamar por su nombre.