This chapter dives deep into custom telemetry with the Application Insights SDK, a critical skill for the AZ-204 exam. You'll learn how to instrument applications to send custom events, metrics, and traces beyond what auto-collection provides. Approximately 10-15% of exam questions touch on telemetry customization, sampling, and SDK configuration, making this a high-yield topic. Mastery of the TelemetryClient, telemetry initializers, processors, and channel configuration is essential for passing the 'Monitor' objective.
Jump to a section
Imagine an airplane's flight data recorder (black box) that logs not just the standard parameters like altitude, speed, and heading, but also custom events such as engine vibration spikes, pilot control inputs, and cabin pressure fluctuations. The recorder is installed by an SDK (the data collection module) that hooks into the aircraft's systems. When a custom event occurs—say, a sudden drop in oil pressure—the SDK immediately writes a timestamped entry with the event name and any additional measurements (e.g., pressure value, temperature) into a local buffer. The buffer periodically flushes its contents to a central ground station (Application Insights) via a secure radio link, using a compression algorithm to minimize bandwidth. On the ground, analysts can query these custom events to correlate failures with specific conditions, set alerts for abnormal patterns, and create dashboards for real-time monitoring. Without the custom telemetry, they would only see generic flight data and miss the root cause of the problem. Similarly, Application Insights SDK allows developers to instrument their code with custom events and metrics, capturing business-specific data that standard auto-collected telemetry misses. The SDK handles buffering, retry logic, and sampling to ensure reliable delivery without overwhelming the application.
What is Custom Telemetry and Why Does It Exist?
Application Insights automatically collects a wealth of telemetry: requests, exceptions, dependencies, page views, and performance counters. However, auto-collection cannot capture business-specific events or custom metrics that are unique to your application. For example, an e-commerce app might need to track 'CheckoutCompleted' or 'ItemAddedToCart' events with custom properties like product ID and price. This is where custom telemetry comes in. By using the Application Insights SDK, you can programmatically send: - Custom Events: Named occurrences (e.g., 'UserLoggedIn', 'PaymentFailed') with optional metrics and properties. - Custom Metrics: Numeric values that can be aggregated (e.g., queue length, processing time). - Traces: Diagnostic log messages with severity levels. - Page Views: Custom page view tracking for non-web contexts.
How It Works Internally
The Application Insights SDK provides a TelemetryClient object that acts as the primary interface for sending custom telemetry. When you call a method like TrackEvent(), the SDK creates a telemetry item (e.g., EventTelemetry) that includes:
- Timestamp: Automatically set to UTC now.
- Sequence number: For ordering.
- Custom properties: A dictionary of string key-value pairs.
- Custom measurements: A dictionary of string-double pairs for numeric data.
- IKey: The instrumentation key (or connection string) identifying the Application Insights resource.
The telemetry item is then passed through a telemetry processor chain (if configured) where you can modify or filter items. After processing, it enters the telemetry channel, which buffers items and sends them to the ingestion endpoint. The channel uses a default buffer size of 500 items and a flush interval of 30 seconds (configurable). If the buffer overflows, items are dropped (by default) or you can configure a custom overflow handler.
Key Components, Values, Defaults, and Timers
TelemetryClient: The main object. Must be created once per application (singleton) to avoid resource issues. Example:
var telemetryClient = new TelemetryClient(new TelemetryConfiguration(connectionString));Connection String: Preferred over instrumentation key. Format: InstrumentationKey=...;IngestionEndpoint=.... Default ingestion endpoint is https://dc.services.visualstudio.com.
TrackEvent: Sends a custom event. Example:
telemetryClient.TrackEvent("OrderPlaced", new Dictionary<string, string> { {"Currency", "USD"} }, new Dictionary<string, double> { {"Revenue", 99.95} });TrackMetric: Sends a custom metric. Note: Pre-aggregate metrics to avoid sending too many data points. Use GetMetric for pre-aggregation.
TrackTrace: Sends log traces. Example:
telemetryClient.TrackTrace("Processing started", SeverityLevel.Information);TelemetryProcessor: Implements ITelemetryProcessor. Use to filter, modify, or enrich telemetry. Example:
public class MyTelemetryProcessor : ITelemetryProcessor
{
private ITelemetryProcessor Next { get; set; }
public MyTelemetryProcessor(ITelemetryProcessor next) => Next = next;
public void Process(ITelemetry item)
{
if (item is EventTelemetry eventTelemetry && eventTelemetry.Name == "SensitiveEvent")
return; // Drop the event
Next.Process(item);
}
}TelemetryInitializer: Adds properties to all telemetry. Implements ITelemetryInitializer. Example:
public class MyTelemetryInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = "MyRole";
telemetry.Context.GlobalProperties["Environment"] = "Production";
}
}Sampling: Adaptive or fixed-rate sampling can reduce volume. Default adaptive sampling target is 5 items per second. Fixed-rate sampling uses a percentage (e.g., 10%).
Channel: InMemoryChannel is default for .NET. It has a buffer size (default 500) and send interval (default 30s). You can configure:
var channel = new InMemoryChannel { MaxTelemetryBufferCapacity = 1000, SendingInterval = TimeSpan.FromSeconds(10) };Configuration and Verification Commands
To configure the SDK in an ASP.NET Core app, add the following to ConfigureServices:
services.AddApplicationInsightsTelemetry(connectionString);For custom telemetry, inject TelemetryClient:
public class MyService
{
private readonly TelemetryClient _telemetryClient;
public MyService(TelemetryClient telemetryClient) => _telemetryClient = telemetryClient;
}To verify telemetry is flowing, use Application Insights Search in the Azure portal or the Live Metrics blade for real-time validation. You can also use Diagnostic Search to query for custom events.
Interaction with Related Technologies
Azure Monitor Metrics: Custom metrics sent via TrackMetric appear in Azure Monitor Metrics, but with limitations (e.g., 10 custom metrics per resource). Use pre-aggregation for better performance.
Log Analytics: Custom events and traces are stored in customEvents and traces tables, queryable via Kusto.
Azure Functions: App Insights integration is automatic; use TelemetryClient in function code.
OpenTelemetry: Application Insights can receive telemetry via OpenTelemetry exporter. Custom events can be sent using OpenTelemetry SDK.
Advanced: Telemetry Channel and Reliability
The channel handles retries with exponential backoff (up to 10 retries). If the endpoint is unreachable, items are stored in a local buffer (default 1 MB) and sent when connectivity resumes. You can implement a custom ITelemetryChannel for persistent storage (e.g., to disk) to prevent data loss during extended outages.
Code Example: Full Custom Telemetry Flow
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.DataContracts;
var config = new TelemetryConfiguration("InstrumentationKey=...;IngestionEndpoint=https://...");
var client = new TelemetryClient(config);
// Add a telemetry initializer
config.TelemetryInitializers.Add(new MyTelemetryInitializer());
// Add a telemetry processor
var builder = config.TelemetryProcessorChainBuilder;
builder.Use((next) => new MyTelemetryProcessor(next));
builder.Build();
// Track a custom event
client.TrackEvent("VideoStarted", new Dictionary<string, string> { {"VideoId", "123"} });
// Track a custom metric
client.GetMetric("VideoDuration").TrackValue(120.5);
// Flush before shutdown
client.Flush();
Task.Delay(5000).Wait(); // Allow time for flushInitialize TelemetryClient Singleton
Create a single instance of `TelemetryClient` using the Application Insights connection string. This instance should be reused across the application to avoid excessive resource consumption and to ensure proper batching. Use dependency injection in ASP.NET Core to register it as a singleton. The `TelemetryConfiguration` object can be configured with initializers and processors before creating the client. If you create multiple clients, they may share the same channel, but it's best practice to have one.
Add Telemetry Initializers
Implement `ITelemetryInitializer` to automatically add properties to every telemetry item, such as role name, environment, or user ID. The initializer's `Initialize` method is called for each telemetry item before processing. This ensures consistent context across all custom telemetry. For example, set `telemetry.Context.Cloud.RoleName` to the service name. Initializers are added to `TelemetryConfiguration.TelemetryInitializers`.
Add Telemetry Processors
Implement `ITelemetryProcessor` to filter or modify telemetry items. Processors form a chain; each processor passes the item to the next. Use for sampling, dropping sensitive events, or adding derived properties. The chain is built using `TelemetryProcessorChainBuilder`. Processors can inspect the item and decide to drop it (by not calling `Next.Process`). This runs after initializers but before the channel.
Track Custom Telemetry
Use methods like `TrackEvent`, `TrackMetric`, `TrackTrace`, `TrackDependency`, `TrackRequest`, and `TrackException` to send custom telemetry. For events and metrics, provide a name and optional properties/measurements. For metrics, prefer `GetMetric` for pre-aggregation to reduce volume. Each call creates a telemetry item (e.g., `EventTelemetry`) that is timestamped and queued in the channel's buffer.
Flush and Shutdown Gracefully
Before the application exits, call `TelemetryClient.Flush()` to send all buffered telemetry. The flush is asynchronous; wait a few seconds (e.g., `Task.Delay(5000)`) to allow the channel to send data. In ASP.NET Core, the SDK automatically flushes on shutdown if configured. Failure to flush may result in data loss. The channel's `SendingInterval` (default 30s) controls how often buffered items are sent.
Scenario 1: E-commerce Checkout Tracking
A large online retailer needs to track every step of the checkout process to identify bottlenecks. They use TrackEvent with properties like StepName, CartValue, and PaymentMethod. They also track custom metrics like TimeSpentOnStep using GetMetric for pre-aggregation. The SDK is configured with a custom telemetry initializer that adds SessionId and UserId to all events. In production, the application handles thousands of checkouts per minute. To avoid overwhelming the ingestion endpoint, they enable adaptive sampling (default target 5 events/sec). However, they mark critical events (e.g., 'PaymentFailed') with SamplingPercentage = 100 by using a telemetry processor that sets item.SamplingPercentage = 100 for those events, ensuring no sampling. They also set up alerts on custom metrics to trigger when the checkout abandonment rate exceeds 20%.
Scenario 2: IoT Device Telemetry
A manufacturing company collects sensor data from thousands of IoT devices. Each device runs a .NET Core application that sends custom metrics (temperature, pressure, vibration) using TrackMetric. To reduce cost and storage, they configure fixed-rate sampling at 10% in the SDK. They also use a telemetry processor to drop metrics that are within normal range (e.g., temperature between 20-30°C) and only send anomalies. The telemetry channel is configured with a larger buffer (MaxTelemetryBufferCapacity=2000) to handle bursts. They also implement a custom channel that writes to a local file if the network is down, then replays when connectivity returns. This ensures no data loss during network outages.
Scenario 3: Multi-tier Microservices
A SaaS provider has a microservices architecture with 20 services. They use Application Insights for distributed tracing. Each service sends custom events for business operations (e.g., 'UserRegistered', 'SubscriptionUpgraded'). They use a telemetry initializer to set Cloud.RoleName to the service name, so they can filter telemetry by service. They also use a telemetry processor to add CorrelationId from the request headers to all custom events, enabling end-to-end tracking. The ingestion endpoint is configured with a custom endpoint for compliance reasons (e.g., https://dc.applicationinsights.azure.com). They monitor the live metrics to ensure no data loss and set up alerts on the rate of custom events per second to detect anomalies.
Common Pitfalls
Forgetting to flush before shutdown leads to data loss.
Creating multiple TelemetryClient instances causes excessive memory use and potential duplicate telemetry.
Not pre-aggregating custom metrics results in high volume and cost. Always use GetMetric.
Overusing custom properties (max 512 characters per property, 100 properties per event) can cause truncation or rejection.
Misconfiguring sampling can cause critical events to be dropped. Use telemetry processors to exempt important events.
What AZ-204 Tests on Custom Telemetry
The AZ-204 exam under objective 'Monitor' (4.1) tests your ability to instrument applications with custom telemetry. You must know:
How to create and configure a TelemetryClient with connection string.
The difference between TrackEvent, TrackMetric, TrackTrace, and TrackException.
How to add telemetry initializers and processors.
How to configure sampling (adaptive vs. fixed-rate) and its impact.
How to use GetMetric for pre-aggregation.
How to flush telemetry before shutdown.
Common Wrong Answers and Why Candidates Choose Them
Using instrumentation key instead of connection string: Many candidates still use the deprecated instrumentation key. The exam emphasizes connection strings because they are more secure and support sovereign clouds. Wrong: 'InstrumentationKey=...'. Correct: 'InstrumentationKey=...;IngestionEndpoint=...'.
Creating a new TelemetryClient for each request: Candidates think it's stateless, but it causes resource leaks. The SDK is designed to be a singleton. Wrong: Creating multiple instances. Correct: Use dependency injection as a singleton.
Not pre-aggregating metrics: Candidates send raw metrics individually, causing high volume. Wrong: TrackMetric for every data point. Correct: Use GetMetric to pre-aggregate.
Setting sampling percentage in code incorrectly: Candidates assume sampling is disabled by default. Wrong: Not configuring sampling leads to high data volume. Correct: Understand adaptive sampling is enabled by default (target 5 items/sec).
Specific Numbers and Terms That Appear on the Exam
Default buffer size: 500 items.
Default send interval: 30 seconds.
Adaptive sampling target: 5 items per second.
Maximum custom properties per event: 100.
Maximum property key length: 150 characters.
Maximum property value length: 8192 characters.
Connection string format: InstrumentationKey=...;IngestionEndpoint=....
GetMetric returns a Metric object that pre-aggregates before sending.
Edge Cases and Exceptions
TelemetryProcessor vs. TelemetryInitializer: Initializers always run and modify items; processors can drop items. The exam may ask which to use for filtering.
Sampling exemption: Use item.SamplingPercentage = 100 in a processor to prevent sampling.
Channel configuration: Custom channel for persistent storage is advanced but may appear.
Flush behavior: Flush() is synchronous but the actual send is async; need to delay before exit.
Multiple telemetry clients: If using multiple TelemetryClient with different configurations, they may share the same channel, leading to unexpected behavior.
How to Eliminate Wrong Answers
If the question mentions 'filtering telemetry based on content', the answer is a telemetry processor, not an initializer.
If the question asks about 'pre-aggregating metrics', the answer is GetMetric, not TrackMetric.
If the question asks about 'adding a property to all telemetry', the answer is a telemetry initializer.
If the question involves 'reducing cost by dropping non-critical telemetry', the answer is sampling or a processor.
If the question mentions 'ensuring no data loss during network outage', the answer is a custom channel with persistent storage.
Use a single TelemetryClient singleton per application.
Prefer connection string over instrumentation key.
Use TelemetryInitializer to add common properties to all telemetry.
Use TelemetryProcessor to filter or modify telemetry (can drop items).
Use GetMetric for custom metrics to pre-aggregate and reduce volume.
Call Flush() and wait before application shutdown to avoid data loss.
Adaptive sampling is enabled by default with target 5 items/sec.
To exempt an event from sampling, set SamplingPercentage = 100 in a processor.
Maximum custom properties per event: 100; max property key length: 150; max value length: 8192.
Default channel buffer size: 500 items; default send interval: 30 seconds.
These come up on the exam all the time. Here's how to tell them apart.
TelemetryInitializer
Adds or modifies properties on all telemetry items.
Cannot drop telemetry items.
Runs before telemetry processors.
Implements ITelemetryInitializer.
Example: Add 'Environment' property to all events.
TelemetryProcessor
Can filter, modify, or drop telemetry items.
Can drop items by not calling Next.Process().
Runs after telemetry initializers.
Implements ITelemetryProcessor.
Example: Drop events with name 'SensitiveEvent'.
TrackMetric
Sends each metric data point individually.
No pre-aggregation; high volume.
Suitable for low-frequency metrics.
Deprecated in favor of GetMetric.
Simple to use but costly.
GetMetric
Pre-aggregates metrics before sending.
Sends aggregated values (count, sum, min, max, stddev).
Recommended for high-frequency metrics.
Returns a Metric object that tracks aggregations.
Reduces telemetry volume and cost.
Mistake
You can use multiple TelemetryClient instances for different telemetry types.
Correct
You should use a single TelemetryClient instance. The SDK is designed to be thread-safe and handles all telemetry types. Multiple instances waste resources and may cause duplicate telemetry or channel conflicts.
Mistake
Custom metrics should be sent individually with TrackMetric for each data point.
Correct
Sending each data point individually generates high volume and cost. Use GetMetric to pre-aggregate metrics (e.g., count, sum, min, max) and send aggregated values at intervals. This reduces telemetry volume significantly.
Mistake
Instrumentation key is the preferred way to configure Application Insights.
Correct
Connection strings are now preferred because they include the ingestion endpoint, supporting sovereign clouds and reducing configuration errors. Instrumentation keys are deprecated for new applications.
Mistake
Telemetry initializers can filter out telemetry items.
Correct
Initializers cannot drop items; they only add or modify properties. To filter telemetry, use a telemetry processor, which can choose not to call Next.Process() to drop an item.
Mistake
Flush() immediately sends all telemetry to the server.
Correct
Flush() triggers the channel to send buffered items, but the send is asynchronous. You must wait a few seconds (e.g., Task.Delay(5000)) before the application exits to ensure data is transmitted. The channel uses a background thread.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
Install the `Microsoft.ApplicationInsights` NuGet package. Create a `TelemetryClient` with a connection string. Use `TrackEvent`, `TrackMetric`, etc. Call `Flush()` and `Task.Delay(5000)` before exit. Example: `var client = new TelemetryClient(new TelemetryConfiguration("connectionString")); client.TrackEvent("AppStarted"); client.Flush(); Task.Delay(5000).Wait();`
`TrackEvent` is for custom events that represent significant occurrences (e.g., 'OrderPlaced'), with optional metrics. `TrackTrace` is for diagnostic log messages with severity levels (e.g., 'Processing file X'). Both support custom properties, but events are typically used for business analytics, traces for debugging.
Implement a telemetry processor that sets `item.SamplingPercentage = 100` for the event. This marks the event as exempt from sampling. Example: `if (item is EventTelemetry eventTelemetry && eventTelemetry.Name == "CriticalEvent") { eventTelemetry.SamplingPercentage = 100; }`
Yes, you can send HTTP POST requests directly to the Application Insights ingestion endpoint. The endpoint is `https://dc.services.visualstudio.com/v2/track`. You must format the payload as JSON with an array of telemetry items. However, using the SDK is recommended for reliability, retries, and ease of use.
Azure Functions automatically integrate with Application Insights when you enable it in the function app settings. You can inject `TelemetryClient` into your function using dependency injection. The SDK is already configured; you just need to add the NuGet package and use the client.
By default, items are dropped (lost). You can configure a custom overflow handler or increase the buffer size (`MaxTelemetryBufferCapacity`). Alternatively, you can implement a custom channel that persists to disk. The default `InMemoryChannel` drops items silently.
In the Azure portal, go to your Application Insights resource, then under 'Investigate' select 'Events' or 'Search'. You can query custom events using the 'customEvents' table in Log Analytics. Use Kusto queries like `customEvents | where name == "MyEvent"`.
You've just covered Custom Telemetry with Application Insights SDK — now see how well it sticks with free AZ-204 practice questions. Full explanations included, no account needed.
Done with this chapter?