For B2B SaaS, observability = difference between 4.7 NPS and 3.2 NPS. An undetected error for 6h = canceling clients. This guide ships the 2026 monitoring stack tested on kolonell.com and 12 client SaaS.
TL;DR
- Sentry: front + back errors, performance monitoring.
- BetterStack (formerly Better Uptime): uptime + log management.
- Plausible / PostHog: privacy-friendly analytics.
- Alerting: Slack + WhatsApp critical only.
Complete monitoring stack
`
[Next.js app]
├── @sentry/nextjs (errors + performance)
├── @vercel/analytics (web vitals)
└── PostHog or Plausible (product analytics)
[Backend]
├── @sentry/node (errors)
├── pino + Better Stack Logs (logs)
└── Prometheus + Grafana (advanced metrics)
[Infrastructure]
├── BetterStack (uptime monitoring)
├── Cloudflare Analytics (edge metrics)
└── Datadog (advanced enterprise)
[Alerting]
├── Slack (standard notifications)
├── WhatsApp (critical only via Twilio)
└── PagerDuty (Enterprise SLA)
`
Step 1 — install Sentry
`bash
pnpm add @sentry/nextjs
npx @sentry/wizard@latest -i nextjs
`
`ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
integrations: [
Sentry.replayIntegration({ maskAllText: false, blockAllMedia: false }),
],
});
`
`ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
beforeSend(event, hint) {
if (event.request?.headers) {
delete event.request.headers['authorization'];
delete event.request.headers['cookie'];
}
return event;
},
});
`
Step 2 — user + tenant context
Critical for B2B SaaS: associate every error with the impacted tenant.
`ts
import * as Sentry from '@sentry/nextjs';
export async function middleware(req: NextRequest) {
const session = await getSession(req);
if (session) {
Sentry.setUser({
id: session.user.id,
email: session.user.email,
});
Sentry.setTag('organization_id', session.organizationId);
Sentry.setTag('plan', session.organization.plan);
}
}
`
With this context: "Error X impacts 14 Pro tenants and 3 Enterprise."
Step 3 — custom capture
`ts
export async function POST(req: Request) {
try {
const data = await processOrder(req);
return Response.json(data);
} catch (error) {
Sentry.withScope(scope => {
scope.setTag('feature', 'order_processing');
scope.setExtra('orderId', data.orderId);
scope.setLevel('error');
Sentry.captureException(error);
});
return Response.json({ error: 'Failed' }, { status: 500 });
}
}
`
Step 4 — performance monitoring
`ts
import { withSentryConfig } from '@sentry/nextjs';
export default withSentryConfig({
// Next config
}, {
silent: true,
org: 'kolonell',
project: 'app',
widenClientFileUpload: true,
reactComponentAnnotation: { enabled: true },
hideSourceMaps: true,
disableLogger: true,
});
Need a professional website?
Kolonell builds websites that attract clients, optimized for the Sénégalese market. Free quote in 2 minutes.
`
Auto-instrumented:
- API route latency
- DB queries (with @sentry/profiling-node)
- React component render time
- Web Vitals (LCP, INP, CLS)
Step 5 — uptime monitoring (BetterStack)
- Go to betterstack.com
- Create monitor: URL = https://app.kolonell.com/api/health
- Frequency: 30 sec
- Regions: Frankfurt, Singapore, US East
- Alerts: Slack + WhatsApp critical
- Public status page (optional)
`ts
export async function GET() {
const checks = await Promise.allSettled([
prisma.$queryRawSELECT 1,
redis.ping(),
fetch('https://api.stripe.com/healthcheck'),
]);
const allOk = checks.every(c => c.status === 'fulfilled');
return Response.json({
status: allOk ? 'healthy' : 'degraded',
timestamp: new Date().toISOString(),
checks: checks.map((c, i) => ({
service: ['db', 'redis', 'stripe'][i],
ok: c.status === 'fulfilled',
})),
}, {
status: allOk ? 200 : 503,
});
}
`
Step 6 — structured logs (Pino + BetterStack)
`ts
import pino from 'pino';
export const logger = pino({
level: process.env.LOG_LEVEL ?? 'info',
transport: process.env.NODE_ENV === 'development'
? { target: 'pino-pretty' }
: undefined,
base: {
env: process.env.NODE_ENV,
service: 'kolonell-app',
},
});
logger.info({ orderId, userId }, 'Order created');
logger.error({ err, orderId }, 'Order processing failed');
`
BetterStack Logs configuration: forward production logs to their SaaS for search + alerting.
Step 7 — product analytics (PostHog)
`tsx
'use client';
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';
if (typeof window !== 'undefined') {
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
person_profiles: 'identified_only',
});
}
export function PHProvider({ children }) {
return
}
`
`tsx
import { usePostHog } from 'posthog-js/react';
const posthog = usePostHog();
posthog.capture('subscription_started', {
plan: 'pro',
amount: 25000,
currency: 'XOF',
});
`
PostHog free up to 1M events/month. Beyond $0.0003/event.
Step 8 — critical WhatsApp alerting
`ts
async function alertCritical(message: string, context: any) {
await sendSlack({ channel: '#critical', message, context });
if (context.severity === 'critical') {
await sendWhatsApp({
to: process.env.ONCALL_PHONE!,
template: 'critical_alert',
params: [message, context.timestamp],
});
}
}
// Triggers
// - Site down >2 min
// - Database unreachable
// - Stripe webhook failed >5 min
// - Error rate >5% over 5 min
`
Typical monthly costs
| Tool | Free tier | SME plan | Startup scale |
|---|---|---|---|
| Sentry | 5K errors/month | $26/mo (50K errors) | $80/mo (250K) |
| BetterStack | 10 monitors | $25/mo | $80/mo |
| PostHog | 1M events | $0 | $50-200/mo (10M+) |
| Plausible | 30d trial | $9/mo (10K) | $69/mo (1M) |
| Slack | Free | $7/mo/user | $7/mo/user |
| Monthly total | $0 | $50-100 | $200-400 |
Real case — Dakar SaaS (350 tenants)
| Before monitoring | After 6 months | |
|---|---|---|
| MTTR (mean time to resolve) | 4-8 hours | 25 min |
| Unreported bugs/month | 12-15 | 1-2 |
| Customer-reported issues | 22% | 3% |
| Uptime | 99.2% | 99.95% |
| NPS | 38 | 67 |
Monitoring ROI: ~+$15K MRR/month thanks to lowered churn.
Common pitfalls
- Trace too much = noise — sample 10% in prod, 100% in dev.
- No PII masking — emails, IDs in logs = GDPR data leak.
- Alarmist alerts — a simple 500 doesn't wake the team at 3am. Critical = real critical.
- No Sentry → ticket link — auto-create Linear/Notion on recurring errors.
- Monitoring never reviewed — weekly dashboard review.
FAQ
Q: Datadog vs Sentry?
A: Sentry = errors-first, Datadog = ops-first. For B2B SaaS startup, Sentry enough. Datadog from Enterprise + complex infrastructure.
Q: Self-host Sentry?
A: Sentry self-hosted free but 4-8 GB RAM dedicated server. At solo SaaS scale: SaaS Sentry profitable.
Q: Minimum metrics?
A: Error rate, P95 latency, Uptime, Stripe webhook success rate, DB connection pool.
Conclusion
B2B SaaS monitoring stack 2026 = $50-200/month but prevents 10-100× more churn. 1-2 weeks initial setup. 1-2h/week ongoing maintenance. Mandatory 2026 standard for serious SaaS.
Mohamed Bah
Fondateur, Kolonell
Passionate about digital and entrepreneurship in Africa, Mohamed has been helping Sénégalese businesses with their digital transformation since 2020. Founder of Kolonell, he believes every SME deserves a professional and accessible online présence.