MVAESTECH
Guide .NET · 14 · Production

Publié le · par Mathieu Vaes

Observabilité

En production, on ne débogue pas : on observe. Une appli bien instrumentée répond à « pourquoi cette requête a pris 4 secondes ? » sans redéploiement.

Chapitres du guide
En clair

Quand un client dit « c'est lent », il faut pouvoir répondre sans deviner. Les logs racontent ce qui s'est passé, les métriques mesurent (combien, à quelle vitesse), les traces suivent une requête d'un service à l'autre, comme un colis suivi à chaque étape de livraison.

Logs

Événements discrets, structurés, corrélés par TraceId. Pour comprendre un cas précis.

Métriques

Agrégats numériques : débit, latence p95/p99, taux d'erreur, GC, ThreadPool. Pour alerter.

Traces

Le parcours d'une requête entre services, découpé en spans. Pour localiser la lenteur.

OpenTelemetry, le standard Confirmé

.NET produit nativement des traces (ActivitySource) et des métriques (Meter) ; ASP.NET Core, HttpClient, EF Core et SqlClient sont déjà instrumentés. OpenTelemetry les collecte et les exporte en OTLP vers n'importe quel backend : Cloud Trace et Cloud Monitoring, Grafana (Tempo, Loki, Prometheus), Jaeger, Datadog…

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r => r.AddService("shop-api", serviceVersion: "1.8.2"))
    .WithTracing(t => t
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddSource("Shop.Orders")                 // vos propres ActivitySource
        .AddOtlpExporter())
    .WithMetrics(m => m
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddRuntimeInstrumentation()              // GC, ThreadPool, exceptions
        .AddMeter("Shop.Orders")
        .AddOtlpExporter());
builder.Logging.AddOpenTelemetry(l => { l.IncludeScopes = true; l.AddOtlpExporter(); });

// Span et métrique métier
private static readonly ActivitySource s_source = new("Shop.Orders");
private static readonly Counter<long> s_submitted =
    new Meter("Shop.Orders").CreateCounter<long>("orders.submitted");

using var activity = s_source.StartActivity("SubmitOrder");
activity?.SetTag("order.id", orderId);
s_submitted.Add(1, new KeyValuePair<string, object?>("channel", "web"));

Logs structurés Junior

// ❌ Interpolation : une chaîne unique par appel, propriétés perdues, allocation même si le niveau est filtré
logger.LogInformation($"Commande {orderId} validée en {ms} ms");

// ✅ Message template : OrderId et ElapsedMs deviennent des champs filtrables
logger.LogInformation("Commande {OrderId} validée en {ElapsedMs} ms", orderId, ms);

// ✅✅ Source generator : zéro allocation, vérifié à la compilation
public static partial class Log
{
    [LoggerMessage(Level = LogLevel.Information, Message = "Commande {OrderId} validée en {ElapsedMs} ms")]
    public static partial void OrderSubmitted(this ILogger logger, Guid orderId, long elapsedMs);
}

Ne loggez jamais de secrets ni de données personnelles en clair (mots de passe, jetons, numéros de carte, e-mails selon votre politique RGPD). Le package Microsoft.Extensions.Compliance.Redaction masque les champs classifiés.

Outils de diagnostic Senior

OutilUsage
dotnet-counters monitor -p <pid>Compteurs en direct : CPU, GC, taille de la file du ThreadPool, requêtes/s
dotnet-trace collectTrace de performance (CPU, allocations), à ouvrir dans PerfView ou Visual Studio
dotnet-dump collect / analyzeDump mémoire : objets retenus, piles de threads (interblocages)
dotnet-gcdumpInstantané léger du tas managé pour chasser une fuite
BenchmarkDotNetMicro-benchmarks fiables avant d'optimiser
k6, NBomberTests de charge de l'API complète

Alerter sur ce qui compte

Définissez des SLI (taux de requêtes réussies, latence p99) et des SLO (99,9 % de succès sur 30 jours). Alertez sur la consommation du budget d'erreur plutôt que sur un pic de CPU isolé : une alerte doit appeler une action.

Piège
  • Cardinalité explosive : un identifiant utilisateur en tag de métrique crée une série par utilisateur. Les identifiants vont dans les traces et logs, pas dans les métriques.
  • Échantillonnage absent : 100 % des traces à fort trafic coûte cher ; échantillonner (par exemple 10 %) en gardant toutes les erreurs.
  • Health check qui ment : un /health qui renvoie toujours 200 sans rien vérifier.

À retenir

  • OpenTelemetry pour les trois signaux, export OTLP, corrélation par TraceId.
  • Message templates ou [LoggerMessage], jamais d'interpolation dans les logs.
  • dotnet-counters d'abord, dotnet-trace ou dotnet-dump ensuite.

Un projet .NET à cadrer ou à reprendre ?

API REST documentées et testées, applications ASP.NET MVC (y compris VB.NET), migration de services anciens vers une architecture claire : parlons de votre code.