Skip to content
prod e051e98
Browse

Monitoring & analytics

The mental model before you execute

Once your app is live, two questions stop being optional: is it broken? and is anyone using it? A vendor app ships “working”, but it does not tell you when a checkout silently 500s for one customer, or which of your funnel pages quietly loses half its visitors. Two different tools answer those two questions — error monitoring (Sentry) and web analytics — and confusing them is the most common beginner mistake. This guide builds the mental model so the setup phases make sense when you run them; it does not run commands for you, the playbooks do that.

Without either tool you are flying blind in two distinct ways. Error monitoring is your smoke alarm: when an exception fires in production, you want to know immediately, with the stack trace and the environment, before the customer emails you (or worse, leaves silently). Web analytics is your traffic report: it tells you where visitors come from, what path they take, and where they give up — the difference between guessing why signups are flat and seeing that 60% drop off on the pricing page. One watches your code; the other watches your users. You need both, and you should never make one do the other’s job.

The two tools at a glance — different question, different data

Section titled “The two tools at a glance — different question, different data”

The clearest way to keep these straight is by the question each one answers and the kind of event it records.

Error monitoring (Sentry)Web analytics
Answers”Is my app broken — where, and how often?""Who visits, and where do they drop off?”
WatchesYour code (exceptions, slow transactions)Your users (pageviews, funnels, conversions)
EventA thrown exception, a failed job, a slow requestA pageview, a click, a goal completion
You wantZero of these (every one is a bug)More of these (every one is a visitor)
LivesServer-side SDK in Laravel + a dashboardA JS snippet in your page <head> + a dashboard
Alerts onA new error, an error spike, a slow endpointUsually reports, not real-time alarms

The asymmetry in the “You want” row is the whole point. A monitoring dashboard trending toward zero is healthy; an analytics dashboard trending toward zero means nobody is coming. Read them with opposite instincts.

graph TD
App["Your live Laravel app"]
App -->|"exception / slow request"| Sentry["Error monitoring<br/>(Sentry SDK, server-side)"]
App -->|"pageview / click"| Analytics["Web analytics<br/>(JS snippet, client-side)"]
Sentry --> SD["Issues · stack traces · alerts<br/><i>question: is it broken?</i>"]
Analytics --> AD["Funnels · conversions · sources<br/><i>question: is anyone using it?</i>"]

Error monitoring — how Sentry actually works

Section titled “Error monitoring — how Sentry actually works”

Sentry catches exceptions your app throws in production and packages each into an issue: the error message, a full stack trace, the environment (local / staging / production), the PHP runtime, and a trace ID. Instead of grepping storage/logs/laravel.log after a customer complains, you get the failure pushed to a dashboard the moment it happens.

In Laravel the wiring is small: install the sentry/sentry-laravel package, register Integration::handles() in bootstrap/app.php, point a SENTRY_LARAVEL_DSN at your project, and add a sentry_logs channel to config/logging.php. The DSN (Data Source Name) is the address that tells the SDK where to send events — it is a project key, not a secret on the level of a payment key, but it still lives in .env and a secrets manager, never hard-coded.

The key design decision: use one Sentry project for the whole app and separate the data by an environment tag (local, staging, production), rather than creating a project per environment. The environment comes from APP_ENV, so the same code reports to the right bucket automatically, and you filter the dashboard by environment:production to see only what’s real.

graph LR
Local["local<br/>APP_ENV=local"] --> Proj["One Sentry project"]
Staging["staging<br/>APP_ENV=staging"] --> Proj
Prod["production<br/>APP_ENV=production"] --> Proj
Proj -->|"filter by environment tag"| View["Issues dashboard<br/>(one place, three buckets)"]

Sampling — why production sends only a fraction

Section titled “Sampling — why production sends only a fraction”

Sentry can record traces (performance data — how long requests and queries take) in addition to errors, and traces are high-volume. The sample rate controls what fraction gets sent. The convention that balances cost against coverage:

EnvironmentSENTRY_TRACES_SAMPLE_RATEWhy
local / staging1.0 (100%)Catch everything while you’re testing — volume is tiny
production0.1 (10%)Real traffic is high-volume; 10% controls quota cost while still catching real patterns

A subtle point beginners miss: errors are not the same as traces. You still capture every exception in production — sampling applies to performance traces, not to the errors themselves. You never want to miss a real bug to save quota.

Privacy — keep user data out of your error tool

Section titled “Privacy — keep user data out of your error tool”

Stack traces can accidentally carry personal data (an email in a variable, an IP in the request). The safe default for a GDPR-aware app:

  • Set send_default_pii => false so the SDK does not attach user emails/IPs automatically.
  • Enable Sentry’s Data Scrubber with the default scrubbers, and add your own sensitive fields (email, password, token, api_key, authorization, credit_card, ssn).
  • Turn on Prevent Storing IP Addresses.

This is the same principle as the security guide — a compliance promise needs a setting behind it. “We don’t store user PII” is only true once these are on.

Monitoring is worthless if nobody looks at the dashboard. Alerts push the signal to you:

  1. New error (first occurrence) — email the moment an issue is seen for the first time. This is the one that catches the bug before the customer does.
  2. High-volume spike — email when an issue exceeds a threshold (e.g. >100 events/hour) so a sudden regression gets your attention even if it’s an old issue.
  3. Slow transaction (optional) — email when a P95 transaction crosses a budget (e.g. >2s) so a creeping performance problem surfaces before users complain.

Web analytics — how visitor tracking works

Section titled “Web analytics — how visitor tracking works”

Web analytics answers the other question. A lightweight JavaScript snippet in your page <head> reports each pageview to an analytics service, which assembles them into sessions (one visitor’s journey), funnels (the ordered steps toward a goal), and conversions (the goal completions that actually matter to the business).

Where Sentry lives server-side in your PHP, analytics is client-side — a script tag in the rendered HTML. On a Laravel app there are a few clean ways to inject it:

MethodWhat it isBest when
Layout includePaste the snippet directly into the base Blade layout <head>Simplest; one site, you control the template
Config + env flagSnippet behind config('services.…') driven by a VENDOR_TRACKING_ENABLED env varYou want to toggle tracking per environment without editing Blade
View composer / middlewareA service provider or middleware injects the snippet before </head>Vendor-safe — you avoid hand-editing a template the vendor owns

The config-flag approach is the one that matters most on a CodeCanyon app, because it lets you keep tracking off on local and staging (you don’t want your own testing polluting the data) and on in production — flipped by a single env var, no code change. On a Workdo/vendor app, prefer injecting via a service provider or the vendor’s own settings field over editing a vendor Blade file directly; that is the same 3-file “safe vendor deviation” shape the analytics setup step walks through (one template hook, one config key, one env var).

Funnels and goals — the part that earns its keep

Section titled “Funnels and goals — the part that earns its keep”

Raw pageview counts are vanity metrics. The value is in two constructs:

  • A goal is a single action that means success — an account created, a demo requested, a paid plan purchased. You define it by the page it lands on (/dashboard after signup) or the element clicked.
  • A funnel is an ordered sequence of steps toward a goal, so you can see exactly which step loses people.
graph LR
S1["Homepage<br/>1,000 visitors"] -->|"60% continue"| S2["Pricing<br/>600"]
S2 -->|"40% continue"| S3["Signup<br/>240"]
S3 -->|"75% continue"| S4["Account created<br/>180"]

The drop from 600 → 240 in that example is the actionable insight: most loss happens at the pricing-to-signup step, so that page is where an improvement pays off most. You can’t see that from a total-visitors number; you can only see it from a funnel.

Privacy — analytics has its own GDPR story

Section titled “Privacy — analytics has its own GDPR story”

A privacy-first analytics tool collects no personal data by default: anonymous (hashed) visitor IDs, optional or omitted IP addresses, and often no cookies at all. The practical rule for a solo SaaS:

Tracking levelWhat it collectsCookie banner needed?
Anonymous (recommended)Hashed IDs, no IP, no cookiesNo — privacy-first by default
BasicOptional IP, hashed cookiesOften yes, depending on jurisdiction
FullAll dataYes — requires explicit consent

Choosing the anonymous level keeps you GDPR-compliant without a consent banner, which is usually the right trade for a small app. Set a data-retention window (e.g. 90 days) and honor Do-Not-Track, and the analytics tool stops being a compliance liability.

They overlap just enough to confuse, so anchor on the question you’re actually asking:

You want to know…Reach forNot
Why did this customer hit an error?Error monitoringAnalytics (it doesn’t see stack traces)
Is the checkout endpoint getting slower?Error monitoring (performance traces)Analytics
Where do visitors abandon signup?Analytics (funnels)Error monitoring (a drop-off isn’t an error)
Which traffic source converts best?AnalyticsError monitoring
Did my last deploy introduce a regression?Error monitoring (release health)Analytics
Should I rebuild the pricing page?Analytics (the funnel says so)Error monitoring

The one-line test: if the thing you’re chasing is a bug, it belongs in error monitoring; if it’s a behavior, it belongs in analytics. A 500 error is a bug. A 50% bounce rate is a behavior. Neither tool can tell you about the other’s domain — that’s why a healthy app runs both.

This guide is the why. The hands-on steps live in the setup playbook, in two different phases because they’re two different concerns: