Développement3 août 2026· via DEV Community

Refonte des métriques OpenTelemetry : briser la malédiction du dieu monolithique

Refonte des métriques OpenTelemetry : briser la malédiction du dieu monolithique

Image : DEV Community

L’API des métriques d’OpenTelemetry force chaque instrument—compteur, histogramme ou jauge—à s’intégrer dans un singleton global unique au processus. Cette contrainte fait de l’emplacement de ce singleton la question architecturale centrale pour tout codebase utilisant OTel. Une refonte récente dans un système en production montre comment une mauvaise réponse peut s’étendre en un cauchemar de maintenance, tandis que la bonne approche maintient la légèreté et la sécurité.

L’anti-modèle : une classe unique, de nombreux mixins

Les équipes centralisent souvent toutes les métriques dans un objet global Metrics, construit au démarrage et transmis dans l’application. Fonctionnant comme un singleton, il mute rapidement en un « objet dieu » envahissant. Chaque sous-système ajoute simplement un nouveau mixin—GeneralMetricsMixin, AcquisitionMetricsMixin, CameraMetricsMixin—et la classe s’étend sans limite. Le défaut se révèle quand les mixins entrent en collision. Python résolvant silencieusement les conflits de noms, deux mixins définissant _duration_metric peuvent s’écraser mutuellement, désactivant discrètement un flux de métriques. Les développeurs recouraient à des contournements baroques comme appeler explicitement GeneralMetricsMixin.measurement_stopped(self) pour éviter les collisions.

La solution : des modules par sous-système

Le nouveau modèle démantèle le monolithe. Chaque sous-système obtient son propre module composé de quatre éléments : une constante de portée, une classe dataclass immuable contenant les handles coûteux d’OTel, une fonction de création en cache des singletons, et une classe légère d’enrobage instanciable à chaque requête.

import dataclasses as dc import functools from opentelemetry import metrics

_SCOPE = "myapp.api.prediction"

@dc.dataclass(frozen=True, slots=True) class _Instruments: failure_counter: metrics.Counter duration: metrics.Histogram

@functools.cache def _get_instruments() -> _Instruments: meter = metrics.get_meter(_SCOPE) return _Instruments( failure_counter=meter.create_counter(name=f"{meter.name}.failures"), duration=meter.create_histogram(name=f"{meter.name}.duration", unit="s"), )

class PredictionMetrics: def init(self, settings): self._instruments = _get_instruments() self._settings = settings

La classe dataclass, immuable après création, le cache garantit une instance unique au processus, et la classe d’enrobage offre une API ergonomique aux appelants. Plus d’héritage, plus de noms masqués, plus d’échecs invisibles.

Pourquoi c’est important

Cette refonte dépasse une simple préférence de style : elle transforme la contrainte de singleton d’OpenTelemetry, passant d’une faiblesse à un atout. En isolant les instruments par sous-système, les équipes réduisent les conflits de fusion, éliminent les pertes de métriques silencieuses et rendent la propriété évidente. Ce modèle s’adapte proprement à travers les services et les langages, s’alignant sur la tendance industrielle vers des architectures modulaires et observables. Pour toute équipe aux prises avec des classes de métriques envahissantes, la leçon est claire : lorsque le runtime impose un singleton, conçoivez le code pour que le singleton soit l’exception, et non l’ensemble de l’application.


Source : DEV Community. Synthèse éditoriale assistée par IA — TechnoExpress.

Lire la source originale sur DEV Community →

← Retour à l'accueil