OpenTelemetry de bout en bout¶
Arclith fournit un runtime OpenTelemetry optionnel qui compose les providers, les exporters OTLP,
la propagation et les instrumentations aux frontières. Le domaine et les use cases ne dépendent que
des ports neutres TracePort, MetricPort, CorrelationContextPort,
ContextPropagatorPort et ObservabilityRuntimePort.
Les projets consommateurs restent responsables de leurs attributs métier, dashboards, alertes, règles de rétention et budgets de télémétrie.
Les trois opt-in¶
OpenTelemetry n'est actif que si les trois décisions suivantes sont prises séparément :
- installer l'extra
arclith[opentelemetry]; - ajouter
opentelemetryàadapters.observability.enabled; - activer au moins un signal dans
signals.traces,signals.metricsousignals.logs.
Sans activation, Arclith utilise NoOpObservabilityRuntime. Aucun SDK, provider, processor,
thread, exporter ou appel réseau OpenTelemetry n'est créé. La corrélation et la propagation restent
accessibles derrière des no-op.
Générer un profil¶
Profil local :
| Bash | |
|---|---|
Le profil development active traces et métriques, sampling à 100 %, corrélation locale des logs
et endpoint Collector local. L'export OTLP des logs et toute capture de contenu restent désactivés.
Profil production :
| Bash | |
|---|---|
Le profil production utilise un sampler parent-based, active traces et métriques, conserve les
logs OTLP en opt-in et n'écrit aucun header d'authentification dans Git. Le CLI ajoute l'extra
Python et fusionne .env.example de façon idempotente.
Configuration complète¶
L'ancien YAML plat (service_name, endpoint, traces, metrics,
instrument_fastapi, etc.) reste accepté et migré en mémoire. Les nouveaux projets doivent
utiliser la forme imbriquée.
Précédence et variables standard¶
La résolution suit :
opentelemetry_overridespassé àArclith;- variables
OTEL_*; - YAML ;
- valeurs par défaut.
| Python | |
|---|---|
Arclith supporte notamment OTEL_SDK_DISABLED, OTEL_SERVICE_NAME,
OTEL_RESOURCE_ATTRIBUTES, OTEL_EXPORTER_OTLP_*, les endpoints et headers propres à chaque
signal, OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTER,
OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG, OTEL_PROPAGATORS, les réglages batch et les
limites d'attributs/spans.
Les variables ne sont jamais recopiées dans os.environ. Une valeur vide est traitée comme
absente. Les booléens standard acceptent uniquement true ou false, insensibles à la casse.
Les variables OTEL_EXPORTER_OTLP_TIMEOUT et propres à chaque signal suivent le type Timeout
stable OpenTelemetry : un entier en millisecondes (10000 représente 10 secondes), comme
export.timeout_millis dans le YAML.
Les headers restent dans le secret store :
| Bash | |
|---|---|
Ne jamais placer cette valeur dans YAML, .env.example, une image ou un manifeste versionné.
Les diagnostics exposent seulement le nom headers_env, jamais sa valeur.
Identité de service et ressources¶
service.name est toujours résolu : OTEL_SERVICE_NAME, puis service.name, puis app.name.
service.version retombe sur app.version. service.namespace groupe plusieurs services et
service.instance.id peut provenir de la variable configurée par instance_id_env.
OTEL_RESOURCE_ATTRIBUTES surcharge les attributs YAML. Les détecteurs process et host
ajoutent uniquement des attributs techniques standards. Arclith ne déclare aucun
telemetry.distro.* et ne se présente donc pas comme une distribution OpenTelemetry officielle.
Modes de providers¶
| Mode | Responsabilité Arclith | Provider existant | Shutdown |
|---|---|---|---|
managed |
crée providers, readers, processors et exporters | refusé s'il n'est pas un proxy | ferme les ressources au dernier runtime |
attach |
ajoute les processors traces/logs compatibles | obligatoire | ferme uniquement les processors ajoutés |
external |
n'ajoute aucun exporter | obligatoire | ne flush ni ne ferme le provider externe |
En mode attach, Python ne permet pas d'ajouter publiquement un metric reader à un
MeterProvider déjà construit. Son propriétaire doit donc configurer l'export des métriques ;
Arclith utilise ce provider sans le remplacer.
Deux instances Arclith simultanées avec une configuration managed identique partagent les
providers sans dupliquer processors, handlers ou exporters. Une configuration différente dans le
même processus échoue avec un diagnostic explicite. Lorsque le dernier runtime managed est fermé,
les providers globaux Python ne peuvent pas être remplacés proprement : le processus doit être
redémarré avant de créer un nouveau runtime managed.
Cycle de vie¶
FastAPI, les runners MCP et le command bus appellent start(), force_flush() et shutdown() aux
frontières appropriées. Les méthodes sont idempotentes et seules les ressources possédées par
Arclith sont fermées.
Pour un script personnalisé :
| Python | |
|---|---|
Les batch queues et timeouts sont bornés. Une indisponibilité temporaire du Collector reste fail-open ; une configuration invalide, une dépendance absente ou un conflit de provider échoue au démarrage.
Signaux et instrumentations¶
Traces et propagation¶
TracePort crée des spans provider-neutral et enregistre les exceptions/statuts. La propagation
utilise une instance locale des propagateurs W3C, sans modifier le propagateur global. Les carriers
ne conservent que traceparent, tracestate et le baggage explicitement allowlisté et borné.
FastAPI et HTTPX¶
FastAPI utilise l'instrumentation officielle avec providers explicites, routes bruyantes exclues et
spans ASGI internes receive/send supprimés. Les headers ne sont capturés que par allowlist ;
authorization, cookies et proxy authorization sont toujours sanitizés.
HTTPX est instrumenté uniquement si instrumentation.httpx et les traces sont actives. Le runtime
désinstrumente le client au dernier shutdown et évite les doubles monkey-patches. Pour les deux
instrumentations, Arclith remplace les attributs d'URL par une version sans query string ni
fragment, quel que soit le nom du paramètre.
FastMCP¶
Les conventions MCP ne sont pas encore stabilisées. Arclith utilise donc des attributs isolés et
versionnés arclith.mcp.*, un span serveur arclith.mcp.tool et des métriques bornées. Arguments,
résultats et payloads ne sont pas capturés. Une future convention officielle pourra remplacer cet
adaptateur sans modifier le core.
RabbitMQ¶
Le publisher crée un span producer et injecte traceparent, tracestate et le baggage autorisé.
Le consumer extrait et attache le contexte avant le handler, puis le détache dans un finally.
Les spans et métriques couvrent publication, traitement, succès, erreurs et rejet sans exporter le
payload de commande.
Pydantic AI et LangGraph¶
Arclith injecte l'instrumentation Pydantic AI dans les seuls agents construits par
pydantic_ai_llm() ; aucun Agent.instrument_all() global n'est appelé. Les providers du runtime
sont transmis explicitement. capture.genai_content reste false par défaut car prompts,
réponses, messages et résultats de tools peuvent contenir secrets, PII et données métier.
Pour LangGraph, Arclith conserve l'objet CompiledStateGraph et observe en place ses frontières
invoke, ainvoke, stream et astream. Un seul span workflow englobe l'exécution, y compris
lorsque LangGraph délègue en interne d'une méthode à une autre ; ni l'état, ni les événements, ni le
résultat ne sont ajoutés aux spans.
Repositories et caches¶
instrumentation.repositories et instrumentation.caches ajoutent des wrappers opt-in. Ils
mesurent opérations, durée, erreurs et hit/miss sans exporter entités, UUID, clés ou valeurs. Les
métriques locales des probes restent indépendantes ; Arclith n'installe pas un second middleware
HTTP de métriques lorsqu'un événement est déjà produit par l'instrumentation officielle.
Logs : corrélation et export indépendants¶
signals.logs.correlate enrichit les logs locaux avec trace_id, span_id et le sampling flag.
signals.logs.enabled exporte en plus des records OTLP via batch processor.
Le logger Loguru existant reste la sortie console. Arclith émet les records OTLP explicitement via un handler dédié et ne modifie pas le root logger, ce qui évite les doubles émissions et les boucles avec les logs internes de l'exporter. Les logs OpenTelemetry Python restent un signal en évolution : garder l'export en opt-in et valider le backend avant production.
Confidentialité et cardinalité¶
Par défaut, Arclith ne capture pas :
- prompts, réponses LLM, arguments/résultats de tools ou contenu MCP ;
- corps HTTP, query string brute ou headers hors allowlist ;
- requête SQL, document de repository, entité, UUID ou clé de cache ;
- token, cookie, secret, payload ou baggage arbitraire.
Les labels de métriques rejettent les clés sensibles/identifiantes, les UUID, les textes longs et les valeurs non scalaires. Utiliser des templates de routes, noms d'opérations et statuts bornés. Une capture de contenu explicite implique une revue sécurité, une rétention limitée et un budget de volume.
OpenTelemetry et LangSmith¶
Les deux adapters peuvent être activés ensemble :
Dans ce cas, LangSmith doit utiliser tracing.mode: otel. Le runtime OpenTelemetry possède l'arbre
de spans et LangSmith ajoute un processor au même TracerProvider. FastAPI, Pydantic AI, LangGraph,
MCP et RabbitMQ ne sont instrumentés qu'une fois. Le shutdown ferme d'abord le processor LangSmith,
puis les providers possédés par OpenTelemetry.
Pour envoyer uniquement OpenTelemetry vers LangSmith, configurer le Collector ou l'endpoint OTLP du tenant LangSmith et ne pas activer l'adapter LangSmith natif. Pour un mode hybride, conserver une seule politique de sampling et de capture.
API et diagnostics¶
| Python | |
|---|---|
Les diagnostics n'exposent jamais les headers OTLP ; ils retirent aussi credentials, query string et fragment de l'endpoint avant affichage.
L'escape hatch vendor-specific est explicite :
| Python | |
|---|---|
Ne faites pas remonter ces types dans le domaine ou les use cases.
Collector local¶
Le POC Jaeger du tutoriel Todo fournit le chemin le plus court pour démarrer un backend local, envoyer une requête FastAPI et retrouver sa trace dans l'UI. Le Collector autonome ci-dessous est utile pour inspecter les trois signaux OTLP dans le terminal.
Créer collector-config.yaml :
| YAML | |
|---|---|
Lancer le Collector :
| Bash | |
|---|---|
Puis démarrer le service avec le profil development et appeler une route, un tool ou un worker. Le terminal du Collector doit afficher les signaux sans corps, secrets ni contenus GenAI.
Le tag latest est pratique pour ce POC seulement. Épingler une version/digest dans tout runtime
partagé ou CI.
Dépannage¶
extra absent: installerarclith[opentelemetry]ou retirer l'adapter de l'activation.provider existe déjà: choisirattachsi Arclith doit ajouter traces/logs, ouexternalsi l'autre bootstrap possède toute la configuration.aucune trace: vérifierobservability.enabled,signals.traces.enabled,OTEL_SDK_DISABLED,OTEL_TRACES_EXPORTER, sampling et exclusions.aucune métrique en attach: configurer le metric reader sur le provider externe.spans dupliqués: retirer l'auto-instrumentation globale et conserver une seule instrumentation Arclith ; avec LangSmith, imposertracing.mode: otel.headers absents: vérifier l'allowlist et ne pas utiliser une query string comme label.Collector indisponible: les batch exporters journalisent une erreur bornée sans interrompre le métier ; vérifier endpoint, protocole, TLS, secret et timeout.
Références¶
- OpenTelemetry Python
- Exporters Python
- Variables d'environnement SDK
- Conventions de ressource
- Conventions de service
Lire aussi Observability, Observabilité production et Quickstart API.