Introduction : sans métriques, un problème ressemble à « tout rame chez nous »

Une scène familière. Dans le chat d'équipe apparaît un message : « les proxys rament ». Cinq minutes plus tard, un deuxième : « et il y a plein d'erreurs ». Ensuite commence ce que les ingénieurs appellent entre eux de l'archéologie. Quelqu'un ouvre un terminal et lance des curl à la main via trois adresses au hasard. Un autre regarde un graphe de charge CPU qui n'a rien à voir avec les proxys. Un troisième écrit au support du fournisseur un message du genre « il y a un truc qui cloche chez vous », sans avoir un seul chiffre en main. Une heure passe. On découvre que seul un segment du pool a dégradé, dans un seul pays, et que ça a commencé non pas « maintenant » mais il y a deux jours, simplement personne ne l'a remarqué.

La différence entre une équipe qui passe une heure sur ce genre d'histoire et une équipe qui y passe une minute n'est ni une question d'expérience ni de talent. La différence, c'est que la seconde a décidé à l'avance ce qu'elle allait mesurer, ce qu'elle allait écrire dans les logs et sur quoi elle allait déclencher des alertes. Quand vous avez un graphe du taux de réussite découpé par pays et par opérateur, la phrase « tout rame » se transforme en une affirmation concrète : « depuis 14h32, le p95 du temps jusqu'au premier octet pour les proxys mobiles de l'opérateur A dans la région B est passé de 1,8 à 6,4 secondes, le taux d'erreur n'a pas bougé, le taux de retry a triplé ». Avec une telle affirmation, on peut travailler : redistribuer la charge, ouvrir un ticket au fournisseur avec des données et non des émotions, ou comprendre que le problème n'est pas du tout dans les proxys mais dans la ressource cible.

Dans ce guide, nous allons décortiquer l'observabilité autour d'un pool de proxys de bout en bout. Vous découvrirez quels quatre signaux fournissent presque toute l'information utile, quels champs doivent obligatoirement figurer dans chaque ligne de log et lesquels ne doivent jamais y apparaître, pourquoi le temps de réponse moyen ment et comment calculer correctement les percentiles, comment étiqueter les données par dimensions pour voir la dégradation d'une tranche précise, comment construire des alertes qui se déclenchent rarement mais à bon escient, et comment distinguer en une minute sur un tableau de bord trois scénarios typiques de problème. À la fin, nous verrons une stack technologique minimale, des cas réels chiffrés et les questions fréquentes.

Précisons d'emblée les limites. La sélection d'une IP spécifique dans le pool, les contrôles de santé des proxys et la mise en quarantaine des adresses défaillantes ne sont pas détaillés ici : un article dédié y est consacré. Ici, il n'est question que de deux choses : la mesure et l'alerte. Tout ce qui suit s'applique à des scénarios d'ingénierie légitimes : surveillance de prix sur des places de marché publiques, vérification de l'affichage publicitaire dans différentes régions, test de contenu géodépendant de ses propres services, collecte de données ouvertes dans le respect des règles des ressources.

Quelques bases : ce qu'est l'observabilité appliquée aux proxys

La définition classique dit que l'observabilité (observability) est la propriété d'un système permettant de comprendre son état interne à partir de ses sorties externes. En langage d'ingénieur : vous pouvez poser au système une nouvelle question non prévue à l'avance et obtenir une réponse sans déployer de nouveau code. Le monitoring répond à des questions connues d'avance (« le service est-il vivant ? »), l'observabilité à des questions inconnues (« pourquoi les requêtes vers ce domaine via des adresses mobiles sont-elles devenues plus lentes, précisément le mardi ? »).

Les trois piliers de l'observabilité sont bien connus : les métriques (séries temporelles numériques, peu coûteuses à stocker, faciles à agréger), les logs (événements avec contexte, coûteux à stocker, mais riches en détails) et les traces (le parcours d'une requête à travers les composants). Pour un pool de proxys, le tableau est un peu particulier. Entre votre code et le site cible se trouve une infrastructure externe que vous ne contrôlez pas : l'adresse de sortie, l'opérateur télécom, la route jusqu'à la cible. Vous ne pouvez pas installer un agent sur une antenne mobile. Le seul point où vous mesurez tout, c'est donc votre propre client qui envoie la requête via le proxy. Chaque requête à travers le pool doit laisser deux traces : une incrémentation de métriques et une ligne de log structurée. Les traces sont utiles, mais superflues pour la plupart des équipes, et nous n'y reviendrons que dans la FAQ.

Pourquoi est-ce particulièrement important pour les proxys ? Parce que la dégradation d'un proxy ne ressemble presque jamais à une panne. Un serveur qui ne répond pas, c'est simple. Le proxy, lui, est « un peu plus lent », « renvoie un peu plus souvent des erreurs de connexion », « coupe parfois le téléchargement à mi-parcours ». Chacun de ces symptômes pris isolément passe sous le seuil d'attention d'un humain. Additionnés, ils doublent le coût de la collecte de données et cassent les délais. Une dégradation lente ne se repère que par la mesure.

Quatre signaux pour un proxy : réussite, latence, retries, trafic

Dans la pratique de l'ingénierie de la fiabilité, il existe une notion de « signaux d'or » : latence, trafic, erreurs et saturation. Pour un pool de proxys, nous adaptons cette idée et obtenons quatre signaux, suffisants dans 90 % des situations. Pourquoi quatre et pas vingt ? Parce que chaque graphe supplémentaire sur un tableau de bord réduit la probabilité que l'astreinte regarde le bon. Quatre signaux, c'est ce qui tient sur une ligne d'écran et se lit en dix secondes.

Signal 1 : le taux de réussite

Le taux de réussite (success rate) est le rapport entre les requêtes ayant abouti à un résultat utile et le nombre total de requêtes sur une fenêtre de temps. Le mot clé est « utile ». Pour un proxy, un succès n'est pas seulement un HTTP 200. Un succès, c'est quand la ressource cible a répondu ce qu'on attendait et que la réponse est arrivée en entier. Une connexion coupée au milieu du corps de la réponse n'est pas un succès, même si les en-têtes portaient un code 200. Un code 403 renvoyé par le site cible n'est pas non plus un succès, mais ce n'est probablement pas la faute du proxy, et nous distinguons cela via la classification de l'issue.

Règle pratique : classez l'issue de chaque requête dans un nombre restreint de catégories. Par exemple : ok (réponse attendue reçue), proxy_connect_error (impossible d'établir la connexion au proxy), proxy_auth_error (le proxy a rejeté l'authentification), upstream_timeout (le proxy a répondu, mais la ressource cible n'a pas répondu à temps), upstream_4xx, upstream_5xx, truncated (connexion rompue avant la fin du corps). Cette classification est la base de toute l'analyse ultérieure. Le taux de réussite se calcule comme ok sur le total, mais on regarde séparément la part de chaque catégorie d'erreur, parce qu'elles désignent des coupables différents.

Signal 2 : la latence en percentiles

Pour un proxy, la latence a du sens à trois endroits : le temps d'établissement de la connexion au proxy, le temps jusqu'au premier octet de la réponse de la ressource cible (TTFB, time to first byte) et le temps total de la requête. Le premier indique la disponibilité du nœud proxy lui-même. Le deuxième, la qualité de la route du proxy jusqu'à la cible et la vitesse de réponse de la cible. Le troisième, le débit du canal. Si seul le troisième augmente alors que les deux premiers restent stables, le problème est dans la bande passante, pas dans la disponibilité. Si le premier augmente, le problème est entre vous et le proxy. La latence doit se calculer en percentiles, pas en moyenne, et une section dédiée y est consacrée ci-dessous.

Signal 3 : le taux de retry

Le taux de retry (retry rate) est le signal le plus sous-estimé. La plupart des clients sont écrits de telle sorte qu'en cas d'erreur, la requête est automatiquement répétée via une autre adresse. C'est correct du point de vue de la fiabilité et désastreux du point de vue de l'observabilité : le taux de réussite reste à 99 %, mais sous le capot chaque requête est exécutée trois fois. Vous payez le trafic trois fois, la ressource cible reçoit une charge triplée, et le tableau de bord est tout vert. Le taux de retry révèle une dégradation cachée avant qu'elle ne devienne visible. Il se calcule comme le rapport entre le nombre de tentatives et le nombre de requêtes logiques : une valeur de 1,0 signifie aucun retry, 1,5 qu'une requête sur deux a été répétée.

Signal 4 : la consommation de trafic

La consommation de trafic est un signal directement lié à l'argent, car de nombreux forfaits, y compris les forfaits Proxeon, facturent le volume de données transmises. Mais elle a aussi un rôle diagnostique. Une brusque augmentation des octets par requête réussie signifie que la ressource cible se met à renvoyer d'autres pages : pages lourdes de type placeholder, redirections vers d'autres sections, mise en page modifiée. Une chute brutale signifie qu'au lieu de pages complètes, vous recevez de courtes réponses d'erreur. Comptez le trafic dans les deux sens et normalisez-le toujours par le nombre de requêtes réussies : « kilo-octets par requête réussie » est une métrique bien plus informative que « gigaoctets par heure ».

À quoi cela ressemble dans le code

Voici un ensemble minimal de métriques pour Prometheus en Python. Notez le jeu de labels : pool, pays, opérateur, type de proxy. L'adresse du proxy spécifique est volontairement absente des labels, et nous expliquerons pourquoi dans la section sur les dimensions.

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()

Notez un détail : le compteur de requêtes logiques n'est incrémenté qu'en cas de succès ou d'épuisement des tentatives. Ainsi, le taux de réussite reflète le résultat pour l'utilisateur de votre code, et le compteur de tentatives le travail réel du pool. La différence entre les deux, c'est le taux de retry.

Tableau : métrique, ce que sa hausse signifie, première action

MétriqueCe que sa hausse signifiePremière action
Part de proxy_connect_errorLes nœuds proxy sont indisponibles ou surchargés ; problème entre vous et le fournisseurVérifier son canal sortant et son DNS, puis comparer par tranches : si tout le pool est touché, chercher la cause chez soi ; si une seule tranche, prévenir le fournisseur avec des données
Part de proxy_auth_errorAccès expiré, identifiants changés, limite de connexions dépasséeVérifier la validité des identifiants et les limites dans le panneau du fournisseur, ne pas réessayer ces requêtes
Part de upstream_timeoutLa ressource cible répond lentement via cette route, ou le proxy ne suit pasComparer le TTFB par hôte cible : si un seul hôte, problème de la cible ; si tous, problème de la route proxy
Part de upstream_4xx (hors 404)La ressource cible limite l'intensité ou a changé ses règles d'accèsRéduire la fréquence des requêtes vers la ressource, vérifier la conformité à ses règles, ne pas augmenter les retries
Part de upstream_5xxProblème du côté de la ressource cibleAttendre, réduire la charge ; le proxy n'y est probablement pour rien
Part de truncatedLes connexions se coupent à mi-chemin : canal instable, timeouts de lecture trop courtsVérifier les timeouts de lecture et la taille des réponses ; comparer par opérateur
p95 du temps de connexion au proxyDégradation de la disponibilité des nœuds proxy ou de votre stack réseauVérifier le nombre de connexions simultanées et l'épuisement des ports chez soi
p95 du TTFB avec temps de connexion stableLa route du proxy jusqu'à la cible a ralenti, ou la cible a ralentiDécomposer par hôtes cibles et par tranches du pool
p99 du temps total avec TTFB stableLe débit du canal proxy a chuté ou les réponses ont grossiRegarder les octets par requête ; si la taille n'a pas changé, problème de bande passante
Taux de retryDégradation cachée : le succès tient grâce aux répétitions, le coût augmenteIdentifier quelle catégorie d'erreur provoque les répétitions et travailler dessus
Octets par requête réussieLa ressource cible renvoie un autre contenu : placeholders, redirections, structure modifiéeVérifier le contenu des réponses sur un échantillon, mettre à jour le parseur et la validation
Trafic total à nombre de requêtes stableHausse des retries ou de la taille des réponses ; impact direct sur le budgetDécomposer en deux facteurs : nombre de tentatives et octets par tentative

Que mettre dans le log à chaque requête — et ce qu'il ne faut jamais y mettre

Les métriques répondent à la question « que se passe-t-il », les logs à « avec quoi exactement ». Quand le tableau de bord montre une hausse des erreurs dans la tranche « pays X, opérateur Y », il vous faut comprendre si toutes les adresses de cette tranche sont en cause ou seulement deux sur trente. Des métriques avec un label par adresse feraient exploser le stockage ; les logs, non, car on ne les agrège pas en permanence, on les filtre à la demande.

Champs obligatoires d'une ligne de log

Chaque tentative de requête via le pool doit produire une entrée structurée, de préférence au format JSON. Voici l'ensemble de champs que nous considérons comme minimalement suffisant :

  • ts — horodatage en UTC avec millisecondes. Pas en heure locale. Jamais.
  • request_id — identifiant de la requête logique, commun à toutes ses tentatives. Permet de reconstituer toute l'histoire d'une requête en une commande.
  • attempt et max_attempts — numéro de la tentative et limite. Sans numéro de tentative, impossible de distinguer la première erreur de la troisième.
  • proxy_id — identifiant interne du proxy. Pas l'adresse avec le port, pas la chaîne de connexion. Un identifiant interne que vous pouvez associer à l'adresse réelle via une table sécurisée séparée.
  • proxy_type, country, operator, pool — les mêmes dimensions que dans les métriques. Cela permet de passer du tableau de bord aux logs sans recalcul.
  • target_host — le nom de domaine de la cible. Pas l'URL complète : les chaînes de requête contiennent parfois des jetons et des données personnelles.
  • method — la méthode HTTP.
  • status — le code HTTP de réponse, s'il a été reçu, sinon null.
  • outcome — la fameuse classification de l'issue évoquée plus haut.
  • error_class — la classe d'exception ou un court code d'erreur de transport si l'issue n'est pas ok.
  • connect_ms, ttfb_ms, total_ms — les trois latences en millisecondes.
  • bytes_in, bytes_out — la taille du corps de la réponse et la taille de la requête.
  • session_id — si vous utilisez des sessions persistantes, l'identifiant de session ; vous verrez ainsi que les erreurs se concentrent dans les sessions longues.
  • retry_reason — pour les tentatives à partir de la deuxième : l'issue de la tentative précédente.

Exemple de ligne :

{"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"}

Une telle ligne pèse environ 400 octets. À 50 requêtes par seconde, cela représente environ 1,7 gigaoctet par jour sans compression et 200 à 300 mégaoctets compressés. Tout à fait gérable, même avec un budget modeste.

Ce qui ne doit pas figurer dans les logs

Nous passons ici de l'ingénierie à la responsabilité. La liste est courte, mais chaque point compte :

  1. Les identifiants d'accès au proxy. La fuite la plus fréquente : une chaîne de connexion du type http://login:motdepasse@hôte:port qui se retrouve intégralement dans le log parce qu'un développeur a logué la config « pour déboguer ». Les logs sont copiés dans les tickets, transférés à des collègues, conservés des années. Des identifiants dedans, ce sont des identifiants accessibles à quiconque a vu le log un jour.
  2. L'en-tête Proxy-Authorization et tout autre en-tête d'autorisation, y compris ceux destinés à la ressource cible.
  3. Le corps de la réponse. Il est énorme, il peut contenir des données personnelles de tiers, et il n'est presque jamais nécessaire pour diagnostiquer un proxy. Si vous devez comprendre ce qu'a renvoyé la ressource cible, conservez un hash du corps et sa longueur, et mettez la réponse elle-même dans un stockage séparé à durée de vie courte et uniquement sur échantillon.
  4. Les cookies dans les deux sens.
  5. L'URL complète avec paramètres de requête. L'hôte suffit et, si besoin, le chemin sans paramètres.
  6. L'adresse réelle du proxy en clair si le log part dans un stockage partagé. L'identifiant interne suffit.

La pratique que nous recommandons : implémentez la rédaction au niveau d'une fonction unique par laquelle passe toute écriture liée aux proxys. Ne comptez pas sur la discipline des développeurs.

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

Échantillonnage et conservation

Faut-il écrire chaque tentative ? Les erreurs — oui, toutes. Les tentatives réussies — on peut les échantillonner : au-delà de cent requêtes par seconde, 5 à 10 % de lignes réussies suffisent pour estimer les latences, car les métriques contiennent déjà l'image complète. L'important est que la décision d'échantillonner soit prise par request_id et non au hasard à chaque tentative : sinon vous obtiendrez la deuxième tentative sans la première. La durée de conservation des logs détaillés est généralement de 7 à 14 jours ; les agrégats et les métriques vivent plus longtemps.

Percentiles plutôt que moyenne : pourquoi la moyenne masque le problème

Imaginez un pool de cent adresses. Quatre-vingt-dix répondent en 0,8 seconde. Dix répondent en 9 secondes, parce qu'elles sont tombées sur une station de base surchargée. Le temps de réponse moyen est de 1,62 seconde. Une semaine plus tôt, quand tout allait bien, la moyenne était de 1,4. Une hausse de 15 %. Qui va réagir à ça ? Personne. Pourtant, une requête sur dix chez vous attend neuf secondes, et si vous avez un timeout à huit, une requête sur dix échoue avec une erreur.

La moyenne arithmétique décrit bien les distributions en forme de cloche. Les latences à travers un proxy n'ont jamais cette forme. C'est une distribution à longue queue droite : la plupart des requêtes sont rapides, mais certaines sont très lentes, et ce sont elles qui déterminent l'expérience utilisateur et le coût. De plus, les latences à travers un pool sont souvent bimodales : il y a une bosse « normale » et une bosse « adresses problématiques ». La moyenne tombe entre les deux, dans une zone où il n'y a pratiquement aucune requête réelle.

Que sont p50, p95, p99

Un percentile est la valeur en dessous de laquelle se situe une proportion donnée des observations. p50 (médiane) — la moitié des requêtes est plus rapide que cette valeur. p95 — 95 % plus rapides, 5 % plus lentes. p99 — seulement un pour cent plus lentes. Pour un pool de proxys, nous recommandons de regarder trois percentiles avec des objectifs différents :

  • p50 montre la requête « typique ». Si le p50 augmente, la majorité a dégradé. C'est un signal rare et sérieux.
  • p95 — la métrique de travail pour les alertes. Elle est sensible aux segments problématiques (dans l'exemple ci-dessus, le p95 affichera immédiatement 9 secondes), mais résistante aux valeurs aberrantes isolées.
  • p99 — la métrique pour les timeouts et la planification. Votre timeout doit être légèrement au-dessus du p99 « sain », sinon vous couperez des requêtes normales et les transformerez en retries.

Comment calculer avec précision

Si les données sont peu nombreuses (quelques milliers de valeurs dans la fenêtre), calculez exactement : triez et prenez l'élément à l'index voulu.

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))

Le calcul exact ne passe pas à l'échelle : pour calculer un p95 sur une semaine et des millions de requêtes, il faudrait stocker toutes les valeurs. Les systèmes industriels utilisent donc des structures approximatives.

Comment calculer à l'échelle : les histogrammes

Prometheus et les systèmes compatibles stockent un histogramme sous forme d'un ensemble de compteurs par bucket : combien d'observations étaient inférieures à 0,1 s, inférieures à 0,25 s, inférieures à 0,5 s, etc. Le percentile se reconstitue par interpolation linéaire à l'intérieur du bucket. La précision dépend du bon choix des bornes. Pour les latences à travers un proxy, une échelle logarithmique de 100 millisecondes à 30 secondes fonctionne bien : 0,1 ; 0,25 ; 0,5 ; 1 ; 2 ; 4 ; 8 ; 16 ; 30. Si votre p95 sain est autour de 2 secondes et votre timeout à 15, une telle grille donnera une erreur de l'ordre de 20 à 30 % sur la queue, ce qui suffit pour les alertes et le diagnostic.

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

Attention au sum by (le, ...) à l'intérieur. C'est un détail critique : les histogrammes de différentes instances de votre client doivent d'abord être additionnés, puis le quantile calculé. L'opération inverse — calculer le p95 sur chaque instance et faire la moyenne — n'a aucun sens mathématique. La moyenne des percentiles n'est pas un percentile. C'est l'une des erreurs les plus fréquentes, et elle peut masquer un problème aussi sûrement qu'une moyenne ordinaire.

Si vous avez besoin d'une grande précision sur la queue (par exemple si vous vendez un SLA de latence à des clients internes), utilisez des structures de type t-digest ou HDR-histogram, qui offrent une précision de l'ordre de la fraction de pour cent à mémoire constante. Pour la plupart des équipes, les histogrammes standards suffisent.

Découpage par dimensions : pays, opérateur, type de proxy

Voici un scénario qui illustre l'utilité des dimensions. Vous avez un pool de 400 adresses, dont 5 % sont des adresses mobiles d'un opérateur dans une région. Ce segment a complètement dégradé : le taux de réussite y est tombé à 10 %. Que montrera le graphe global du taux de réussite du pool ? Une baisse de 99 % à 94,5 %. Visible, mais pas critique. L'astreinte pensera « le réseau fait des siennes » et passera à autre chose. Or le client qui consomme les données de cette région recevra du vide.

Le découpage par dimensions (en terminologie Prometheus — les labels) résout précisément ce problème : il permet de décomposer l'agrégat en tranches et de voir que le problème est localisé. Mais les labels ont un coût, qu'il faut comprendre.

Quelles dimensions choisir

Une bonne dimension a deux propriétés : un nombre de valeurs restreint et borné, et un découpage qui répond à une vraie question de diagnostic. Pour un pool de proxys, nous recommandons quatre dimensions dans les métriques :

  • proxy_type — mobiles, résidentiels, datacenter. Les différents types dégradent différemment et ont des coûts différents.
  • country — le pays de l'adresse de sortie. Généralement de 1 à 30 valeurs.
  • operator — l'opérateur télécom ou le système autonome par lequel sort l'adresse. Les problèmes sont souvent localisés précisément là : une station de base surchargée ou un mauvais peering chez un opérateur.
  • pool — l'affectation logique : quel produit ou quelle tâche utilise ce segment. Permet de séparer les incidents selon l'impact métier.

Une cinquième dimension mérite réflexion — target_host, l'hôte cible. Elle est nécessaire pour distinguer les problèmes de proxy des problèmes de cible. Mais si vous tapez sur des milliers de domaines différents, la cardinalité va exploser. Compromis : ne gardez dans le label que le top 20 des hôtes par volume, et regroupez le reste dans une valeur other.

L'arithmétique de la cardinalité

Chaque combinaison unique de valeurs de labels est une série temporelle distincte. Faisons le calcul : 3 types × 10 pays × 15 opérateurs × 4 pools × 7 issues = 12 600 séries rien que pour le compteur de requêtes. Une histogramme à 9 buckets multipliera son nombre de séries par 10. Total : environ 50 000 à 60 000 séries. Pour Prometheus, c'est confortable. Imaginez maintenant que vous ajoutiez un label avec l'adresse du proxy (400 valeurs). Le nombre de séries est multiplié par 400 — jusqu'à 20 millions. Ce n'est plus « confortable », c'est la mort du stockage.

D'où la règle : l'adresse d'un proxy précis vit dans les logs, pas dans les métriques. Les métriques répondent à « quelle tranche a dégradé », les logs à « quelles adresses exactement dans la tranche ». Cette division du travail donne à la fois la vitesse et le détail sans explosion des coûts.

Requêtes qui trouvent la tranche

La question centrale du diagnostic : « où est-ce le pire ? » On y répond en triant les tranches par taux de réussite :

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]))
)

Deuxième question : « est-ce un nouveau problème ou ça a toujours été comme ça ? » On y répond par comparaison avec le passé :

(
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))
)

Une valeur négative montre à quel point la tranche s'est dégradée par rapport à il y a 24 heures. Un tel graphe sur un tableau de bord transforme « il y a un truc qui cloche quelque part » en « opérateur B dans le pays C, moins 12 points par rapport à hier ».

D'où viennent les valeurs des dimensions

Le pays et le type de proxy sont généralement connus depuis la configuration du pool. L'opérateur peut s'obtenir à partir des métadonnées du fournisseur : par exemple, dans le panneau et l'API Proxeon, les adresses sont accompagnées d'informations de géographie et d'opérateur, que vous récupérez à l'initialisation du pool et mettez en cache à côté de l'identifiant interne du proxy. Déterminer l'opérateur au moment de la requête via des bases externes n'est pas recommandé : cela ajoutera de la latence et créera une dépendance inutile.

Alertes qui ne font pas de bruit : seuils, fenêtres, hystérésis

La pire chose qui puisse arriver à un système d'alerte n'est pas un incident manqué. La pire, c'est quand l'astreinte reçoit 40 notifications par jour, dont 39 ne veulent rien dire, et cesse de lire la quarantième. Une alerte qu'on ignore est pire que pas d'alerte du tout, parce qu'elle crée l'illusion du contrôle.

Principe 1 : alerter sur le symptôme, pas sur la cause

Ne déclenchez pas d'alerte sur « le proxy px-2841 a renvoyé une erreur ». Une adresse isolée a le droit d'être malade ; c'est le rôle du mécanisme de quarantaine à l'intérieur du pool, que nous n'abordons pas ici. Déclenchez l'alerte sur ce que ressent le consommateur de vos données : taux de réussite du pool sous l'objectif, p95 au-dessus de l'objectif, retries au-dessus de l'objectif. Vous trouverez la cause sur le tableau de bord en une minute si vous avez bien fait vos dimensions. Le rôle de l'alerte est de réveiller, pas d'expliquer.

Principe 2 : volume de trafic minimal

Un taux de réussite de 50 % sur deux requêtes en cinq minutes, c'est une requête ratée, pas un incident. Toute règle sur un taux doit comporter une condition de nombre minimal de requêtes dans la fenêtre. Sinon, la nuit, quand le trafic chute, vous vous noierez dans les faux positifs.

Principe 3 : fenêtres et vitesse de consommation du budget

Si vous avez défini un objectif de fiabilité (par exemple 98 % de requêtes réussies sur un mois), vous avez un budget d'erreur : 2 % des requêtes. Une bonne alerte réagit à la vitesse de consommation du budget, pas à une valeur instantanée. La pratique devenue standard dans l'ingénierie de la fiabilité est celle des alertes multi-fenêtres : une fenêtre courte (5 minutes) attrape les chutes brutales, une fenêtre longue (1 heure) la dégradation lente, et les deux doivent être dépassées simultanément pour exclure les pics passagers.

Principe 4 : hystérésis

L'hystérésis, ce sont des seuils différents pour déclencher et pour lever l'alerte. Si l'alerte se déclenche à un taux de réussite sous 95 % et se lève au retour au-dessus de 95 %, une métrique oscillant autour de 95 % générera une série de notifications « déclenchée — levée — déclenchée » toutes les quelques minutes. Solution : déclencher à 95 %, lever à 97 %. Ou, plus simplement, exiger que la condition persiste continûment pendant un temps donné (dans Prometheus, le mot-clé for), et prévoir aussi un délai de levée.

Principe 5 : niveaux de gravité et routage

Tout n'a pas besoin d'être délivré par téléphone. Séparez les alertes en deux ou trois niveaux : critiques (impact sur les consommateurs de données en ce moment — appel ou push à l'astreinte), avertissements (dégradation de tranche, hausse des retries, anomalie de trafic — message dans un canal, à traiter en heures ouvrées), informationnels (absence de données d'une instance, approche d'une limite de trafic — résumé quotidien).

Exemple de jeu de règles

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 }

Notez la dernière règle. L'absence de données est la panne la plus sournoise : le client tombe, il n'y a plus de métriques, les graphes sont plats et verts, tout le monde est tranquille. Une alerte sur le silence est obligatoire.

Check-list pour chaque nouvelle alerte

  1. L'annotation indique-t-elle quoi faire au destinataire ? Si non, la règle n'est pas prête.
  2. Y a-t-il une condition de trafic minimal ?
  3. Y a-t-il un délai for ou une hystérésis ?
  4. La règle a-t-elle été testée sur l'historique du dernier mois : combien de fois se serait-elle déclenchée, et combien de fois pour de vrais incidents ?
  5. Est-il clair à quel niveau de gravité elle appartient et où elle est routée ?
  6. Un processus inverse est-il prévu : si l'alerte s'est déclenchée et s'est révélée fausse, qui la révisera et quand ?

Diagnostic rapide sur tableau de bord : trois scénarios typiques

Un tableau de bord n'est pas une galerie de graphes, mais un outil de décision. Un bon tableau de bord pour un pool de proxys est structuré comme un entonnoir. Ligne du haut : les quatre signaux sur tout le pool sur les 6 dernières heures, avec la journée de la veille en ligne fine superposée. Deuxième ligne : les mêmes signaux, découpés par pays, opérateur et type — sous forme de tableau ou de carte de chaleur, triés par pire valeur. Troisième ligne : taux de réussite et TTFB pour le top 10 des hôtes cibles. Quatrième : rapport entre temps de connexion, TTFB et temps total. Plus des marqueurs verticaux des déploiements et des changements de configuration du pool.

Avec un tel tableau de bord, le diagnostic se réduit à répondre à trois questions : « est-ce tout le pool qui a dégradé ou une tranche ? », « est-ce toutes les cibles ou une seule ? », « laquelle des trois latences a augmenté ? ». Les combinaisons de réponses donnent trois scénarios typiques.

Scénario 1 : toutes les tranches ont dégradé en même temps

Tous les pays, tous les opérateurs, tous les types de proxys montrent une hausse des erreurs ou des latences de façon synchrone, à la minute près. Des fournisseurs et opérateurs externes ne tombent pas comme ça : ils n'ont pas de point de défaillance commun de votre côté. Donc le problème est chez vous : votre canal sortant, votre DNS, épuisement des descripteurs de fichiers ou des ports locaux, un déploiement récent ayant modifié les timeouts ou la taille du pool de connexions, une concurrence pour le CPU avec un autre processus sur la même machine. Première action : regarder le marqueur de déploiement et le temps de connexion au proxy. Si le temps de connexion a augmenté et que le TTFB (moins la connexion) est stable, le problème est entre vous et les nœuds proxy, et c'est presque certainement de votre côté ou celui de votre opérateur réseau.

Scénario 2 : une seule tranche a dégradé

Un seul opérateur dans un seul pays, ou un seul type de proxy, montre une chute, tandis que les autres tranches sont stables. C'est le scénario le plus fréquent. La cause est chez l'opérateur télécom (saturation d'un segment réseau, travaux planifiés) ou dans un segment de l'infrastructure du fournisseur de proxys. Première action : réduire temporairement le poids de cette tranche dans la répartition de charge (le mécanisme dépasse le cadre de cet article) et écrire au support du fournisseur un message avec des données concrètes : la tranche, l'heure de début, les métriques avant et après, des exemples de request_id. Le support Proxeon réagit à ce type de demande plus vite qu'à un « tout va mal chez vous », simplement parce qu'il reçoit une localisation prête à l'emploi. Deuxième action : regarder les logs de la tranche pour comprendre si toutes les adresses ont dégradé ou seulement quelques-unes.

Scénario 3 : le taux de réussite a chuté pour un seul hôte cible, les latences sont normales

Toutes les tranches du pool continuent de bien communiquer avec tous les hôtes, sauf un. Pour cet hôte, la part de upstream_4xx augmente ou les octets par requête ont changé. Les proxys n'y sont pour rien. La ressource cible a modifié son comportement : elle a introduit une limitation de fréquence, changé la structure de la page, commencé à renvoyer un autre contenu à une partie des requêtes. Première action : réduire l'intensité des requêtes vers cette ressource et vérifier que votre collecte respecte ses règles d'utilisation. Deuxième : vérifier le contenu sur un échantillon et mettre à jour la validation des réponses. Il ne faut surtout pas, dans cette situation, augmenter le nombre de retries ou changer d'adresses plus souvent — cela ne ferait qu'accroître la charge sur la ressource et vos coûts de trafic.

Scénario 4 (bonus) : les retries augmentent, le succès tient

Taux de réussite 99 %, latences tolérables, mais le taux de retry est passé de 1,1 à 1,8. C'est une dégradation cachée. Elle vous coûte de l'argent dès maintenant et deviendra une dégradation visible dès que la réserve de tentatives s'épuisera. Première action : regarder quelle issue est à l'origine des répétitions (le champ retry_reason dans les logs), puis suivre l'un des trois premiers scénarios.

Check-list de diagnostic en 60 secondes

  1. Secondes 0–10 : ligne du haut du tableau de bord. Lequel des quatre signaux s'est écarté de la ligne de la veille ? Quand exactement ?
  2. Secondes 10–25 : tableau par tranches. Une tranche rouge ou toutes ? Si toutes — scénario 1, aller aux marqueurs de déploiement et à votre infrastructure.
  3. Secondes 25–40 : tableau par hôtes cibles. Un hôte ou tous ? Si un seul — scénario 3, le proxy n'y est pour rien.
  4. Secondes 40–50 : rapport entre les latences. Est-ce la connexion, le TTFB ou le temps total qui a augmenté ?
  5. Secondes 50–60 : formuler une phrase : « depuis [heure], dans la tranche [tranche] pour [cible], [métrique] est passée de [avant] à [après], probablement à cause de [hypothèse] ». Cette phrase part dans le canal d'incident et dans le ticket au fournisseur.

Erreurs typiques qui annulent le bénéfice de l'observabilité

Nous avons vu des dizaines de mises en œuvre de monitoring autour de proxys. Les erreurs se répètent avec une constance étonnante.

  • Le temps de réponse moyen comme métrique principale. Déjà traité : la moyenne cache la queue, et la queue, c'est justement le problème.
  • Moyenner les percentiles entre instances. Additionnez les histogrammes, pas les p95 déjà calculés.
  • L'adresse du proxy comme label de métrique. Explosion de cardinalité et effondrement du stockage au bout d'un mois.
  • Les identifiants d'accès dans les logs. Une seule ligne « pour déboguer » et le mot de passe repose dans un stockage de logs partagé pendant un an.
  • Les retries comptés comme des succès. Si vous ne comptez que l'issue finale, vous ne verrez jamais que le pool travaille trois fois plus intensément que nécessaire.
  • Les codes 429 et 403 de la ressource cible enregistrés comme erreurs de proxy. C'est une erreur de classification qui oriente l'enquête dans la mauvaise direction et abîme la relation avec le fournisseur.
  • Une seule latence au lieu de trois. Sans mesure séparée de la connexion, du TTFB et du temps total, vous ne distinguerez pas un problème de disponibilité d'un problème de bande passante.
  • Une alerte sans trafic minimal. Bruit nocturne et perte de confiance dans les alertes.
  • Une alerte sur chaque adresse individuelle. C'est le rôle de la quarantaine, pas celui de l'alerte humaine.
  • Pas d'alerte sur l'absence de données. Le client tombe — les graphes sont verts.
  • L'heure locale dans les logs. La corrélation avec les données du fournisseur et des collègues dans un autre fuseau devient un supplice.
  • Un tableau de bord sans marqueurs de déploiement. La moitié des incidents coïncide avec un changement de code ou de configuration. Sans marqueur, vous chercherez la cause à l'extérieur alors qu'elle est à l'intérieur.
  • Conserver les logs détaillés indéfiniment. Coûteux et inutile : au bout de deux semaines, les détails ne servent plus, et les agrégats sont déjà dans les métriques.

Une stack minimale sans surcomplification

L'observabilité se transforme facilement en projet pour le projet. Une équipe de trois personnes déploie une file de messages distribuée, un cluster de base colonne et un système de traçage — pour un client qui fait 20 requêtes par seconde. Il ne faut pas faire ça. La stack doit correspondre à l'échelle.

Niveau 1 : un seul service, jusqu'à 50 requêtes par seconde

Il suffit de logs JSON structurés dans un fichier ou sur la sortie standard, et d'un script qui les agrège une fois par minute et écrit les quatre signaux dans une base simple ou même dans le même log. Le tableau de bord, c'est n'importe quel outil capable de tracer un graphe à partir d'un tableau. Les alertes, c'est un script cron qui compare les agrégats aux seuils et envoie un message dans une messagerie. Une telle stack se met en place en un jour et couvre 80 % des besoins.

Niveau 2 : plusieurs services, 50 à 1 000 requêtes par seconde

L'ensemble standard : Prometheus pour les métriques, Grafana pour les tableaux de bord, Alertmanager pour le routage des alertes, Loki ou un stockage équivalent pour les logs indexés par labels. Les clients exportent les métriques via un endpoint HTTP intégré et écrivent des logs JSON sur la sortie standard ; un agent les collecte. Les recording rules pré-calculent les taux de réussite pour que les tableaux de bord s'ouvrent instantanément. C'est la configuration décrite dans les exemples ci-dessus, et elle couvre la grande majorité des équipes.

Niveau 3 : des dizaines de services, des milliers de requêtes par seconde

Ici, OpenTelemetry comme couche de collecte unifiée prend tout son sens, ainsi qu'un stockage colonne pour les logs permettant des requêtes analytiques arbitraires sur des milliards de lignes et, éventuellement, des traces pour les chaînes complexes. Mais même à ce niveau, les quatre signaux et la structure des logs ne changent pas. Seuls le transport et le stockage évoluent.

Les données du fournisseur en complément

Le fournisseur de proxys voit son propre côté : consommation de trafic par port, nombre de connexions actives, heure de la dernière rotation. Le panneau et l'API Proxeon fournissent ces statistiques, et il est utile de les rapprocher de vos propres métriques — ne serait-ce que pour recouper la consommation de trafic : si votre compteur d'octets et celui du fournisseur divergent de plus de 10 à 15 %, vous avez un problème de mesure (le plus souvent, les en-têtes, les handshakes TLS ou les réponses interrompues ne sont pas pris en compte). Mais les données du fournisseur ne remplacent pas les vôtres : vous seul savez quelle requête a été un succès du point de vue de la tâche.

Le wrapper client comme point de mesure unique

Quel que soit le niveau de la stack, toutes les mesures doivent passer par un wrapper unique au-dessus du client HTTP. Pas de « ici on mesure, ici on a oublié ». Schématiquement :

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 # уже настроена на прокси по внутреннему 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

Notez l'issue invalid_content : une réponse avec un code 200 qui n'a pas passé votre validation (par exemple une page sans le bloc de données attendu) n'est pas un succès. Ce sont précisément ces réponses qui masquent le plus souvent un changement de comportement de la ressource cible.

Cas concrets : à quoi ressemble l'observabilité en pratique

Cas 1 : un opérateur, un pays, moins deux heures d'indisponibilité par semaine

Une équipe de surveillance de prix sur des places de marché publiques travaillait via un pool d'environ 300 adresses mobiles et résidentielles, à une intensité d'environ 40 requêtes par seconde. Avant d'introduire le découpage par opérateur, elle ne voyait qu'une chose : le p95 TTFB global bondissait de temps en temps de 1,6 à 3–4 secondes pendant quelques heures, et le taux de réussite baissait de 3 à 5 points. Chaque épisode demandait entre une et deux heures d'investigation, et se terminait le plus souvent par « c'est passé tout seul ». Après l'ajout des labels country et operator, le premier épisode s'est localisé en deux minutes : adresses mobiles d'un opérateur dans une région, p95 à 11 secondes contre une norme de 2. Les 85 % restants du pool étaient parfaitement sains. L'équipe a réduit le poids de la tranche dans la répartition, envoyé au fournisseur un rapport avec les plages horaires et les métriques, et a reçu quelques heures plus tard la confirmation de travaux planifiés chez l'opérateur. Bilan sur le trimestre : le temps de diagnostic est passé de 90 minutes à 5, le nombre d'épisodes n'a pas changé, mais leur impact sur le p95 global est devenu indiscernable.

Cas 2 : retries cachés et +38 % sur la facture de trafic

Une autre équipe a constaté une hausse de la consommation de trafic de 38 % sur un mois à nombre de tâches constant et taux de réussite stable à 98,5 %. Première hypothèse : les pages cibles sont devenues plus lourdes. La métrique des octets par requête réussie l'a réfutée : 46 kilo-octets, comme avant. En revanche, le taux de retry, que personne n'avait regardé jusque-là, était passé de 1,08 à 1,52. L'analyse du champ retry_reason dans les logs a montré : presque toutes les répétitions étaient causées par l'issue upstream_timeout sur un hôte cible devenu plus lent, mais qui rentrait dans le timeout à la deuxième tentative. Les proxys n'étaient pas du tout la cause. L'équipe a augmenté le timeout de lecture pour cet hôte de 8 à 15 secondes (le p99 sain était de 11), réduit l'intensité des requêtes vers lui et ramené la consommation de trafic au niveau antérieur en trois jours. Morale : le taux de retry est une métrique qui parle d'argent.

Cas 3 : de 200 notifications par semaine à six

Une troisième équipe a hérité d'un système d'alerte avec la règle « taux de réussite de n'importe quelle tranche sur 1 minute sous 90 % », sans limite de trafic. La nuit, quand certaines tranches recevaient deux ou trois requêtes par minute, la règle se déclenchait des dizaines de fois. Les astreintes ont coupé le son. Un vrai incident — la chute de disponibilité d'un segment entier pendant quatre heures — n'a été remarqué que le matin, via des plaintes. La refonte des règles a pris une journée : ajout d'une condition de trafic minimal, introduction de deux fenêtres (5 minutes et 1 heure), ajout d'un délai for de 10 minutes, déplacement des tranches au niveau warning, et conservation d'un seul critical au niveau pool. Résultat : environ six notifications par semaine, dont quatre à cinq nécessitant une action réelle. Les astreintes ont rallumé le son.

FAQ : questions fréquentes sur l'observabilité d'un pool de proxys

Faut-il des traces si on a déjà métriques et logs ?

Pour la plupart des équipes, non. Une requête via un proxy ne traverse pas dix microservices ; elle a une seule transition. Le rôle de la trace est ici joué par le request_id dans les logs : avec lui, vous reconstituez toutes les tentatives d'une même requête. Les traces deviennent utiles quand le client proxy n'est qu'un maillon d'une longue chaîne (file de tâches, parseur, validateur, chargeur en base) et que vous devez comprendre où précisément le temps se perd.

Comment mesurer le TTFB si le client HTTP ne le fournit pas directement ?

La plupart des clients rendent la main après réception des en-têtes si on demande la lecture en flux (stream=True ou équivalent). Ce moment est une approximation du TTFB. Le temps de connexion se mesure plus précisément au niveau du transport : beaucoup de bibliothèques exposent des hooks sur les événements de connexion, certaines fournissent directement des informations sur les phases de la requête. S'il n'y a pas de hook, on peut mesurer séparément le temps jusqu'à l'envoi du premier octet de la requête par une mesure bas niveau, mais pour l'usage pratique, l'approximation par lecture en flux suffit.

Comment distinguer une erreur de proxy d'une erreur de ressource cible ?

Par l'endroit où elle survient. Si la connexion au proxy ne s'est pas établie, ou si le proxy a renvoyé son propre code (par exemple 407 en cas de problème d'authentification, ou une réponse spécifique de passerelle), c'est une erreur de proxy. Si le proxy a accepté la requête et que vous avez ensuite reçu un code du serveur cible ou un timeout d'attente de réponse, c'est une erreur entre le proxy et la cible, ou de la cible elle-même. Classez à cette frontière, puis comparez par tranches : les erreurs de cible se voient simultanément pour toutes les tranches, les erreurs de proxy pour des tranches spécifiques.

Quel est un niveau sain de taux de retry ?

Cela dépend des objectifs et du type de proxy. Pour des adresses mobiles, on peut considérer 1,05–1,15 comme normal : un réseau sans fil est par nature moins prévisible. Pour des adresses datacenter, 1,01–1,05. Plus que la valeur absolue, c'est sa stabilité qui compte : une hausse de 30 % par rapport au niveau de base hebdomadaire mérite qu'on s'y intéresse, même si la valeur absolue reste modeste.

Comment choisir les seuils d'alerte sans historique ?

Commencez par observer sans alerter : collectez deux semaines de données, regardez la distribution du taux de réussite et du p95 par jour et par heure. Fixez le seuil critical pour qu'il se serait déclenché au maximum une ou deux fois sur ces deux semaines, et que dans les deux cas vous auriez été d'accord qu'il s'agissait d'un incident. Le seuil warning, pour qu'il se déclenche quelques fois par semaine. Puis révisez chaque mois : une alerte qui n'a jamais conduit à une action est soit durcie, soit supprimée.

Faut-il conserver le corps de la réponse, au moins pour les erreurs ?

Pour les erreurs de transport, il n'y a pas de corps. Pour les réponses au contenu inattendu (invalid_content), il est utile de conserver les premières centaines d'octets, ou un hash et une longueur, dans un stockage séparé d'une durée de vie de un à deux jours, avec nettoyage obligatoire des données personnelles. Les corps complets ne sont pas nécessaires : ils sont lourds, ils peuvent contenir des données de tiers, et pour diagnostiquer un proxy il suffit presque toujours de comprendre quel type de page est arrivé.

Comment recouper son comptage de trafic avec les données du fournisseur ?

Comptez les octets dans les deux sens, en-têtes inclus, et comparez les sommes journalières par port ou par session avec ce qu'affiche le panneau du fournisseur. Un écart de 5 à 10 % est normal : vous ne comptez pas les surcoûts TLS ni les connexions interrompues. Un écart supérieur à 20 % mérite une vérification : peut-être ne comptez-vous que le corps de la réponse et perdez-vous les tentatives qui se sont terminées par une exception avant la lecture du corps.

Combien de temps prend la mise en œuvre de cette approche à partir de zéro ?

Pour un client avec un wrapper HTTP existant : une journée pour les métriques et la structure des logs, une journée pour le tableau de bord, une journée pour les règles d'alerte, et encore deux semaines d'observation avant d'activer les alertes critical. Si vous avez plusieurs clients dans des langages différents, le gros du délai passe dans la concertation sur un ensemble unifié d'issues et de labels — il ne faut pas se précipiter, car renommer une métrique après un an d'historique accumulé est très douloureux.

Que faire si le fournisseur n'a pas de données d'opérateur pour une partie des adresses ?

Créez une valeur unknown et surveillez sa part. Si unknown dépasse 10 à 15 % du pool, vos découpages par opérateur perdent leur sens — demandez au fournisseur des métadonnées complètes ou étiquetez les adresses manuellement une fois pour toutes lors de leur ajout au pool.

Conclusion : une minute au lieu d'une heure

Revenons à la scène du début. « Les proxys rament » est une phrase qui ne contient aucune information. Tout ce qui est décrit dans ce guide sert un seul objectif : la transformer en une phrase avec des chiffres, une tranche, une heure et une hypothèse. Quatre signaux — taux de réussite, latence en percentiles, taux de retry, consommation de trafic — donnent l'image de l'état du pool. Un log structuré avec identifiant de requête, identifiant de proxy, numéro de tentative, code, trois latences et taille — les détails pour l'investigation. Les percentiles plutôt que la moyenne montrent la queue, où vivent les vrais problèmes. Le découpage par pays, opérateur et type localise la dégradation jusqu'à la tranche. Les alertes avec trafic minimal, deux fenêtres et hystérésis se déclenchent rarement et à bon escient. Le tableau de bord en entonnoir répond à trois questions en une minute.

Par quoi commencer demain ? Premier pas : un wrapper au-dessus du client avec une classification unifiée des issues et une fonction unique d'écriture. Deuxième : un histogramme TTFB et un compteur de tentatives avec des labels par pays, opérateur et type. Troisième : une fonction de rédaction des logs par laquelle tout passe sans exception. Quatrième : un tableau de bord avec quatre graphes et un tableau par tranches. Cinquième : deux semaines d'observation, et seulement ensuite les alertes. Dans un mois, vous regarderez le graphe du taux de retry et, peut-être bien, vous serez surpris : le pool travaillait nettement plus intensément que vous ne le pensiez. Et dans un trimestre, la phrase « tout rame chez nous » aura disparu de votre chat — non pas parce qu'il n'y aura plus de problèmes, mais parce que vous appellerez chacun d'eux par son nom.