Technology

C# Entity Framework Observability Stack

c entity framework observability stack
C# Entity Framework Observability Stack | SiamCafe Blog
2025-12-08· อ. บอม — SiamCafe.net· 8,165 คำ

EF Core Observability

C# Entity Framework Core Observability OpenTelemetry Distributed Tracing Metrics Logging Serilog Prometheus Grafana Jaeger .NET ASP.NET Core SQL Performance Monitoring

SignalToolBackendDashboardเหมาะกับ
LoggingSerilogSeq / ElasticsearchKibana / Seq UIDebug + Audit
TracingOpenTelemetryJaeger / TempoGrafanaDistributed
MetricsPrometheusPrometheusGrafanaPerformance
HealthHealthChecksBuilt-inGrafanaUptime

OpenTelemetry Setup

# === .NET OpenTelemetry Configuration ===

# NuGet Packages:
# dotnet add package OpenTelemetry.Extensions.Hosting
# dotnet add package OpenTelemetry.Instrumentation.AspNetCore
# dotnet add package OpenTelemetry.Instrumentation.SqlClient
# dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore
# dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
# dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore

# Program.cs
# using OpenTelemetry.Metrics;
# using OpenTelemetry.Resources;
# using OpenTelemetry.Trace;
#
# var builder = WebApplication.CreateBuilder(args);
#
# builder.Services.AddOpenTelemetry()
#     .ConfigureResource(r => r
#         .AddService("MyApp", serviceVersion: "1.0.0"))
#     .WithTracing(tracing => tracing
#         .AddAspNetCoreInstrumentation()
#         .AddHttpClientInstrumentation()
#         .AddSqlClientInstrumentation(opt =>
#         {
#             opt.SetDbStatementForText = true;
#             opt.RecordException = true;
#         })
#         .AddEntityFrameworkCoreInstrumentation()
#         .AddOtlpExporter(opt =>
#         {
#             opt.Endpoint = new Uri("http://jaeger:4317");
#         }))
#     .WithMetrics(metrics => metrics
#         .AddAspNetCoreInstrumentation()
#         .AddHttpClientInstrumentation()
#         .AddRuntimeInstrumentation()
#         .AddPrometheusExporter());
#
# // DbContext
# builder.Services.AddDbContext<AppDbContext>(options =>
#     options.UseSqlServer(connectionString)
#         .EnableSensitiveDataLogging()
#         .LogTo(Console.WriteLine, LogLevel.Information));
#
# var app = builder.Build();
# app.MapPrometheusScrapingEndpoint();

from dataclasses import dataclass

@dataclass
class TelemetrySignal:
    signal: str
    source: str
    exporter: str
    data_points: str
    retention: str

signals = [
    TelemetrySignal("Traces", "ASP.NET + EF Core + SQL", "OTLP -> Jaeger", "Spans per request", "7 days"),
    TelemetrySignal("Metrics", "Runtime + HTTP + Custom", "Prometheus", "Counters, Histograms", "30 days"),
    TelemetrySignal("Logs", "Serilog + EF Core", "Seq / Elasticsearch", "Structured JSON", "30 days"),
    TelemetrySignal("Health", "DB + Redis + External", "HTTP endpoint", "Up/Down/Degraded", "Real-time"),
]

print("=== Telemetry Signals ===")
for s in signals:
    print(f"  [{s.signal}] {s.source}")
    print(f"    Exporter: {s.exporter} | Data: {s.data_points} | Retention: {s.retention}")

EF Core Performance

# === EF Core Performance Monitoring ===

# Slow Query Interceptor
# public class SlowQueryInterceptor : DbCommandInterceptor
# {
#     private readonly ILogger _logger;
#     private readonly TimeSpan _threshold = TimeSpan.FromMilliseconds(100);
#
#     public override DbDataReader ReaderExecuted(
#         DbCommand command,
#         CommandExecutedEventData eventData,
#         DbDataReader result)
#     {
#         if (eventData.Duration > _threshold)
#         {
#             _logger.LogWarning(
#                 "Slow query ({Duration}ms): {Query}",
#                 eventData.Duration.TotalMilliseconds,
#                 command.CommandText);
#         }
#         return result;
#     }
# }
#
# // Register in DbContext
# options.AddInterceptors(new SlowQueryInterceptor(logger));

# MiniProfiler — In-page Query Profiling
# dotnet add package MiniProfiler.EntityFrameworkCore
# builder.Services.AddMiniProfiler(options =>
# {
#     options.RouteBasePath = "/profiler";
#     options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
# }).AddEntityFramework();

# dotnet-counters — Real-time Metrics
# dotnet-counters monitor \
#   --counters Microsoft.EntityFrameworkCore \
#   --process-id 
#
# Metrics:
# - ec_Microsoft.EntityFrameworkCore|active-db-contexts
# - ec_Microsoft.EntityFrameworkCore|total-queries
# - ec_Microsoft.EntityFrameworkCore|total-save-changes
# - ec_Microsoft.EntityFrameworkCore|compiled-query-cache-hit-rate
# - ec_Microsoft.EntityFrameworkCore|total-execution-strategy-operation-failures

@dataclass
class QueryMetric:
    query_type: str
    count_24h: int
    avg_ms: float
    p99_ms: float
    slow_count: int
    cache_hit_pct: float

metrics = [
    QueryMetric("SELECT (Read)", 45000, 12.5, 85, 23, 95.2),
    QueryMetric("INSERT (Create)", 8000, 8.3, 45, 5, 0),
    QueryMetric("UPDATE (Modify)", 5000, 15.2, 120, 12, 0),
    QueryMetric("DELETE (Remove)", 1200, 6.1, 30, 1, 0),
    QueryMetric("JOIN (Complex)", 3500, 45.8, 250, 45, 88.5),
    QueryMetric("Stored Proc", 2000, 22.3, 150, 8, 0),
]

print("\n=== EF Core Query Metrics (24h) ===")
total_queries = sum(m.count_24h for m in metrics)
total_slow = sum(m.slow_count for m in metrics)
for m in metrics:
    print(f"  [{m.query_type}] Count: {m.count_24h:,}")
    print(f"    Avg: {m.avg_ms}ms | p99: {m.p99_ms}ms | Slow: {m.slow_count}")
print(f"\n  Total: {total_queries:,} queries | Slow: {total_slow}")

Grafana Dashboard

# === Production Dashboard ===

# Docker Compose — Observability Stack
# services:
#   jaeger:
#     image: jaegertracing/all-in-one:latest
#     ports:
#       - "16686:16686"  # Jaeger UI
#       - "4317:4317"    # OTLP gRPC
#
#   prometheus:
#     image: prom/prometheus:latest
#     volumes:
#       - ./prometheus.yml:/etc/prometheus/prometheus.yml
#     ports:
#       - "9090:9090"
#
#   grafana:
#     image: grafana/grafana:latest
#     ports:
#       - "3000:3000"
#     environment:
#       - GF_SECURITY_ADMIN_PASSWORD=admin
#
#   seq:
#     image: datalust/seq:latest
#     ports:
#       - "5341:5341"  # Ingestion
#       - "8080:80"    # UI
#     environment:
#       - ACCEPT_EULA=Y

# prometheus.yml
# scrape_configs:
#   - job_name: 'dotnet-app'
#     scrape_interval: 15s
#     static_configs:
#       - targets: ['app:8080']

# Grafana Dashboard Panels:
# 1. Request Rate (req/s)
# 2. Response Time (p50, p95, p99)
# 3. Error Rate (5xx %)
# 4. EF Core Query Count
# 5. Slow Query Count
# 6. DB Connection Pool
# 7. GC Collections
# 8. Thread Pool Queue Length

dashboard_panels = {
    "HTTP Request Rate": "125 req/s",
    "Response Time p99": "180ms",
    "Error Rate": "0.05%",
    "EF Queries/sec": "85",
    "Slow Queries (>100ms)": "12/hour",
    "DB Connections (pool)": "18/100",
    "GC Gen2 Collections": "3/min",
    "Memory Usage": "450 MB",
    "CPU Usage": "35%",
    "Active Traces": "1,250",
}

print("Grafana Dashboard:")
for panel, value in dashboard_panels.items():
    print(f"  {panel}: {value}")

alerts = [
    "p99 Latency > 500ms -> Warning (Slack)",
    "Error Rate > 1% -> Critical (PagerDuty)",
    "Slow Queries > 50/hour -> Warning (Slack)",
    "DB Pool Exhaustion > 80% -> Critical (PagerDuty)",
    "Memory > 1GB -> Warning (Slack)",
    "Health Check Failed -> Critical (PagerDuty + SMS)",
]

print(f"\n\nAlert Rules:")
for i, a in enumerate(alerts, 1):
    print(f"  {i}. {a}")

เคล็ดลับ

Entity Framework Observability คืออะไร

ตรวจสอบ EF Core SQL Query Performance Slow Query Connection Pool OpenTelemetry Tracing Prometheus Metrics Grafana Dashboard Seq Elasticsearch Log

OpenTelemetry กับ .NET ใช้อย่างไร

NuGet Package AddOpenTelemetry WithTracing WithMetrics ASP.NET SQL EF Core OTLP Jaeger Prometheus Auto-instrument Trace Span

EF Core Performance Monitoring ทำอย่างไร

Logging OnConfiguring LogTo MiniProfiler Slow Query Interceptor 100ms Threshold dotnet-counters Query Compilation Cache Hit Rate

สร้าง Observability Stack อย่างไร

Serilog Seq Elasticsearch OpenTelemetry Jaeger Tempo Prometheus Grafana Health Check DB Redis Alert PagerDuty Docker Compose

สรุป

C# Entity Framework Observability OpenTelemetry Distributed Tracing Prometheus Grafana Serilog Seq Jaeger Slow Query Interceptor MiniProfiler Health Check .NET Production Monitoring

📖 บทความที่เกี่ยวข้อง

C# Entity Framework CQRS Event Sourcingอ่านบทความ → C# Entity Framework Production Setup Guideอ่านบทความ → C# Entity Framework Stream Processingอ่านบทความ → C# Entity Framework Clean Architectureอ่านบทความ → C# Entity Framework API Gateway Patternอ่านบทความ →

📚 ดูบทความทั้งหมด →