Theory — Application Insights Fundamentals
Theory — Application Insights Fundamentals
Lab 4.3 embedded the OpenTelemetry-based SDK (@azure/monitor-opentelemetry) into your application. This page covers what Azure Monitor actually does, the telemetry types it collects, and how to reason about the data it produces.
What is Application Insights?
Application Insights is part of Azure Monitor. It's an application performance monitoring (APM) service that instruments your app with a SDK, capturing application-level telemetry — every request, trace, and dependency your code generates.
Application Insights and Log Analytics share the same backend. When you enable Application Insights on an App Service, the SDK sends telemetry to an Application Insights resource, which then stores it in a Log Analytics Workspace. This means you query Application Insights telemetry using the same KQL queries you use for platform logs — just against different tables.
The telemetry model
The OpenTelemetry-based SDK categorises every piece of data it collects into telemetry types. Each type maps to a specific KQL table — you'll query them differently depending on what you're looking for.
Requests
Every HTTP request to your application generates an entry in the requests table. This is the primary telemetry for web applications.
| Field | Description | Example |
|---|---|---|
name | The HTTP endpoint that was called | "GET /api/users" |
url | The full request URL | "https://app.azurewebsites.net/api/users" |
success | Whether the request completed successfully | true or false |
duration_ms | How long the request took (in milliseconds) | 45.0 |
status_code | The HTTP status code | "200" or "500" |
operation_Id | Distributed trace identifier for the request | "abc123" |
Security angle: Failed requests (success == false), unusual status codes, and requests to unexpected endpoints are often the first signal of an attack. Start your investigation here — the requests table gives you the attack surface at a glance.
Dependencies
Every outgoing call from your application generates an entry in the dependencies table. If your app calls a database, an API, or a storage service, that call is captured as a dependency.
| Field | Description | Example |
|---|---|---|
name | The operation that was called | "SELECT * FROM users" or "GET /api/external" |
commandName | The type of call | "SqlServer", "HttpClient", "Blob" |
resultCode | The result of the call | "OK", "Timeout", "NotFound" |
success | Whether the dependency call succeeded | true or false |
duration_ms | How long the dependency call took (in milliseconds) | 123.0 |
operation_Id | Same trace ID as the parent request | "abc123" |
Security angle: Unexpected dependencies — your app calling a service it shouldn't — can indicate data exfiltration. Slow or repeatedly failing dependencies may signal compromised credentials or resource exhaustion from an active attack.
Traces
Traces are custom log messages that you write in your application code. They capture events, decisions, and state that the HTTP layer never sees.
logger.LogInformation("User {UserId} logged in from {IpAddress}", userId, ipAddress);
logger.LogWarning("High memory usage detected: {MemoryPercent}%", memoryPercent);
logger.LogError(ex, "Database connection failed");These become entries in the traces table.
| Field | Description | Example |
|---|---|---|
message | The log message content | "User 12345 logged in from 203.0.113.5" |
severityLevel | Log level | "Information", "Warning", "Error" |
operation_Id | Links this trace to its parent request | "abc123" |
Security-relevant events — authentication attempts, authorization decisions, input validation failures — live in traces. This is where you'll find the human-readable context that turns raw telemetry into an investigation.
Custom Events
Custom events capture discrete actions in your application. Unlike traces (which are log messages), events are structured records with named properties.
telemetryClient.TrackEvent("FileUploaded", new Dictionary<string, string> {
{ "userId", userId },
{ "fileName", fileName },
{ "fileSize", fileSize.ToString() },
{ "fileType", contentType }
});These become entries in the customEvents table.
Custom events capture business security events that don't fit neatly into HTTP requests or traces: account creation, password changes, permission modifications, and data access operations.
Telemetry types compared
| Telemetry Type | Source | Typical Use | Security Relevance |
|---|---|---|---|
| Requests | HTTP layer | Performance monitoring | Detect unexpected endpoints, failed auth attempts |
| Dependencies | Outgoing calls | Performance monitoring | Detect data exfiltration, compromised credentials |
| Traces | Application code | Debugging and logging | Log security events, decisions, and violations |
| Custom Events | Application code | Business tracking | Track security-relevant user actions |
The telemetry pipeline
When a user makes a request, the OpenTelemetry-based SDK captures multiple records:
- A Request record (in
requests) for the HTTP call - One or more Trace records (in
traces) for log messages written during processing - A Dependency record (in
dependencies) for each outgoing call (database, API, storage)
All records from the same request share the same operation_Id, which is how you reconstruct the full request flow in KQL queries. Here's what that looks like:
The operation_Id links all three records. A KQL query using where operation_Id == "abc123" returns the request, all traces, and all dependencies for that single call — giving you the full picture of what happened. For more on querying this data, see Lab 4.5 — KQL Hunting.
Performance anomalies as security signals
The OpenTelemetry-based SDK continuously tracks request rates, response times, and failure rates. While this is primarily a performance feature, anomalous patterns are often the first indicator of an attack:
| Metric | Normal pattern | Security signal |
|---|---|---|
| Request rate | Steady or expected peaks | Sudden spike → DDoS or bot activity |
| Response time | Consistent p95 under threshold | Sudden increase → resource exhaustion or crypto-mining |
| Failure rate | Low, predictable error rate | Sudden increase → application exploit or brute-force |
| Dependency rate | Expected number of outgoing calls | Unexpected increase → data exfiltration or lateral movement |
The Day 4 labs have you run the application under normal conditions to establish a baseline. Without this reference, you can't distinguish a performance spike from a security incident.
Application Insights vs. Diagnostic Settings
Application Insights and Diagnostic Settings cover different layers of the same system:
| OpenTelemetry-based SDK | Diagnostic Settings | |
|---|---|---|
| What it captures | Application-level telemetry (requests, traces, dependencies) | Platform-level telemetry (HTTP logs, audit logs, metrics) |
| How it works | SDK embedded in your code | Configured on the Azure resource itself |
| Code changes needed? | Yes — deploy with the SDK | No |
The OpenTelemetry-based SDK tells you what your application did; Diagnostic Settings tells you what Azure did with your application. Security investigations need both. For more on configuration, data volumes, and cost trade-offs, see the telemetry fundamentals overview.
Anti-patterns to avoid
1. Logging secrets in traces
// BAD — secrets appear in the SDK's traces
logger.LogInformation("Login attempt with password: {Password}", password);SDK data is accessible to developers and operations teams. Never log passwords, tokens, or other secrets.
2. Over-instrumentation
Every trackTrace call adds data to the SDK's output, which increases cost and makes queries slower. Only instrument events that you actually plan to query.
3. Ignoring the operation_Id
The operation_Id is Azure Monitor's distributed tracing ID — generated automatically by the SDK for every HTTP request. It links all telemetry records (AppRequests, AppTraces, AppDependencies) from a single request together. Don't confuse it with our code variable correlationId, which is the value of the x-correlation-id HTTP header we set and pass through the request chain. Both are useful for tracing, but they serve different purposes: use operation_Id to find all telemetry for a request, use correlationId to trace a specific client request across services.
4. Treating all failures the same
A 500 error from your own code is different from a 500 error from a dependency. In the exceptions table, use outerId to correlate exception records back to their parent request, and inspect the details column for the full stack trace. Always check type to distinguish application-level exceptions from dependency-level failures.
Key takeaways
The OpenTelemetry-based SDK captures four telemetry types: requests, dependencies, traces, and custom events. Each serves a different purpose. 2. The operation_Id links all records from a single request — use it to reconstruct the full request flow. The OpenTelemetry-based SDK and Diagnostic Settings are complementary — one shows what your code did, the other shows what Azure did. 4. Establishing a performance baseline is essential for detecting security anomalies. 5. Never log secrets in traces — SDK data is accessible to multiple teams.