Tổng quan
Observability là nền tảng của Operational Excellence. Không thể fix những gì không nhìn thấy. Azure Monitor ecosystem gồm Application Insights, Log Analytics, Metrics, và Alerts - tạo thành full-stack monitoring cho .NET applications.
Application Insights Setup cho .NET
Step 1: Cài đặt NuGet packages
bash
dotnet add package Microsoft.ApplicationInsights.AspNetCore
dotnet add package Microsoft.ApplicationInsights.Kubernetes
Step 2: Cấu hình trong Program.cs
csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
options.EnableAdaptiveSampling = true;
});
builder.Services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
var app = builder.Build();
csharp
public class CustomTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = "OrderApi";
telemetry.Context.GlobalProperties["deployment-slot"] = "production";
}
}
Step 3: appsettings.json
json
{
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=xxx;IngestionEndpoint=https://southeastasia-1.in.applicationinsights.azure.com/"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}
Custom Events & Metrics
csharp
public class OrderService
{
private readonly TelemetryClient _telemetry;
public OrderService(TelemetryClient telemetry)
{
_telemetry = telemetry;
}
public async Task<Order> CreateOrder(CreateOrderRequest request)
{
var stopwatch = Stopwatch.StartNew();
try
{
var order = await ProcessOrder(request);
_telemetry.TrackEvent("OrderCreated", new Dictionary<string, string>
{
["OrderId"] = order.Id.ToString(),
["PaymentMethod"] = request.PaymentMethod,
["ItemCount"] = request.Items.Count.ToString()
}, new Dictionary<string, double>
{
["OrderTotal"] = (double)order.Total,
["ProcessingTimeMs"] = stopwatch.ElapsedMilliseconds
});
_telemetry.GetMetric("OrderProcessingTime").TrackValue(stopwatch.ElapsedMilliseconds);
return order;
}
catch (Exception ex)
{
_telemetry.TrackException(ex, new Dictionary<string, string>
{
["Operation"] = "CreateOrder",
["CustomerId"] = request.CustomerId.ToString()
});
throw;
}
}
}
Log Analytics Workspace Design
bash
az monitor log-analytics workspace create \
--resource-group rg-monitoring \
--workspace-name law-central-prod \
--location southeastasia \
--retention-time 90 \
--sku PerGB2018
az monitor app-insights component create \
--app appi-orderapi-prod \
--location southeastasia \
--resource-group rg-monitoring \
--workspace law-central-prod
KQL Queries - Common Scenarios
1. Slow Requests (P95 > 2s)
kql
requests
| where timestamp > ago(24h)
| where success == true
| summarize
P50 = percentile(duration, 50),
P95 = percentile(duration, 95),
P99 = percentile(duration, 99),
Count = count()
by bin(timestamp, 1h), name
| where P95 > 2000
| order by P95 desc
2. Error Rate by Endpoint
kql
requests
| where timestamp > ago(1h)
| summarize
TotalRequests = count(),
FailedRequests = countif(success == false),
ErrorRate = round(100.0 * countif(success == false) / count(), 2)
by name
| where ErrorRate > 1
| order by ErrorRate desc
3. Exception Hotspots
kql
exceptions
| where timestamp > ago(24h)
| summarize Count = count() by type, outerMessage, problemId
| order by Count desc
| take 20
4. Dependency Failures (SQL, Redis, HTTP calls)
kql
dependencies
| where timestamp > ago(1h)
| where success == false
| summarize
FailCount = count(),
AvgDuration = avg(duration)
by target, type, resultCode
| order by FailCount desc
5. Custom Business Metrics
kql
customEvents
| where name == "OrderCreated"
| where timestamp > ago(24h)
| extend
OrderTotal = todouble(customMeasurements.OrderTotal),
ProcessingTime = todouble(customMeasurements.ProcessingTimeMs),
PaymentMethod = tostring(customDimensions.PaymentMethod)
| summarize
OrderCount = count(),
TotalRevenue = sum(OrderTotal),
AvgProcessingTime = avg(ProcessingTime)
by bin(timestamp, 1h), PaymentMethod
Metric Alerts vs Log Alerts
| Tiêu chí | Metric Alert | Log Alert (KQL-based) |
|---|
| Evaluation frequency | 1 phút (min) | 5 phút (min) |
| Data source | Azure Metrics | Log Analytics / App Insights |
| Complexity | Đơn giản (threshold) | Phức tạp (KQL query) |
| Cost | Rẻ hơn | Tốn hơn (query cost) |
| Stateful | Có (auto-resolve) | Có (configurable) |
| Phù hợp | CPU, memory, response time | Custom patterns, correlations |
Azure Monitor Alert Rules - Playbook
Metric Alert - High Response Time
bash
az monitor metrics alert create \
--resource-group rg-monitoring \
--name "alert-high-response-time" \
--scopes /subscriptions/{sub}/resourceGroups/rg-app/providers/Microsoft.Web/sites/app-orderapi \
--condition "avg HttpResponseTime > 3" \
--window-size 5m \
--evaluation-frequency 1m \
--severity 2 \
--action-group ag-platform-team \
--description "Response time exceeds 3s threshold"
Log Alert - Error Spike
bash
az monitor scheduled-query create \
--resource-group rg-monitoring \
--name "alert-error-spike" \
--scopes /subscriptions/{sub}/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/law-central-prod \
--condition "count 'ErrorRate' > 5" \
--condition-query "requests | where timestamp > ago(5m) | summarize ErrorRate = round(100.0 * countif(success == false) / count(), 2) | where ErrorRate > 5" \
--severity 1 \
--evaluation-frequency 5m \
--window-size 5m \
--action-groups ag-platform-team
Action Groups & Notification Channels
bash
az monitor action-group create \
--resource-group rg-monitoring \
--name ag-platform-team \
--short-name PlatformTeam \
--email-receiver name=TeamLead email=lead@company.com \
--email-receiver name=OnCall email=oncall@company.com \
--webhook-receiver name=Slack uri="https://hooks.slack.com/services/xxx" \
--webhook-receiver name=PagerDuty uri="https://events.pagerduty.com/integration/xxx/enqueue"
Alert Severity Guide
| Severity | Mô tả | Response Time | Action |
|---|
| Sev 0 (Critical) | Service down | < 15 phút | Page on-call, war room |
| Sev 1 (Error) | Significant degradation | < 30 phút | Notify team, investigate |
| Sev 2 (Warning) | Performance degradation | < 2 giờ | Review next business day |
| Sev 3 (Info) | Anomaly detected | Next review cycle | Track trend |
Distributed Tracing với Correlation IDs
csharp
public class CorrelationIdMiddleware
{
private readonly RequestDelegate _next;
public CorrelationIdMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context)
{
var correlationId = context.Request.Headers["X-Correlation-ID"]
.FirstOrDefault() ?? Activity.Current?.Id ?? Guid.NewGuid().ToString();
context.Items["CorrelationId"] = correlationId;
context.Response.Headers["X-Correlation-ID"] = correlationId;
var telemetry = context.Features.Get<RequestTelemetry>();
if (telemetry != null)
{
telemetry.Properties["CorrelationId"] = correlationId;
}
using (LogContext.PushProperty("CorrelationId", correlationId))
{
await _next(context);
}
}
}
Tracing across services - KQL query
kql
// Trace toàn bộ request flow bằng Operation ID
union requests, dependencies, exceptions
| where operation_Id == "abc123xyz"
| project
timestamp,
itemType,
name,
duration,
success,
resultCode,
target,
type
| order by timestamp asc
Dashboard Creation - Bicep
bicep
resource dashboard 'Microsoft.Portal/dashboards@2022-12-01-preview' = {
name: 'dash-app-overview'
location: location
tags: {
'hidden-title': 'Application Overview Dashboard'
}
properties: {
lenses: [
{
order: 0
parts: [
{
position: { x: 0, y: 0, colSpan: 6, rowSpan: 4 }
metadata: {
type: 'Extension/Microsoft_OperationsManagementSuite_Workspace/PartType/LogsDashboardPart'
inputs: [
{ name: 'query', value: 'requests | where timestamp > ago(24h) | summarize count() by bin(timestamp, 1h), success' }
]
}
}
]
}
]
}
}
Monitoring Checklist cho Production .NET App
| # | Item | Tool | Priority |
|---|
| 1 | Application Insights connected | App Insights | P0 |
| 2 | Health endpoint (/healthz) configured | App Insights availability test | P0 |
| 3 | Error rate alert (> 1%) | Metric Alert | P0 |
| 4 | Response time alert (P95 > 3s) | Metric Alert | P0 |
| 5 | CPU/Memory alert (> 80%) | Metric Alert | P1 |
| 6 | Dependency failure alerts | Log Alert | P1 |
| 7 | Custom business metrics tracking | Custom Events | P1 |
| 8 | Distributed tracing enabled | Correlation ID | P1 |
| 9 | Log Analytics retention configured | Log Analytics | P2 |
| 10 | Dashboard cho team | Azure Dashboard / Grafana | P2 |
| 11 | Runbook cho common alerts | Automation | P2 |
| 12 | Cost alert cho monitoring spend | Budget | P3 |
Trade-off Analysis
| Quyết định | Option A | Option B |
|---|
| Sampling On vs Off | Giảm cost, miss rare errors | Full data, chi phí cao |
| Workspace per app vs Central | Isolation, quản lý riêng | Cross-app correlation, single pane |
| Metric vs Log alerts | Nhanh (1m), đơn giản | Linh hoạt (KQL), chậm hơn (5m) |
| Grafana vs Azure Dashboards | Powerful, cần maintain | Tích hợp native, ít flexible |
| 30 vs 90 vs 365 day retention | Rẻ, ít data | Đắt, audit compliance |
Nguyên tắc: Bắt đầu với Application Insights + 4 basic alerts (error rate, response time, availability, CPU). Mở rộng monitoring khi hiểu rõ hơn patterns của application.