Custom Metrics & Events
Overview
While Application Insights auto-collects requests, dependencies, and exceptions, custom telemetry lets you track business-specific events and metrics that matter to your domain โ order completions, payment failures, queue depths, or feature usage.
TelemetryClient Basics
The TelemetryClient is the primary API for sending custom telemetry in .NET:
public class OrderService
{
private readonly TelemetryClient _telemetry;
public OrderService(TelemetryClient telemetryClient)
{
_telemetry = telemetryClient;
}
}
๐ก In ASP.NET Core,
TelemetryClientis registered automatically when you callAddApplicationInsightsTelemetry().
TrackEvent โ Custom Events
Use TrackEvent to record discrete business actions:
public async Task PlaceOrder(Order order)
{
// ... business logic ...
_telemetry.TrackEvent("OrderPlaced", new Dictionary<string, string>
{
["OrderId"] = order.Id,
["CustomerId"] = order.CustomerId,
["Region"] = order.ShippingRegion
}, new Dictionary<string, double>
{
["ItemCount"] = order.Items.Count,
["TotalAmount"] = (double)order.Total
});
}
Parameters:
- Name โ The event name (appears in
customEventstable) - Properties โ String key-value pairs for filtering/grouping
- Metrics โ Numeric values associated with the event
TrackMetric โ Custom Metrics
Use TrackMetric for numeric measurements:
// Track queue depth every 30 seconds
_telemetry.TrackMetric("QueueDepth", currentDepth);
// Track with dimensions
var metric = _telemetry.GetMetric("ProcessingTime", "MessageType");
metric.TrackValue(elapsed.TotalMilliseconds, messageType);
GetMetric vs TrackMetric
| Method | Pre-aggregation | Recommended |
|---|---|---|
TrackMetric | No โ sends every value | Legacy |
GetMetric().TrackValue() | Yes โ aggregates locally | โ Preferred |
GetMetric pre-aggregates values locally before sending, reducing network traffic and cost:
// Pre-aggregated metric with up to 2 dimensions
var metric = _telemetry.GetMetric("ApiLatency", "Endpoint", "StatusCode");
metric.TrackValue(responseTime, "/api/orders", "200");
Custom Dimensions
Custom dimensions (properties) let you slice and filter telemetry:
_telemetry.TrackEvent("PaymentProcessed", new Dictionary<string, string>
{
["PaymentMethod"] = "CreditCard",
["Gateway"] = "Stripe",
["Currency"] = "USD"
});
Query in KQL:
customEvents
| where name == "PaymentProcessed"
| extend gateway = tostring(customDimensions["Gateway"])
| summarize count() by gateway
| render piechart
Telemetry Initializers โ Global Properties
Add properties to ALL telemetry automatically:
public class EnvironmentInitializer : ITelemetryInitializer
{
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.GlobalProperties["Environment"] = "Production";
telemetry.Context.GlobalProperties["ServiceVersion"] = "2.1.0";
}
}
// Register in DI
builder.Services.AddSingleton<ITelemetryInitializer, EnvironmentInitializer>();
JavaScript / Browser SDK
Track events from the frontend:
import { ApplicationInsights } from "@microsoft/applicationinsights-web";
const appInsights = new ApplicationInsights({
config: { connectionString: "InstrumentationKey=..." }
});
appInsights.loadAppInsights();
// Track a custom event
appInsights.trackEvent({ name: "ButtonClicked" }, { buttonId: "checkout", page: "cart" });
// Track a metric
appInsights.trackMetric({ name: "CartValue", average: 149.99 });
Node.js Custom Telemetry
const appInsights = require("applicationinsights");
const client = appInsights.defaultClient;
client.trackEvent({
name: "OrderShipped",
properties: { orderId: "12345", carrier: "FedEx" },
measurements: { weight: 2.5 }
});
client.trackMetric({ name: "ActiveConnections", value: 42 });
Best Practices
- Use consistent naming conventions โ PascalCase for event names, camelCase for properties
- Keep cardinality low on dimensions โ Don't use unique IDs as dimension values (use properties instead)
- Prefer GetMetric over TrackMetric โ Pre-aggregation saves cost and bandwidth
- Don't track PII โ Never put emails, SSNs, or passwords in custom dimensions
- Batch and flush โ In short-lived processes, call
TelemetryClient.Flush()before exit
_telemetry.TrackEvent("BatchComplete");
_telemetry.Flush();
// Wait for flush to complete
Task.Delay(5000).Wait();
- Use structured event names โ
Feature.Actionpattern (e.g.,Cart.ItemAdded,Payment.Failed) - Set operation context โ Ensures custom telemetry correlates with the parent request
using var operation = _telemetry.StartOperation<RequestTelemetry>("ProcessBatch");
// All telemetry inside this scope shares the operation_Id
_telemetry.TrackEvent("BatchItemProcessed");
Querying Custom Telemetry
// Events by name over time
customEvents
| where timestamp > ago(24h)
| summarize count() by name, bin(timestamp, 1h)
| render timechart
// Metric values
customMetrics
| where timestamp > ago(1h)
| where name == "QueueDepth"
| summarize avg(value), max(value) by bin(timestamp, 5m)
| render timechart
Key Takeaways
TrackEventrecords business actions;GetMetricrecords numeric measurements- Custom dimensions enable powerful filtering and grouping in KQL
- Use telemetry initializers for properties that apply to all telemetry
- Pre-aggregate with
GetMetricto reduce cost in high-throughput scenarios - Never include PII in telemetry properties