A end-to-end sample showing Sentry tracing and span metrics across a checkout flow.
- Frontend: React (Vite + TS), Tailwind CSS, Framer Motion, Fetch API
- Backend: Node.js (Express + TS), in-memory data store
- Sentry: JavaScript SDK for React and Node with single-span patterns
- Product list fetched from backend API (
GET /api/products) - Product cards with animated UI and "Add to Cart" functionality
- Cart page with item count, total price, and Checkout button
- Checkout calls
POST /api/checkoutwith simulated latency and 10–20% failures - Order confirmation modal shows order ID, provider, and total on success
- Error toast notifications display specific failure messages
- In-memory order creation and selectable fake payment providers
- Three fictional payment providers with configurable performance via
backend/.env:- ZapPay — fast, low failure (demo: low latency, 5% failure)
- GlitchPay — variable and error-prone (demo: wide latency, 30% failure)
- LagPay — slow, moderate failure (demo: high latency, 10% failure)
- Spans carry low-cardinality attributes suitable for span metrics
- Frontend: A single span per click
- name:
Checkout, op:ui.action - attributes at start:
cart.item_count,cart.value_minor,cart.currency - on success: sets
order.id,payment.provider - on error: marks span status error
- relies on auto-instrumentation for the fetch call
- name:
- Backend: A single span in the
POST /api/checkouthandler, plus a payment child span - name:
Order Processing, op:commerce.order.server - child span for payment:
Charge <Provider>, op:commerce.payment - attributes:
order.id,payment.provider,payment.status,inventory.reserved, andpayment.latency_ms
- Node 18+
# Install all dependencies (both frontend and backend)
npm install
npx playwright install --with-depsThis will automatically install dependencies for both the frontend and backend applications, and install browsers for Playwright.
Alternatively, you can install them separately:
# Frontend only
npm run install:frontend
# Backend only
npm run install:backend- Create
frontend/.envwith:
VITE_SENTRY_DSN=<your public DSN>
VITE_API_URL=http://localhost:5174
- Create
backend/.envwith (example values shown; all are optional since defaults exist):
SENTRY_DSN=<your server DSN>
PORT=5174
# ZapPay (fast & reliable)
PAYMENT_ZAPPAY_MIN_MS=50
PAYMENT_ZAPPAY_MAX_MS=150
PAYMENT_ZAPPAY_FAILURE_RATE=0.05
# GlitchPay (variable & error-prone)
PAYMENT_GLITCHPAY_MIN_MS=200
PAYMENT_GLITCHPAY_MAX_MS=1200
PAYMENT_GLITCHPAY_FAILURE_RATE=0.30
# LagPay (slow)
PAYMENT_LAGPAY_MIN_MS=1200
PAYMENT_LAGPAY_MAX_MS=3000
PAYMENT_LAGPAY_FAILURE_RATE=0.10
- In development, the SDKs use
tracesSampleRate: 1.0. In production, tune sampling.
npm run devThis uses concurrently to run both the frontend and backend in a single terminal.
- Frontend: http://localhost:5173
- Backend: http://localhost:5174
If you prefer to run the services in separate terminals:
# Terminal 1: backend
npm run dev:backend
# -> http://localhost:5174
# Terminal 2: frontend
npm run dev:frontend
# -> http://localhost:5173The frontend connects directly to the backend API (no proxy required).
# Build both frontend and backend
npm run build
# Or build separately
npm run build:frontend
npm run build:backend- Frontend span creation:
frontend/src/App.tsxinsideonCheckoutClick() - Frontend Sentry init:
frontend/src/sentry.ts - Backend spans:
backend/src/server.tsin the/api/checkouthandler usingstartSpan(distributed tracing is propagated automatically), plus a child span for payment
GET /api/products- Returns the product catalog with names, descriptions, prices, and stylingGET /api/payment-config- Returns the effective per-provider configuration derived from environment variablesPOST /api/checkout- Processes orders with simulated payment processingGET /api/health- Health check endpoint
The UI fetches products and payment config on load, displaying provider performance metrics in the cart.
- Perform 10–20 successful checkouts and 3–5 failed ones
- Confirm span attributes appear in Sentry
- Example span-metrics queries:
- p95 of Checkout UI action: filter
op:ui.action, group bycart.item_count - Error rate of server span grouped by
payment.provider - Duration distribution for successful vs failed checkouts
- p95 of Checkout UI action: filter
- Checkout latency (UI): p50/p95/p99 for the
Checkoutspan (op:ui.action).- Group by:
cart.item_count,cart.currencyto see how cart size or currency affects speed.
- Group by:
- Checkout latency (Server): p50/p95/p99 for the
POST /api/checkoutspan (op:http.server).- Group by:
payment.provider,inventory.reservedto spot provider or inventory effects.
- Group by:
- Error rate: Percent of failed spans for UI and server paths.
- Segment by:
payment.provider,cart.item_countto find brittle paths.
- Segment by:
- Throughput (traffic): Spans per minute (SPM) for
Checkoutto understand demand and capacity needs. - Long-tail risk: Count of spans over thresholds (e.g., >1s, >2s) to detect tail latency regressions.
-
UI checkout p95 by cart size
- Filter:
op:ui.action description:Checkout - Columns:
p95(span.duration),count() - Group by:
cart.item_count
- Filter:
-
Server latency by payment provider
- Filter:
op:http.server name:"POST /api/checkout" - Columns:
p95(span.duration),p99(span.duration),count() - Group by:
payment.provider
- Filter:
-
Failure rate by provider
- Filter:
op:http.server name:"POST /api/checkout" - Columns:
count(),count_if(span.status!=ok)(or compare status facets) - Group by:
payment.provider, optionallyinventory.reserved
- Filter:
-
Tail latency (UI) health check
- Filter:
op:ui.action name:Checkout span.duration:>1000ms - Columns:
count()(track over time to watch the >1s tail)
- Filter:
-
Currency segmentation (UI)
- Filter:
op:ui.action name:Checkout - Columns:
p95(span.duration),count() - Group by:
cart.currency
- Filter:
- UI p95: < 1200 ms
- Server p95: < 800 ms
- Error rate: < 2% overall, and < 5% for any single
payment.provider - Tail (>2s) share: trending down and < 1% of checkouts
Tip: Start from the spans table in Trace Explorer, filter by op, add latency percentiles and a group by you care about, then pivot into traces from any outlier row to see concrete examples.
This repo includes two ways to simulate traffic:
- Batch simulator script (recommended for demos): runs users in small batches (default 5 at a time) until a target count is met. Each user randomly adds 1–5 products, selects a random payment method (ZapPay, GlitchPay, LagPay), opens the cart, and attempts checkout. Failures are expected based on provider rates and end that user's flow.
- Playwright Test suite: generates N independent tests, runs with multiple workers, and prints progress using reporters.
npm run dev # start the app (in a separate terminal)
# simulate 500 users in batches of 5 (headful by default for visibility)
HEADLESS=false npm run loadtest
# smaller/local verification run
TOTAL_USERS=10 BATCH_SIZE=5 HEADLESS=false npm run loadtest
Environment variables for tuning:
BASE_URL(defaulthttp://localhost:5173)TOTAL_USERS(default500) — total users to simulateBATCH_SIZE(default5) — browsers run at a timeINTER_BATCH_DELAY_MS(default250)HEADLESS(true|false, defaultfalse)DEBUG_FLOW(true|false, defaultfalse)
Notes:
- The frontend now exposes stable selectors:
#cart-button,#checkout-button, anddata-testid="add-<id>"anddata-testid="payment-<Provider>". - This is a lightweight traffic simulator, not a full benchmark harness.
# auto-starts dev server per playwright.config.ts
BASE_URL=http://localhost:5173 TOTAL_USERS=200 npx playwright test --workers=5 --reporter=list
# or via npm script
npm run test:e2e
The test suite generates TOTAL_USERS tests, each performing the same randomized checkout flow. Expected payment failures are logged but do not fail tests in this demo.