A five-hop order pipeline that cannot be traced, at 200 GB/day
An order flows through API Management, an HTTP-triggered function, a Service Bus topic, a second function, and Cosmos DB. Application Insights is enabled on both function apps and APIM with default adaptive sampling. Support reports that order 88123 never reached Cosmos; the second function's logs show a 429 from Cosmos, but Application Insights shows only about 4% of the expected request volume, and the logs from the two functions cannot be joined on any field. The Log Analytics workspace ingests 200 GB/day of which roughly 70% is requests and traces from the two function apps. Finance wants ingestion down 40% without losing the ability to investigate a single failed order. Retention is 30 days, but audit requires querying failures for 90 days.
1. Explain why the two functions' telemetry cannot be joined, and fix it.
Read the worked answer
Application Insights correlates automatically over HTTP because W3C traceparent rides in headers and the SDK reads it. The Service Bus hop silently breaks the chain: the message body carries no headers, so unless the sender writes the trace context into the message's application properties and the receiver reads it back and creates a child Activity, the second function starts a fresh root operation. That is exactly what you would see: two unrelated operation_Id trees and no way to follow order 88123 across the boundary.
Fix:
- On send, set
ServiceBusMessage.ApplicationProperties["Diagnostic-Id"](ortraceparent) fromActivity.Current. Recent versions ofAzure.Messaging.ServiceBusdo this automatically inServiceBusSender; verify the package version before writing custom code. - On receive, if not automatic, extract the property and start an activity with it as parent -
ActivitySource.StartActivity(..., parentContext)- so the SDK links the child request. - Do not rely on correlation alone for business investigation. Set the business key as a custom dimension on every item:
telemetryClient.Context.GlobalProperties["orderId"]in a middleware, or the scope-basedTelemetryClient.StartOperationwith properties. Thenunion requests, traces, exceptions | where tostring(customDimensions.orderId) == "88123"answers the support question regardless of sampling or correlation gaps.
Correlation tells you the shape of a healthy path. Dimensions answer the actual support ticket.
2. Only 4% of expected requests appear. Is this the sampling setting, and what do you change?
Read the worked answer
Partially - and the 4% figure is a red flag you should chase before touching a config value, because 4% is far more aggressive than adaptive sampling typically goes under steady load.
Two distinct things to separate:
- Adaptive sampling is per-application and does not propagate the decision. Each function samples independently, so a sampled-in request in hop one can have a sampled-out child in hop two. That produces orphaned traces even when overall volume looks fine. Fix: use a fixed-rate sampling with the same rate on both apps, or ensure parent-based (
Inheritance) sampling so children follow the parent's decision. - Something else is dropping data. Check the ingestion endpoint / instrumentation key (Application Insights endpoint restrictions, private link misconfiguration, or
samplingPercentageset in ahost.jsonorappsettingsoverride that a deployment introduced), and check therequestscount against APIM's own metrics, which are independent. A 4% figure smells like a broken pipeline or a stale key more than a sampling setting. Sampling also does not reducerequeststo 4% while leaving exceptions intact.
Then set sampling overrides so you are never debugging from sampled data where it matters:
- 100% (no sampling) for
requestswith a non-success result code,exceptions, and anything carrying a specificorderIdfailure flag. - Aggressive sampling for successful
requestsanddependencies. - Optionally move dependencies out of App Insights entirely to reduce volume - see the cost task.
The judgement: never diagnose a data-loss complaint by adjusting sampling first. Verify the pipeline, then tune.
3. Cut ingestion by 40% while keeping single-order investigations and 90-day audit queryability.
Read the worked answer
You cannot meet both goals with retention settings alone - the reduction must come from what you ingest and the audit need must move to cheaper storage.
Reduce ingestion (the 40%):
- Attack the 70%.
requestsanddependenciesat default verbosity from two function apps is enormous. Two moves: lower the sampling rate for successfulrequests/dependenciesonly (keeping failures at 100%), and stop logging HTTP client dependencies you do not need, usingsamplingExcludedTypesinverted - i.e. excludeDependencyfrom unsampled retention. - Ingestion-time filtering with a data collection rule. For workspace-based App Insights, DCR transformations can drop rows matching harmless patterns (health probes, a chatty
/statusendpoint, 200s from a known canary) before they are billed. This is cheaper than querying and discarding. - Basic Logs. Verbose custom tables that are rarely queried can be set to the Basic plan: much cheaper ingestion, and KQL still works, just without alerts on that table and with the restricted query surface. Good for the trace volume; not appropriate for the tables you alert on.
- Metrics, not logs, for alerting. Alerts on platform metrics and a handful of custom metrics cost nothing to query and are not sampled.
Audit for 90 days:
- Keep 30 days interactive in the workspace, then use a workspace retention and archive policy so data is searchable via a search job or restoration beyond the interactive window - cheaper than 90 days interactive.
- Or export failures only (a scheduled query on
exceptions/failedrequestspushing to Blob with an immutable policy) - 90-day auditability of failures does not require 90-day retention of 200 GB/day of successes.
Do not propose: turning sampling up and calling it done, or extending the workspace retention to 90 days, which increases cost rather than cutting it.