Monday, 2 November 2026

How do you implement logging and monitoring using Application Insights?

Logging and Monitoring with Application Insights

Application Insights is the application performance monitoring (APM) feature of Azure Monitor. It collects requests, dependencies (SQL, HTTP, Service Bus), exceptions, traces, custom metrics, and custom events, and gives you tools to query, visualize, and alert on them.

Current Application Insights resources are workspace-based: the data is stored in a Log Analytics workspace, so you can query it alongside other Azure logs with KQL. Classic (non-workspace) resources are retired, so create new ones as workspace-based.

1. Create the resource

az monitor log-analytics workspace create \
  --resource-group my-rg --workspace-name my-logs --location centralindia

az monitor app-insights component create \
  --app my-app-insights --resource-group my-rg --location centralindia \
  --workspace my-logs --kind web

Get the connection string (use this, not the old instrumentation key alone):

az monitor app-insights component show --app my-app-insights \
  --resource-group my-rg --query connectionString -o tsv

2. Instrument a .NET app

There are two options. Microsoft's recommended path for new ASP.NET Core apps is the Azure Monitor OpenTelemetry Distro, which replaces the older Application Insights SDK as the main approach. The classic SDK still works and is fine for existing apps.

Option A: Azure Monitor OpenTelemetry (recommended)

dotnet add package Azure.Monitor.OpenTelemetry.AspNetCore
using Azure.Monitor.OpenTelemetry.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry().UseAzureMonitor();

var app = builder.Build();

Set the connection string via configuration (not in code):

APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...;IngestionEndpoint=https://...

This automatically collects incoming requests, outgoing HTTP calls, SQL calls, exceptions, ILogger output, and performance counters.

Option B: Classic Application Insights SDK

dotnet add package Microsoft.ApplicationInsights.AspNetCore
builder.Services.AddApplicationInsightsTelemetry();

Again, provide APPLICATIONINSIGHTS_CONNECTION_STRING through app settings or environment variables.

Azure Functions (isolated worker)

dotnet add package Microsoft.Azure.Functions.Worker.ApplicationInsights
var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureServices(services =>
    {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
    })
    .Build();

Note that the isolated worker applies a default logging filter that can hide Information logs from your code. If your logs are missing, remove it in ConfigureLogging.

App Service with no code changes

For App Service, you can enable Application Insights under the app's Monitoring blade (auto-instrumentation, "codeless"). It's the quickest start, but code-based instrumentation gives you more control for custom telemetry.

3. Logging with ILogger

Use the standard .NET ILogger<T>. Application Insights captures it automatically, and your structured properties become searchable custom dimensions.

public class OrdersController : ControllerBase
{
    private readonly ILogger<OrdersController> _logger;

    public OrdersController(ILogger<OrdersController> logger) => _logger = logger;

    [HttpPost]
    public IActionResult Create(Order order)
    {
        _logger.LogInformation("Creating order {OrderId} for customer {CustomerId}",
            order.Id, order.CustomerId);
        try
        {
            // ...
            return Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to create order {OrderId}", order.Id);
            throw;
        }
    }
}

Use message templates with named placeholders, not string interpolation. Templates keep the properties as structured fields you can filter on.

Set log levels in appsettings.json. Note the provider-specific section so you control what is sent to Application Insights:

{
  "Logging": {
    "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" },
    "ApplicationInsights": { "LogLevel": { "Default": "Information" } }
  }
}

4. Custom telemetry

Events and metrics (with the OpenTelemetry distro, use System.Diagnostics.Metrics; with the classic SDK, use TelemetryClient):

// Custom metric with OpenTelemetry / .NET Meter
static readonly Meter Meter = new("MyApp.Orders");
static readonly Counter<long> OrdersCreated = Meter.CreateCounter<long>("orders_created");

OrdersCreated.Add(1, new KeyValuePair<string, object?>("region", "EU"));

Register the meter so it's exported:

builder.Services.AddOpenTelemetry()
    .UseAzureMonitor()
    .WithMetrics(m => m.AddMeter("MyApp.Orders"));
// Classic SDK
_telemetryClient.TrackEvent("OrderPlaced",
    new Dictionary<string, string> { ["region"] = "EU" });
_telemetryClient.GetMetric("OrderValue").TrackValue(order.Total);

Custom spans (distributed tracing) with ActivitySource:

static readonly ActivitySource Source = new("MyApp.Orders");

using var activity = Source.StartActivity("ProcessPayment");
activity?.SetTag("order.id", order.Id);

Register it with .WithTracing(t => t.AddSource("MyApp.Orders")).

5. Distributed tracing across services

Application Insights correlates calls across services using the W3C traceparent header. Requests, dependencies, logs, and exceptions that belong to one operation share an operation ID, so you can follow a single user action across APIs, queues, and functions. Use Application Map and the End-to-end transaction details view to see this visually. Service Bus and HTTP calls propagate context automatically with the standard Azure SDKs.

Set a meaningful cloud role name so each service appears as its own node on the map:

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r => r.AddService("orders-api"))
    .UseAzureMonitor();

6. Querying with KQL

In Application Insights → Logs (or the Log Analytics workspace):

// Failed requests in the last hour
requests
| where timestamp > ago(1h) and success == false
| summarize count() by resultCode, name
| order by count_ catch desc

Correction: catch isn't valid KQL there. The correct form is:

requests
| where timestamp > ago(1h) and success == false
| summarize failures = count() by resultCode, name
| order by failures desc
// Slowest operations (p95)
requests
| where timestamp > ago(24h)
| summarize p95 = percentile(duration, 95) by name
| order by p95 desc

// Exceptions with context
exceptions
| where timestamp > ago(24h)
| project timestamp, type, outerMessage, operation_Id, cloud_RoleName

// All telemetry for one operation
union requests, dependencies, traces, exceptions
| where operation_Id == "<id>"
| order by timestamp asc

// Slow or failing dependencies (SQL, HTTP, Service Bus)
dependencies
| where timestamp > ago(24h) and success == false
| summarize count() by target, type

Note: in a workspace-based resource queried from the Log Analytics workspace, the table names are AppRequests, AppDependencies, AppExceptions, AppTraces, and so on, with different column names (TimeGenerated, DurationMs). Queries from the Application Insights Logs blade use the classic names shown above.

7. Monitoring features worth using

  • Live Metrics: near-real-time view of requests, failures, and CPU while you deploy or debug.
  • Application Map: dependency graph with failure and latency per link.
  • Failures and Performance blades: group exceptions and slow operations, then drill into samples.
  • Availability tests: synthetic checks from multiple regions (URL ping is being replaced by Standard tests; use those).
  • Smart detection: automatic anomaly alerts for failure-rate and latency changes.
  • Workbooks and dashboards: shared visual reports built from KQL.
  • Profiler and Snapshot Debugger: deeper diagnostics for slow code and exceptions (primarily classic SDK and App Service scenarios).

8. Alerts

Create alert rules on metrics or on KQL queries, and route them to an action group (email, SMS, Teams, webhook, Logic App).

az monitor metrics alert create \
  --name high-failed-requests --resource-group my-rg \
  --scopes <app-insights-resource-id> \
  --condition "count requests/failed > 10" \
  --window-size 5m --evaluation-frequency 1m \
  --action <action-group-id>

Typical alerts: failed request spike, p95 latency above a threshold, exceptions rate, availability test failures, and dependency failures. Prefer log-based alerts for business conditions (e.g., no orders in 30 minutes).

9. Cost and data control

Application Insights is billed by data ingested (GB) to the workspace, so control volume deliberately:

  • Sampling: adaptive sampling is on by default in the classic SDK. With OpenTelemetry, set SamplingRatio (or use rate-limited sampling) in AzureMonitorOptions. Sampled telemetry keeps correlated items together.
  • Log levels: don't send Debug or Trace in production, and raise noisy framework categories to Warning.
  • Daily cap: set a cap on the workspace as a safety net, knowing that data stops being collected when it's hit.
  • Retention: default is 90 days for Application Insights tables (30 days for Log Analytics generally); extend or archive only what you need.
  • Filter noise: drop health-check requests and static files with a processor or filter.

10. Security and good practice

  • Never log secrets or PII (tokens, passwords, card numbers, full emails). Sanitize or omit them before logging.
  • Use Entra authentication for ingestion (DefaultAzureCredential with the Monitoring Metrics Publisher role) and disable local (key-based) auth, so a leaked connection string can't be used to inject fake telemetry.
  • Use RBAC to control who can read logs (Log Analytics Reader, Monitoring Reader).
  • Use private link (Azure Monitor Private Link Scope) if telemetry must stay off the public internet.
  • Use consistent correlation: include IDs such as OrderId in log templates so you can search by business key.
  • One resource per environment (or at least distinct cloud role names and an environment dimension) so dev noise doesn't pollute production.

Enabling Entra ingestion with the OpenTelemetry distro:

builder.Services.AddOpenTelemetry().UseAzureMonitor(o =>
{
    o.Credential = new DefaultAzureCredential();
});

Minimal setup checklist

  1. Create a workspace-based Application Insights resource.
  2. Add Azure.Monitor.OpenTelemetry.AspNetCore and call UseAzureMonitor(), with the connection string in app settings.
  3. Log through ILogger with structured templates.
  4. Set the service name so Application Map shows each service.
  5. Add alerts for failures, latency, and availability.
  6. Configure sampling, log levels, and a daily cap to control cost.


No comments:

Post a Comment

Note: only a member of this blog may post a comment.