npm package: @kodeme-io/next-core-monitoring
Description: Error tracking, monitoring, and observability for Next.js + Odoo applications
Author: ABC Food
Latest version: 0.8.4

# @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

```bash
pnpm add @kodeme-io/next-core-monitoring
# or
npm install @kodeme-io/next-core-monitoring
# or
yarn add @kodeme-io/next-core-monitoring
```

---

## Quick Start (5 minutes)

### 1. Get Sentry DSN

Sign up at [sentry.io](https://sentry.io) (free tier available) and get your DSN.

### 2. Add Environment Variables

```bash
# .env.local
NEXT_PUBLIC_SENTRY_DSN=https://...@sentry.io/...
NEXT_PUBLIC_APP_VERSION=1.0.0

# Optional
NEXT_PUBLIC_LOG_LEVEL=warn  # debug, info, warn, error, fatal
NEXT_PUBLIC_SENTRY_DEBUG=false
```

### 3. Initialize Monitoring

```typescript
// 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

```typescript
// sentry.client.config.ts
import { initSentryClient } from '@kodeme-io/next-core-monitoring/sentry/client'

initSentryClient()
```

```typescript
// sentry.server.config.ts
import { initSentryServer } from '@kodeme-io/next-core-monitoring/sentry/server'

initSentryServer()
```

```typescript
// 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

```typescript
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

```typescript
import { ErrorBoundary } from '@kodeme-io/next-core-monitoring'

<ErrorBoundary fallback={<div>Something went wrong</div>}>
  <CustomerList />
</ErrorBoundary>
```

#### Route Error Boundary

```typescript
import { RouteErrorBoundary } from '@kodeme-io/next-core-monitoring'

<RouteErrorBoundary>
  <Router />
</RouteErrorBoundary>
```

#### Chunk Loading Error Boundary

```typescript
import { ChunkErrorBoundary } from '@kodeme-io/next-core-monitoring'

<ChunkErrorBoundary>
  <LazyComponent />
</ChunkErrorBoundary>
```

#### Custom Fallback

```typescript
<ErrorBoundary
  fallback={(error, errorInfo) => (
    <div>
      <h1>Oops!</h1>
      <p>{error.message}</p>
      <button onClick={() => window.location.reload()}>
        Reload
      </button>
    </div>
  )}
>
  <MyComponent />
</ErrorBoundary>
```

### Structured Logging

```typescript
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

```typescript
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

```typescript
import { addBreadcrumb } from '@kodeme-io/next-core-monitoring'

addBreadcrumb({
  message: 'User clicked submit button',
  category: 'ui',
  level: 'info',
  data: { formId: 'customer-form' }
})
```

### Tags & Context

```typescript
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

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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:

```bash
# Run all tests
pnpm test

# Run tests in watch mode
pnpm test:watch

# Run tests with coverage
pnpm test:coverage
```

#### Test Examples

```typescript
// 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 tests
```

### Running Tests

```bash
# 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:watch
```

### Test 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

```bash
# Required
NEXT_PUBLIC_SENTRY_DSN=https://...@sentry.io/...

# 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 errors
```

### Advanced Configuration

```typescript
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

```typescript
// ❌ Bad - no error boundary
<CustomerList />

// ✅ Good - wrapped in error boundary
<ErrorBoundary>
  <CustomerList />
</ErrorBoundary>
```

### 2. Add Context to Errors

```typescript
// ❌ 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

```typescript
// ❌ 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

```typescript
// 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

```typescript
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](https://sentry.io) and create an account (free tier available).

### 2. Create Project

1. Click "Create Project"
2. Select "Next.js"
3. Copy your DSN

### 3. Configure Alerts

1. Go to Project Settings → Alerts
2. 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`:

```javascript
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

1. Check DSN is correct
2. Check environment variables are set
3. Check browser console for Sentry errors
4. Enable debug mode: `NEXT_PUBLIC_SENTRY_DEBUG=true`

### Source Maps Not Working

1. Ensure `withSentryConfig` is in `next.config.js`
2. Set `SENTRY_AUTH_TOKEN` environment variable
3. Run build: `pnpm build`
4. Check Sentry dashboard → Settings → Source Maps

### Logs Not Appearing

1. Check log level: `NEXT_PUBLIC_LOG_LEVEL`
2. Check console transport is enabled
3. 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/](../../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](https://github.com/abc-food/next-core)
- Issues: [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/](https://docs.sentry.io/platforms/javascript/guides/nextjs/)