%3Aquality(100)%2F&w=3840&q=75)
From failed check to real user impact: Pairing Synthetic Monitoring and Frontend Observability in Grafana Cloud
Say you get a support escalation about a page in the app that won’t load. But when you pull up your synthetic checks, they're all green: 100% uptime, probes are passing. Something's not adding up, but which one do you trust?
If you’ve run Grafana Cloud Synthetic Monitoring, you’ve been on both sides of this. Sometimes it's the ticket: real users hit a wall on the path but your checks pass cleanly. Other times, it’s the inverse: a check is failing, you're in a panic, and you start trying to reproduce things for 30 minutes—only to find it was a blip from a single region, with minimal impact to real users. Neither the green dashboard nor the red alert were lying, they just weren’t answering the correct question.
This ends up being the root problem. Synthetic Monitoring is exceptionally good at telling you if something broke. It can not, however, tell you who it happened to, how bad it was, or why it matters. This is not a flaw in Synthetic Monitoring; it's the boundary of what a controlled, scheduled test can know.
Grafana Cloud Frontend Observability helps to close this gap. Synthetic Monitoring gives you a proactive, outside-in signal; Frontend Observability gives you the real-user, inside-out signal. Together they form a closed loop: synthetic alerts end up getting some real user context, and real user data can make your synthetic tests smart.
In this post, we’ll look at why a synthetic-only strategy can leave blind spots, what Frontend Observability adds, and walk through practical workflows for running them together in Grafana Cloud.
Along the way, you'll learn that the payoff is concrete: faster triage, alerts that carry blast-radius context, and a check suite that evolves with real traffic instead of aging against it.
Green checks don't mean happy users
Synthetic Monitoring is an active signal. You script a journey or declare a target, run it on a schedule from known probe locations, and in return get clean consistent results. This precise control of variables is its value: when a check fails or metrics from a check deviate, it’s easy to know the exact locations, the exact steps, and the exact assertions causing it. It’s how you detect issues before your users do.
But, a synthetic-only strategy has fundamental gaps:
Synthetic tests only cover what you think to test. Your scripted paths only reflect the journeys you predict. Real users can take emergent paths, arrive from long-tail browser and device combinations, and hit regional edge cases that probes might not be able to reproduce. The error that enters your escalation chain could be a path nobody thought about.
No visibility of the blast radius. A failing check tells you that a user will fail. It cannot tell you how many real users have already failed. Is this failure impacting three users in a single region? Or is it silently degrading the experience for thousands? A synthetic check cannot tell you this.
Alert fatigue without signal enrichment. When every synthetic alert carries the same (lack of) user-impact context, every alert can feel equally urgent. Many users combat this with proper labeling strategies and tweaking alert sensitivities; but sometimes you get caught in the rut where there’s so many alerts they stop feeling urgent and suddenly monitoring has been eroded.
I always think of Synthetic Monitoring as a canary: it’s trying to warn you early, reliably, and before too many users are affected. But the canary can only tell you that the air has gone bad; it can’t tell you how many people might be impacted by it.
What’s actually happening in your users’ browsers
Frontend Observability is real user monitoring (RUM), powered by the open source Grafana Faro SDK. Once your app is instrumented with the lightweight JavaScript snippet, it captures what’s actually happening in your users’ browsers. With Frontend Observability, you have access to metrics like:
- Core Web Vitals and page performance from real sessions: loading, interactivity, and visual stability as users actually experience them, across real devices, networks, and geographies
- JavaScript errors with stack traces and the user context around them—including the errors your checks were never scripted to catch
- Session Replay so you can follow a real user's journey from entry to exit and see exactly what they did before something broke
This data isn’t redundant with your synthetic data, even though they both can produce Web Vitals. The reason why is the entire logic behind running them together.
A synthetic check is a controlled experiment. Same script, same target, same probe locations, same runtime. Over and over and over; the only input changing is the time it ran. So when a result of a check starts to deviate from its baseline, that deviation means something; your system changed, not the test. This determinism is what makes synthetic data trustworthy enough to alert on.
Real user data is quite the opposite. Every session is effectively unique. Different users on different devices, browsers, operating systems, networks, geography, and much more. No single session will tell you whether a problem is your code, a spotty connection, or an aggressive browser extension. But if you aggregate enough sessions, commonalities begin to surface: every affected user is on Chrome, or in a single region, or hitting the same JS error. The signal isn't in any one sample, it’s the broad pattern across them.
This is why neither replaces the other. Synthetics checks give you a number of samples you can trust individually; RUM gives you thousands to trust in aggregate. One detects something has changed; the other tells you who it's affecting and what the affected have in common.
The reliability loop in practice
Here’s where it gets practical. The loop runs in both directions: reactive (synthetic alert fires -> real user data to scope it) and proactive (real-user data reveals a gap-> codify it as a check).
Reactive: Understand a blast radius after a synthetic alert
Every on-call engineer knows the scramble that follows a failed check: Is it real? Does it actually matter? Can I reproduce it myself or through a proxy of someone else? Real-user data turns that struggle into a look up.
The scenario: your browser check on /checkout fails from the Frankfurt probe. Before Frontend Observability, the next step was guesswork, but with both signals in Grafana Cloud, the workflow looks like this:
- The check fails. Synthetic Monitoring alerts you that the
/checkoutbrowser check failed from Frankfurt at 09:13. - Pivot to real sessions. Follow the direct link in Synthetic Monitoring straight into the captured session in Frontend Observability. We built this connection specifically to skip manual timestamp-matching and url-hunting. From here, you can see what page may be experiencing the problem, a session replay from your browser session, and click the Page button to see real user sessions that visit the same page.
- Quantify the impact and look for the pattern. You can now see how many real users hit an error in the same window, and just as importantly, what the affected sessions have in common: one browser? One region? The same JavaScript error? Those commonalities narrow root cause before you've opened a single trace. Something no individual session, and no synthetic check, could tell you alone.
- Confirm the root cause. Compare the error real users encountered against the failed assertion in your check.
- Act with data, not gut-feel. You now have:
- Scope: How many users were affected
- Duration: When the issue actually started in the real world, not just when your check cadence caught it
- Severity: What users experienced, a hard failure or a slow degradation
The outcome: incident priority and customer comms are data-driven. "The checkout flow has degraded for approximately 8% of EU users since 09:07" is a very different first Slack message than "a check failed, investigating."
%3Aquality(100)%2F&w=3840&q=75)
A replay of the captured session
Proactive: Turn real-user data into smarter synthetic tests
The loop's return path is the one most teams miss. Without it, your synthetic suite doesn’t get updated as real usage changes.
The scenario: Frontend Observability shows that a meaningful share of Chrome users hit a JavaScript error on /onboarding/step-3: a path that no synthetic check covers, because nobody predicted users would struggle there.
- Find the coverage gap. Use Frontend Observability's top errors and slowest pages views to see where real users struggle most, then compare that list against your synthetic check inventory. The mismatches are your blind spots.
- Set thresholds from reality. Instead of arbitrary values, set latency thresholds from the real-user p75/p95 baselines Frontend Observability gives you. Your check now alerts when performance degrades relative to what real users normally experience, not relative to a guess someone made in a sprint planning meeting.
- Retire low-signal checks. Real traffic data also shows which monitored pages barely anyone visits. Reallocating those executions toward high-traffic, high-struggle paths improves coverage without increasing spend.
The outcome: your synthetic suite reflects real traffic patterns instead of last year's assumptions—and every check you run earns its place.
%3Aquality(100)%2F&w=3840&q=75)
Count of error “too many recommendations” over time, signaling a potential problem related to this service
Setting up the correlation in Grafana Cloud
You don't need a deep integration to run this loop; both products land their data in the same Grafana Cloud stack. A few practical building blocks:
- Instrument the app your checks already monitor. The fastest path to value: take your most critical browser check and instrument that same URL with the Faro SDK first. Now you have both signals for the flow you already decided matters most.
- Query both sources side by side. Synthetic results are Prometheus metrics and Loki logs; Faro data is queryable in the same Grafana Cloud instance. In Explore, you can line up a check failure against real-user errors by time range and URL.
- Build a single pane for your critical flow. Aim for a simple dashboard layout that works well: synthetic check status and uptime on one side, Faro error rate and session count for the same URL on the other. When the check fails, blast radius is one glance away, before the first Slack message.
Wrapping up
If you lead a team that owns reliability, here's what closing this loop actually buys you:
- Faster triage. The debate over whether it's real or a fluke disappears, you have both signals in one place.
- Better on-call. Understanding blast radius context from the start, so severity calls happen in minutes, not after twenty minutes of digging.
- Smarter test coverage. Your synthetic suite evolves with real traffic patterns instead of aging against them.
- Closer dev/ops collaboration. Developers see the real sessions behind an alert; ops trusts the synthetic baselines behind a threshold. Same data, same Grafana.
Synthetic Monitoring tells you before your customers do. Frontend Observability tells you what your customers actually experienced. You need both halves to run frontend reliability as a closed loop rather than a guessing game.
Together, Synthetic Monitoring and Frontend Observability are the two halves Grafana Labs' Digital Experience Monitoring (DEM) story: the controlled, proactive signal and the real-user ground truth, in one platform. To go deeper on that broader picture, check out our page on Digital Experience Monitoring solutions in Grafana Cloud. And if you're already using Grafana Cloud Synthetic Monitoring, you can get started with Frontend Observability in minutes; instrument the website or app your most critical check already monitors first.
%3Aquality(100)%2F&w=3840&q=75)