KQL · usage
Custom events by name as a timechart
Chart hourly counts of your ten most frequent custom events over the last 7 days, sampling-adjusted, plus how to sum a custom measurement.
customEvents usage
let lookback = 7d;
let topEvents = customEvents
| where timestamp > ago(lookback)
| summarize events = sum(itemCount) by name
| top 10 by events desc
| project name;
customEvents
| where timestamp > ago(lookback)
| where name in (topEvents)
| summarize events = sum(itemCount) by bin(timestamp, 1h), name
| order by timestamp asc
| render timechart Returns: One line per custom event name for the ten most frequent events, showing the sampling-adjusted count in each hour of the last 7 days.
Pitfalls
- Custom events are sampled like other telemetry, so count them with sum(itemCount). The classic ASP.NET SDKs sample events separately from other types by default, so events can run at a different sampling rate from requests.
- Event names are case-sensitive in the comparison. CheckoutCompleted and checkoutCompleted are two lines; agree on one spelling in code.
- customMeasurements is a dynamic column. Convert a value with todouble() before doing arithmetic on it.
- Ten events can be the wrong ten if a chatty diagnostic event dominates. Exclude it in topEvents with a where clause on name.
How it works
Custom events are the business side of telemetry: a sign-up, a checkout, a report export, sent with TrackEvent in the classic SDKs or as an event through OpenTelemetry. Charting them over time is how you find out that checkouts stopped at 14:00 before a customer tells you.
Line by line
let topEvents = ...;finds the ten event names with the most occurrences in the window and keeps only the name column. Charting every event name produces an unreadable chart.where name in (topEvents)keeps those ten.events = sum(itemCount)counts occurrences the sampling-aware way. Microsoft’s Classic API documentation makes the point with this table:itemCount10 means only one of ten calls was kept, so the correct count issum(itemCount).by bin(timestamp, 1h), namegives one count per event per hour; the time bin comes first because the chart takes its x-axis from the first column.render timechartdraws one line per name.
Summing a custom measurement
Events can carry numbers in customMeasurements, such as an order total. The data model notes that measurements are sampled together with the item they belong to, so weight them by itemCount too:
let lookback = 7d;
customEvents
| where timestamp > ago(lookback)
| where name == "CheckoutCompleted"
| extend order_total = todouble(customMeasurements["OrderTotal"])
| summarize checkouts = sum(itemCount), revenue = sum(order_total * itemCount) by bin(timestamp, 1d)
| order by timestamp asc
Replace the event and measurement names with your own; the keys are whatever your code sent. String values belong in customDimensions, read with tostring(customDimensions["Plan"]).
When to use it
Use it as a business health chart next to the technical ones, and as the basis of an alert on an event that should never stop, such as orders placed. For a value you want to chart independently of events, a metric is the better tool; the custom metrics guide explains the difference. The one-request guide shows how to send a test event and find it here.
Variations
- One event, daily: filter to one
nameand bin by1d. - Workspace tables: use
AppEvents,TimeGenerated,Name,Measurements(the classiccustomMeasurements),Properties(the classiccustomDimensions) andItemCount.