@kodeme-io/next-core-monitoring
v0.8.4
Published
Error tracking, monitoring, and observability for Next.js + Odoo applications
Maintainers
Readme
@kodeme-io/next-core-monitoring
Error tracking, monitoring, and observability for Next.js + Odoo applications
Production-grade monitoring solution with Sentry integration, Error Boundaries, structured logging, and performance tracking.
Features
- ✅ Error Tracking - Sentry integration for client, server, and edge runtimes
- ✅ Error Boundaries - React Error Boundaries to prevent white screens
- ✅ Structured Logging - Production-safe logging with levels and context
- ✅ Session Replay - See exactly what users did before errors
- ✅ Performance Monitoring - Track Web Vitals and API response times
- ✅ User Context - Know which users are affected by errors
- ✅ Breadcrumbs - See the trail of events leading to errors
- ✅ Source Maps - Readable stack traces in production
- ✅ Zero Config - Works out of the box with sensible defaults
Installation
pnpm add @kodeme-io/next-core-monitoring
# or
npm install @kodeme-io/next-core-monitoring
# or
yarn add @kodeme-io/next-core-monitoringQuick Start (5 minutes)
1. Get Sentry DSN
Sign up at sentry.io (free tier available) and get your DSN.
2. Add Environment Variables
# .env.local
NEXT_PUBLIC_SENTRY_DSN=https://[email protected]/...
NEXT_PUBLIC_APP_VERSION=1.0.0
# Optional
NEXT_PUBLIC_LOG_LEVEL=warn # debug, info, warn, error, fatal
NEXT_PUBLIC_SENTRY_DEBUG=false3. Initialize Monitoring
// src/app/layout.tsx
import { initMonitoring, ErrorBoundary } from '@kodeme-io/next-core-monitoring'
// Initialize once at app startup
initMonitoring({
appName: 'my-app',
version: process.env.NEXT_PUBLIC_APP_VERSION || '1.0.0',
environment: process.env.NODE_ENV,
getUserContext: () => {
// Return current user info
return {
id: user?.id,
email: user?.email,
username: user?.name,
}
},
})
export default function RootLayout({ children }) {
return (
<html>
<body>
<ErrorBoundary>
{children}
</ErrorBoundary>
</body>
</html>
)
}4. Add Sentry Config Files
// sentry.client.config.ts
import { initSentryClient } from '@kodeme-io/next-core-monitoring/sentry/client'
initSentryClient()// sentry.server.config.ts
import { initSentryServer } from '@kodeme-io/next-core-monitoring/sentry/server'
initSentryServer()// sentry.edge.config.ts
import { initSentryEdge } from '@kodeme-io/next-core-monitoring/sentry/edge'
initSentryEdge()That's it! All errors are now being tracked.
Usage
Error Tracking
Automatic Error Capture
All unhandled errors are automatically captured:
- React component errors
- Unhandled Promise rejections
- Console errors
- Network errors
- API errors
Manual Error Capture
import { captureClientException, captureClientMessage } from '@kodeme-io/next-core-monitoring'
try {
await riskyOperation()
} catch (error) {
captureClientException(error, {
tags: { component: 'CustomerForm' },
extra: { customerId: 123 }
})
}
// Info/warning messages
captureClientMessage('Unusual user action', 'warning', {
userId: 123,
action: 'bulk-delete'
})Error Boundaries
Component Error Boundary
import { ErrorBoundary } from '@kodeme-io/next-core-monitoring'
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<CustomerList />
</ErrorBoundary>Route Error Boundary
import { RouteErrorBoundary } from '@kodeme-io/next-core-monitoring'
<RouteErrorBoundary>
<Router />
</RouteErrorBoundary>Chunk Loading Error Boundary
import { ChunkErrorBoundary } from '@kodeme-io/next-core-monitoring'
<ChunkErrorBoundary>
<LazyComponent />
</ChunkErrorBoundary>Custom Fallback
<ErrorBoundary
fallback={(error, errorInfo) => (
<div>
<h1>Oops!</h1>
<p>{error.message}</p>
<button onClick={() => window.location.reload()}>
Reload
</button>
</div>
)}
>
<MyComponent />
</ErrorBoundary>Structured Logging
import { logger } from '@kodeme-io/next-core-monitoring'
// Basic logging
logger.debug('Debug info')
logger.info('User logged in', { userId: 123 })
logger.warn('High memory usage', { memory: 1024 })
logger.error('API failed', error, { endpoint: '/api/users' })
logger.fatal('Database connection lost', error)
// With context
const userLogger = logger.withContext({ userId: 123, requestId: 'abc' })
userLogger.info('Processing request')
// All logs will include userId and requestId
// Performance timing
logger.time('query')
const result = await database.query()
logger.timeEnd('query') // Logs: "query: 123ms"User Context
import { setUser } from '@kodeme-io/next-core-monitoring'
// Set user after login
setUser({
id: user.id,
email: user.email,
username: user.name
})
// Clear user on logout
setUser(null)Breadcrumbs
import { addBreadcrumb } from '@kodeme-io/next-core-monitoring'
addBreadcrumb({
message: 'User clicked submit button',
category: 'ui',
level: 'info',
data: { formId: 'customer-form' }
})Tags & Context
import { setTag, setContext } from '@kodeme-io/next-core-monitoring'
// Tags for grouping
setTag('feature', 'checkout')
setTag('version', '2.0.0')
// Custom context
setContext('customer', {
id: 123,
name: 'ACME Corp',
plan: 'enterprise'
})API Monitoring
import { withMonitoring } from '@kodeme-io/next-core-monitoring'
// Wrap API routes
export const GET = withMonitoring(
async (request: Request) => {
const data = await fetchData()
return Response.json(data)
},
{
name: 'GET /api/customers',
tags: { api: 'customers' }
}
)React Hooks 🆕
useWebVitals - Performance Monitoring Hook
Track Core Web Vitals with automatic cleanup and scoring:
import { useWebVitals } from '@kodeme-io/next-core-monitoring'
function PerformanceDashboard() {
const { metrics, ratings, score, grade } = useWebVitals({
onMetric: (metric) => {
if (metric.rating === 'poor') {
// Show performance warning
showPerformanceWarning(metric)
}
},
onLCPCollect: (metric) => {
// Track loading performance
analytics.track('LCP', { value: metric.value, rating: metric.rating })
}
})
return (
<div>
<h2>Performance Score: {score}/100 (Grade: {grade})</h2>
<div>LCP: {metrics.get('LCP')?.value}ms ({ratings.LCP})</div>
<div>FID: {metrics.get('FID')?.value}ms ({ratings.FID})</div>
<div>CLS: {metrics.get('CLS')?.value} ({ratings.CLS})</div>
</div>
)
}useLogger - Component Logging Hook
Structured logging with automatic component context:
import { useLogger } from '@kodeme-io/next-core-monitoring'
function UserProfile({ userId }: { userId: string }) {
const { logger, info, error } = useLogger({
componentName: 'UserProfile',
context: { userId },
logLifecycle: true, // Log mount/unmount
trackPerformance: true // Track render performance
})
const [user, setUser] = useState(null)
useEffect(() => {
logger.time('fetch-user')
fetchUser(userId)
.then(setUser)
.catch(err => error('Failed to fetch user', err))
.finally(() => logger.timeEnd('fetch-user'))
}, [userId])
return <div>{user?.name}</div>
}useErrorBoundary - Error Handling Hook
Error boundary with automatic recovery and error counting:
import { useErrorBoundary } from '@kodeme-io/next-core-monitoring'
function RiskyComponent() {
const { error, ErrorBoundary, reset, errorCount } = useErrorBoundary({
onError: (error, errorInfo) => {
analytics.track('component_error', {
component: 'RiskyComponent',
error: error.message
})
},
maxErrors: 3,
autoResetDelay: 5000,
context: { feature: 'user-management' }
})
if (error) {
return (
<div>
<p>Something went wrong (Error #{errorCount})</p>
<button onClick={reset}>Try Again</button>
</div>
)
}
return (
<ErrorBoundary>
<ComponentThatMightFail />
</ErrorBoundary>
)
}useMonitoring - All-in-One Hook
Complete monitoring setup with Web Vitals, logging, and error boundaries:
import { useMonitoring } from '@kodeme-io/next-core-monitoring'
function App({ user }: { user: User | null }) {
const monitoring = useMonitoring({
componentName: 'App',
user,
context: { version: '1.0.0', environment: process.env.NODE_ENV },
tags: { feature: 'main-app' },
webVitals: {
onMetric: (metric) => {
if (metric.rating === 'poor') {
// Show performance notification
}
}
},
logger: {
logLifecycle: true,
trackPerformance: true
},
errorBoundary: {
maxErrors: 3,
onError: (error, errorInfo) => {
// Custom error handling
}
}
})
const { ErrorBoundary } = monitoring.errorBoundary
return (
<ErrorBoundary>
<YourAppContent />
</ErrorBoundary>
)
}Testing 🆕
Comprehensive test suite with Jest and React Testing Library:
# Run all tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage
pnpm test:coverageTest Examples
// Example test for useLogger hook
import { renderHook } from '@testing-library/react'
import { useLogger } from '@kodeme-io/next-core-monitoring'
describe('useLogger', () => {
it('should create logger with component context', () => {
const { result } = renderHook(() =>
useLogger({
componentName: 'TestComponent',
context: { testId: '123' }
})
)
expect(result.current.logger).toBeDefined()
expect(typeof result.current.info).toBe('function')
})
})Testing 🆕
This package includes a comprehensive test suite covering:
- ✅ Unit Tests - All core modules, hooks, and utilities
- ✅ Integration Tests - React hooks and error boundaries
- ✅ Mocking - Complete test setup with Jest mocks
- ✅ Coverage - High test coverage for production reliability
Test Structure
src/__tests__/
├── setup.ts # Test configuration and mocks
├── config.test.ts # Configuration module tests
├── logger.test.ts # Logger functionality tests
├── sentry.test.ts # Sentry integration tests
├── interceptors.test.ts # API interceptor tests
├── performance.test.ts # Performance monitoring tests
├── error-boundaries.test.tsx # Error boundary component tests
└── hooks.test.tsx # React hooks testsRunning Tests
# Install dependencies
pnpm install
# Run all tests
pnpm test
# Run tests with coverage report
pnpm test:coverage
# Run tests in watch mode during development
pnpm test:watchTest Configuration
- Framework: Jest with React Testing Library
- Environment: JSDOM for React component testing
- Mocking: Comprehensive mocks for Sentry, Performance API, and browser APIs
- Coverage: Configured for high coverage thresholds
Configuration
Environment Variables
# Required
NEXT_PUBLIC_SENTRY_DSN=https://[email protected]/...
# Optional
NEXT_PUBLIC_APP_VERSION=1.0.0
NEXT_PUBLIC_LOG_LEVEL=warn # debug, info, warn, error, fatal
NEXT_PUBLIC_SENTRY_DEBUG=false # Enable debug logging
NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE=0.1 # 10% of transactions
NEXT_PUBLIC_SENTRY_REPLAYS_SESSION_SAMPLE_RATE=0.1 # 10% of sessions
NEXT_PUBLIC_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE=1.0 # 100% of errorsAdvanced Configuration
import { initMonitoring } from '@kodeme-io/next-core-monitoring'
initMonitoring({
appName: 'my-app',
version: '1.0.0',
environment: 'production',
// Sentry configuration
sentry: {
enabled: true,
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
},
// Logger configuration
logger: {
enabled: true,
level: LogLevel.WARN,
console: false, // Disable console in production
},
// User context provider
getUserContext: () => {
const user = getCurrentUser() // Your auth function
if (!user) return null
return {
id: user.id,
email: user.email,
username: user.name,
// Add custom fields
role: user.role,
plan: user.subscription?.plan,
}
},
})Best Practices
1. Always Use Error Boundaries
// ❌ Bad - no error boundary
<CustomerList />
// ✅ Good - wrapped in error boundary
<ErrorBoundary>
<CustomerList />
</ErrorBoundary>2. Add Context to Errors
// ❌ Bad - no context
throw new Error('Failed to save')
// ✅ Good - with context
captureClientException(error, {
tags: { feature: 'customer-management' },
extra: { customerId, formData }
})3. Use Structured Logging
// ❌ Bad - console.log
console.log('User logged in:', userId)
// ✅ Good - structured logger
logger.info('User logged in', { userId, timestamp: Date.now() })4. Set User Context Early
// In your auth callback
async function onLogin(user) {
setUser({
id: user.id,
email: user.email,
username: user.name
})
// Now all errors will include user info
}5. Use Breadcrumbs for User Actions
function handleFormSubmit() {
addBreadcrumb({
message: 'User submitted customer form',
category: 'form',
data: { formId: 'customer-form', fields: Object.keys(formData) }
})
// Submit form...
}Sentry Setup
1. Create Account
Go to sentry.io and create an account (free tier available).
2. Create Project
- Click "Create Project"
- Select "Next.js"
- Copy your DSN
3. Configure Alerts
- Go to Project Settings → Alerts
- Create alert rules:
- "New error occurs" → Notify via Slack/Email
- "Error rate exceeds threshold" → Notify team
4. Set Up Source Maps
Add to next.config.js:
const { withSentryConfig } = require('@sentry/nextjs')
module.exports = withSentryConfig(
{
// Your Next.js config
},
{
// Sentry webpack plugin options
silent: true,
org: 'your-org',
project: 'your-project',
},
{
// Upload source maps on build
hideSourceMaps: true,
disableLogger: true,
}
)Troubleshooting
Errors Not Showing in Sentry
- Check DSN is correct
- Check environment variables are set
- Check browser console for Sentry errors
- Enable debug mode:
NEXT_PUBLIC_SENTRY_DEBUG=true
Source Maps Not Working
- Ensure
withSentryConfigis innext.config.js - Set
SENTRY_AUTH_TOKENenvironment variable - Run build:
pnpm build - Check Sentry dashboard → Settings → Source Maps
Logs Not Appearing
- Check log level:
NEXT_PUBLIC_LOG_LEVEL - Check console transport is enabled
- Check browser console is not filtered
Cost
Sentry Free Tier
- 5,000 errors/month
- 1 user
- 30-day retention
- Cost: $0
Sentry Team Plan (Recommended)
- 50,000 errors/month
- 50,000 performance transactions/month
- 500 session replays/month
- Unlimited users
- 90-day retention
- Cost: $26/month
For Multiple Apps
- ~8,333 errors/month per app (6 apps)
- Still fits in Team plan
- Total: $26/month
Examples
See docs/examples/monitoring/ for complete examples:
- Basic setup
- Custom error handling
- API monitoring
- Performance tracking
License
MIT
Support
- Documentation: https://github.com/abc-food/next-core
- Issues: https://github.com/abc-food/next-core/issues
- Sentry Docs: https://docs.sentry.io/platforms/javascript/guides/nextjs/
