|
| 1 | +--- |
| 2 | +title: Debugging canvas apps with Live monitor and Trace |
| 3 | +description: Learn how to debug canvas apps across users and environments using Live monitor and the Trace function. |
| 4 | +author: carlosff |
| 5 | +ms.subservice: troubleshoot |
| 6 | +ms.topic: how-to |
| 7 | +ms.custom: canvas |
| 8 | +ms.date: 01/15/2026 |
| 9 | +ms.author: carlosff |
| 10 | +search.audienceType: |
| 11 | + - maker |
| 12 | +contributors: |
| 13 | + - carlosff |
| 14 | +--- |
| 15 | + |
| 16 | +# Debugging canvas apps with Live monitor and Trace |
| 17 | + |
| 18 | +You can use the [Live monitor](/power-apps/maker/monitor-canvasapps) together with the [Trace function](/power-platform/power-fx/reference/function-trace) to quickly diagnose issues in canvas apps—even those issues that appear only for certain users or in specific environments. The live monitor shows real-time events—network calls, data operations, errors, performance details—while Trace lets you emit structured diagnostic records that help you follow the values from behavior formulas at key moments in your app's logic. |
| 19 | + |
| 20 | +This article builds on [Debugging canvas apps with Live monitor](/power-apps/maker/monitor-canvasapps) and [Collaborative troubleshooting using Live Monitor](/power-apps/maker/monitor-collaborative-debugging). Review those pages first if you're new to Live monitor. |
| 21 | + |
| 22 | +## Why combine Live monitor and Trace |
| 23 | + |
| 24 | +Live monitor gives you visibility into platform-level activity: data operations (`getRows`, `createRow`, `patch`), control evaluations, errors (HTTP status codes like 404 or 429), timing, and delegation indicators. By adding targeted Trace calls in your [behavior formulas](power-apps/maker/canvas-apps/working-with-formulas#manage-app-behavior) (for example, `OnSelect`, `OnVisible`, `OnStart`), you enrich those platform events with domain context: which user, which environment, which screen, entity counts, business flags, elapsed times, and any information that may help you better understand what the app is doing when it runs. Together they answer both "what happened" and "why it happened." |
| 25 | + |
| 26 | +## Scenario 1: Works for one user but not another |
| 27 | + |
| 28 | +Let's start with a common scenario that we see in many support requests: User A can submit orders successfully, while User B sees intermittent failures and the UI behaves differently (a discount checkbox is disabled for User B). You suspect the underlying data (for example, a missing role assignment or a flag value) differs between their accounts. |
| 29 | + |
| 30 | +### Goal |
| 31 | +Capture what the app believes about the current user (email, roles, customer selection, discount eligibility) and correlate it with the data operations the platform performs. |
| 32 | + |
| 33 | +### Steps |
| 34 | + |
| 35 | +1. Open the app in Power Apps Studio. |
| 36 | +2. Add Trace calls in the `OnSelect` of the submit button (or in the logic that encapsulates submit). |
| 37 | +3. Save / publish the app. |
| 38 | +4. Open the Live monitor for the published app. |
| 39 | +5. To invite User A, select the 'Connect user' option |
| 40 | +6. As the User A runs through the app, you see the out-of-the-box events from the live monitor instrumented with the new Trace calls. |
| 41 | +7. Open a new instance of the Live monitor for the published app. Do the same for User B (invite the user, then have them play the app). You can now see the runtime details for that user. |
| 42 | +8. Compare the values, and try to find the difference that caused the app to behave incorrectly for one of the users. |
| 43 | + |
| 44 | +**Example `OnSelect` formula with Trace** |
| 45 | + |
| 46 | +```powerfx |
| 47 | +// Emit pre-submit context |
| 48 | +Trace( |
| 49 | + "Debug: Before Submit", |
| 50 | + TraceSeverity.Information, |
| 51 | + { |
| 52 | + user: User().Email, |
| 53 | + customerId: ddCustomer.Selected.Id, |
| 54 | + cartCount: CountRows(colCart), |
| 55 | + orderCountForCustomer: CountIf(Orders, Customer = ddCustomer.Selected), |
| 56 | + isVIP: ddCustomer.Selected.'VIP Flag', |
| 57 | + env: Param("env"), |
| 58 | + screen: App.ActiveScreen.Name |
| 59 | + } |
| 60 | +); |
| 61 | +
|
| 62 | +// Perform data operations (simplified) |
| 63 | +ForAll(colCart, |
| 64 | + Patch(Orders, Defaults(Orders), { |
| 65 | + Customer: ddCustomer.Selected, |
| 66 | + Product: ThisRecord.Product, |
| 67 | + Quantity: ThisRecord.Quantity |
| 68 | + }) |
| 69 | +); |
| 70 | +
|
| 71 | +// Post-submit trace with elapsed time and any collected errors |
| 72 | +Trace( |
| 73 | + "Debug: After Submit" |
| 74 | + TraceSeverity.Information, |
| 75 | + { |
| 76 | + orderCountForCustomer: CountIf(Orders, Customer = ddCustomer.Selected) |
| 77 | + } |
| 78 | +); |
| 79 | +``` |
| 80 | + |
| 81 | +### Analyze |
| 82 | + |
| 83 | +In Live monitor, filter by the Trace event type (or search for the name of the button, or for "Debug:" in the info). Compare User A vs User B: |
| 84 | + |
| 85 | +* Do they have different `isVIP` values? That information could change the calculation for discounts. |
| 86 | +* Are cart counts identical? If not, upstream logic differs. |
| 87 | +* Are error traces present only for User B? Expand the event to inspect error payload details. |
| 88 | + |
| 89 | +Then correlate Trace events with adjacent `getRows` / `patch` operations. If User B triggers extra data calls (perhaps a non-delegable `Filter` forcing multiple network requests), you can see them directly in the event table. |
| 90 | + |
| 91 | +## Scenario 2: Works in one environment but not another |
| 92 | + |
| 93 | +Your app behaves correctly in the **Test** environment but fails when deployed to **Production**: a gallery loads no items and submission is slow. Differences in the data between the two environments may be the cause—even though the app is the same, the data that comes through it can be different on different environments. |
| 94 | + |
| 95 | +### Goal |
| 96 | +Surface environment-specific metadata and counts, then compare the sequence and status codes of data operations between environments. In this example, the app has one screen with a form where a **Product** selected from a gallery can be updated—and the update works on **Test** but fails in **Production**. |
| 97 | + |
| 98 | +### Steps |
| 99 | + |
| 100 | +1. On the test environment, add an `OnVisible` Trace on the affected screen (for example, order entry screen) to emit information about the selected product: |
| 101 | + |
| 102 | +```powerfx |
| 103 | +Trace( |
| 104 | + "Debug: OnVisible on " & App.ActiveScreen.Name, |
| 105 | + TraceSeverity.Information, |
| 106 | + { |
| 107 | + recordId: varSelectedProduct.Id, |
| 108 | + hasDiscount: varSelectedProduct.HasDiscount, |
| 109 | + relatedOrders: CountIf(Orders, ProductId = varSelectedProduct.Id) |
| 110 | + } |
| 111 | +); |
| 112 | +``` |
| 113 | + |
| 114 | +2. Deploy the app with the new traces in the production environment. |
| 115 | +3. Open Live monitor in **Test** and then in **Production**; capture and export logs if needed (use the export option in Live monitor). |
| 116 | + |
| 117 | +### Analyze network events |
| 118 | + |
| 119 | +In the event list: |
| 120 | + |
| 121 | +* Compare initial `getRows` for `Products` across environments—does one return 0 results or error codes (404 if table missing, 403 if access denied, 429 if throttled)? |
| 122 | +* Look for repeated `getRows` calls that may indicate a non-delegable formula being reevaluated. |
| 123 | +* In the trace emitted for the screen's OnVisible property, do the products have different values for `relatedOrders` or `hasDiscount`? |
| 124 | + |
| 125 | +If you find a difference—for example, in the hasDiscount property for varSelectedProduct—you can go back to the place where that variable was created, and add more Trace calls to see how it was populated. |
| 126 | + |
| 127 | +If there are errors in the network calls (such as 4xx responses), check whether the tables / flows / connectors are set up correctly in both the **Test** and **Production** environment. |
| 128 | + |
| 129 | +## Viewing data flowing over the network |
| 130 | + |
| 131 | +Live monitor surfaces each data operation event with: |
| 132 | + |
| 133 | +* Operation type (`getRows`, `createRow`, `patch`, `removeRow`) |
| 134 | +* Data source (Dataverse table, connector name) |
| 135 | +* Timing (start/finish, duration) |
| 136 | +* Result (success, error status code like 429 for throttling) |
| 137 | +* Delegation hints (non-delegable operations cause client-side processing and more data traffic) |
| 138 | + |
| 139 | +Select an event to inspect details and correlate them with nearby Trace events to understand *why* the operation occurred. For example, a `getRows` surge following a Trace with `phase: "ApplyFilters"` may indicate an inefficient filter expression. |
| 140 | + |
| 141 | +> [!TIP] |
| 142 | +> When you see HTTP 429 (throttling), look at preceding network events to see whether a loop, or repeated evaluation triggered excessive operations (similar to the analysis in [Debugging canvas apps with Live monitor](/power-apps/maker/monitor-canvasapps)). Optimize formulas or introduce caching (collections) to reduce churn. |
| 143 | +
|
| 144 | +## Using Trace effectively |
| 145 | + |
| 146 | +The [Trace function](/power-platform/power-fx/reference/function-trace) writes a structured record to Live monitor. Key points: |
| 147 | + |
| 148 | +* Works only in behavior properties (for example, `OnSelect`, `OnChange`, `OnVisible`, `OnStart`). |
| 149 | +* Accepts simple text values, but also a record payload—where we can include more details for the information being traced. |
| 150 | +* TraceSeverity helps filter (Information, Warning, Error). Use Error severity sparingly for true failures. |
| 151 | +* Minimal performance impact when used judiciously; remove or guard traces in production if they become noisy. |
| 152 | + |
| 153 | +### Tracing data property values (debug-only buttons) |
| 154 | + |
| 155 | +Because Trace can't be placed directly inside data properties (like a label's `Text`), you can surface those values through temporary debug-only controls. It's also useful to trace the values that are used to calculate the data property, as they may indicate why the value isn't the expected one. |
| 156 | + |
| 157 | +1. Add a button `btnDebugSnapshot` with `Visible = Param("debug") = "true"`. |
| 158 | +2. In its `OnSelect`, call Trace with a snapshot record. |
| 159 | +3. When playing the app under the live monitor, add a query string parameter (`&debug=true`) to show the button. |
| 160 | + |
| 161 | +**Example debug snapshot button** |
| 162 | + |
| 163 | +```powerfx |
| 164 | +// Visible property: Param("debug") = "true" |
| 165 | +// OnSelect: |
| 166 | +Trace( |
| 167 | + "Debug: Label value: " & Label1.Text, |
| 168 | + TraceSeverity.Information, |
| 169 | + { |
| 170 | + kind: "DataSnapshot", |
| 171 | + user: User().Email, |
| 172 | + customerCount: CountRows(Customers), |
| 173 | + productCount: CountRows(Products), |
| 174 | + maxPrice: Max(Products, Price), |
| 175 | + selectedProductId: If(!IsBlank(galProducts.Selected), galProducts.Selected.ProductId) |
| 176 | + } |
| 177 | +); |
| 178 | +``` |
| 179 | + |
| 180 | +> [!NOTE] |
| 181 | +> Guard debug-only controls with query string parameters (like `debug=true`) or role checks so end users don't see them. While **Trace** calls have minimum performance impact, those "debug controls" do add some complexity to the app, so you should remove them before finalizing the app once they aren't needed anymore. |
| 182 | +
|
| 183 | +## When Live monitor can't be used |
| 184 | + |
| 185 | +Some hosted or embedded scenarios (for example, SharePoint integrated forms, custom pages, certain custom portal embeddings) don't support opening Live monitor alongside the app. In these cases, consider alternatives: |
| 186 | + |
| 187 | +| Alternative | Use case | Notes | |
| 188 | +|-------------|----------|-------| |
| 189 | +| [Application Insights integration](/power-apps/maker/canvas-apps/application-insights) | Centralized telemetry & performance | Requires setup; emits traces and metrics outside Power Apps. | |
| 190 | +| Dataverse logging table | Ad-hoc diagnostics, audit trails | Create a `Debug Logs` table; use guarded Trace-like logic that uses `Patch` to create records when `Param("debug")="true"`. | |
| 191 | +| SharePoint list logging | Lightweight environments without Dataverse | Use `Collect` / `Patch` to a list; prune entries to control size. | |
| 192 | +| On-screen diagnostics panel | Immediate user feedback | Only for secure audiences; remove before broad rollout. | |
| 193 | + |
| 194 | +**Example: Writing a debug record to Dataverse when Live monitor isn't available** |
| 195 | + |
| 196 | +```powerfx |
| 197 | +If(Param("debug") = "true", |
| 198 | + Patch( |
| 199 | + 'Debug Logs', |
| 200 | + Defaults('Debug Logs'), |
| 201 | + { |
| 202 | + Title: "BeforeSubmit", |
| 203 | + UserEmail: User().Email, |
| 204 | + CartCount: CountRows(colCart), |
| 205 | + Timestamp: Now(), |
| 206 | + Payload: JSON({customerId: ddCustomer.Selected.Id}) |
| 207 | + } |
| 208 | + ) |
| 209 | +); |
| 210 | +``` |
| 211 | + |
| 212 | +**Example: creating a diagnostics panel on the screen when Live monitor isn't available** |
| 213 | + |
| 214 | +1. In places where a `Trace` call would be made, change it to a `Collect` call to a local collection |
| 215 | +2. Add a text control to the screen, only visible for certain users or depending on a query string parameter, that displays the locally collected traces |
| 216 | + |
| 217 | +```powerfx |
| 218 | +// "Tracing" data to the collection |
| 219 | +If( |
| 220 | + Param("debug") = "true", |
| 221 | + Collect( |
| 222 | + debugTraces, |
| 223 | + { |
| 224 | + Timestamp: Now(), |
| 225 | + Data: $"Before submit for {User().Email} with {CountRows(colCart)} items in the cart" |
| 226 | + } |
| 227 | + ) |
| 228 | +) |
| 229 | +``` |
| 230 | + |
| 231 | +Add this label to the screen, which shows the traces that can be copied and viewed outside the app |
| 232 | +```yaml |
| 233 | +- TextCanvas1: |
| 234 | + |
| 235 | + Properties: |
| 236 | + Height: =200 |
| 237 | + Size: =12 |
| 238 | + Text: |- |
| 239 | + =Concat( |
| 240 | + debugTraces, |
| 241 | + $"[{Text(Timestamp,"hh:mm:ss.fff")}] {Data}", |
| 242 | + Char(10)) |
| 243 | + Visible: =Param("debug") = "true" |
| 244 | + Width: =200 |
| 245 | + X: =Parent.Width - 220 |
| 246 | + Y: =20 |
| 247 | +``` |
| 248 | +
|
| 249 | +> [!NOTE] |
| 250 | +> Once you understand the issue, remove the debug controls (or make them invisible) to prevent users from accidentally seeing them if they open the app with the debug query string parameter. |
| 251 | +
|
| 252 | +## Checklist for effective debugging |
| 253 | +
|
| 254 | +1. Reproduce with Live monitor open (Studio or published session). |
| 255 | +2. Add Trace calls at key phases (start, decision, end, error handlers) when further details are needed. |
| 256 | +3. Use query string parameters (`Param`) to tag environment or enable debug controls. |
| 257 | +4. Compare traces across users/environments; look for divergent flags or counts. |
| 258 | +5. Correlate Trace phases with network events (throttling, errors, extra calls). |
| 259 | +7. Remove or guard Trace calls before broad deployment if verbose. |
| 260 | + |
| 261 | +## Next steps |
| 262 | + |
| 263 | +[Collaborative debugging with Live monitor](/power-apps/maker/monitor-collaborative-debugging) |
| 264 | +[Advanced monitoring](/power-apps/maker/monitor-advanced) |
| 265 | +[Live monitor overview](/power-apps/maker/monitor-overview) |
| 266 | +[Trace function reference](/power-platform/power-fx/reference/function-trace) |
| 267 | + |
| 268 | +### See also |
| 269 | + |
| 270 | +[Debugging canvas apps with Live monitor](/power-apps/maker/monitor-canvasapps) |
0 commit comments