Skip to main content

Overview

Kuest integrates with Sentry for comprehensive error tracking, performance monitoring, and debugging. This guide covers monitoring setup, health checks, and troubleshooting.

Sentry Integration

Configuration

Sentry is configured in multiple files for complete coverage:
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.1,      // 10% of transactions
  enableLogs: true,             // Capture console logs
})
// src/instrumentation.ts
import * as Sentry from '@sentry/nextjs'

export async function register() {
  await import('../sentry.server.config')
}

export const onRequestError = Sentry.captureRequestError

Environment Variables

Configure Sentry in your .env file:
# Required
SENTRY_DSN="https://[key]@[org].ingest.sentry.io/[project]"

# Optional (for source maps and releases)
SENTRY_ORG="your-org-slug"
SENTRY_PROJECT="your-project-slug"
SENTRY_AUTH_TOKEN="your-auth-token"
1

Create Sentry Project

  1. Sign up at sentry.io
  2. Create a new project (choose Next.js)
  3. Copy your DSN from project settings
2

Configure Environment

Add your SENTRY_DSN to .env:
SENTRY_DSN="https://[email protected]/7890123"
3

Deploy with Source Maps

Configure authentication for source map uploads:
SENTRY_AUTH_TOKEN="your-token-from-settings"
Source maps help debug minified production code by showing original source locations.

Error Tracking

Automatic Error Capture

Sentry automatically captures:

Unhandled Exceptions

Runtime errors that crash the application

Promise Rejections

Unhandled rejected promises

Server Errors

API route failures and server-side errors

Client Errors

Browser-side JavaScript errors

Manual Error Reporting

Capture errors explicitly when needed:
import * as Sentry from '@sentry/nextjs'

try {
  await riskyOperation()
} catch (error) {
  // Log to console
  console.error('Operation failed:', error)
  
  // Send to Sentry with context
  Sentry.captureException(error, {
    tags: {
      operation: 'riskyOperation',
      severity: 'high'
    },
    extra: {
      userId: user.id,
      marketId: market.id,
      timestamp: Date.now()
    }
  })
  
  // Handle gracefully
  return { success: false, error: 'Operation failed' }
}

Error Context

Enrich errors with additional context:
// Set user context
Sentry.setUser({
  id: user.id,
  email: user.email,
  username: user.username,
  wallet: user.address,
})

// Add breadcrumbs
Sentry.addBreadcrumb({
  category: 'trading',
  message: 'User initiated trade',
  level: 'info',
  data: {
    marketId: trade.marketId,
    amount: trade.amount,
    outcome: trade.outcome,
  }
})

// Set tags for filtering
Sentry.setTag('market_type', 'binary')
Sentry.setTag('network', 'polygon')
Sentry.setTag('feature', 'trading')

Health Checks

API Health Endpoint

While not explicitly implemented in the source, you should add a health check endpoint:
// src/app/api/health/route.ts
import { NextResponse } from 'next/server'
import { db } from '@/lib/drizzle'

export const dynamic = 'force-dynamic'
export const runtime = 'edge'

export async function GET() {
  const startTime = Date.now()
  
  try {
    // Check database connection
    await db.execute('SELECT 1')
    
    const responseTime = Date.now() - startTime
    
    return NextResponse.json({
      status: 'healthy',
      timestamp: new Date().toISOString(),
      responseTime: `${responseTime}ms`,
      services: {
        database: 'connected',
        storage: process.env.S3_BUCKET ? 'configured' : 'not configured',
        sentry: process.env.SENTRY_DSN ? 'enabled' : 'disabled',
      }
    })
  } catch (error) {
    return NextResponse.json(
      {
        status: 'unhealthy',
        error: error instanceof Error ? error.message : 'Unknown error',
        timestamp: new Date().toISOString(),
      },
      { status: 503 }
    )
  }
}

Cron Job Monitoring

Monitor automated tasks:
// Example: Resolution sync monitoring
export async function GET(request: Request) {
  const auth = request.headers.get('authorization')
  if (!isCronAuthorized(auth, process.env.CRON_SECRET)) {
    Sentry.captureMessage('Unauthorized cron attempt', {
      level: 'warning',
      tags: { cron: 'resolution_sync' }
    })
    return NextResponse.json({ error: 'Unauthenticated.' }, { status: 401 })
  }

  try {
    const syncResult = await syncResolutions()
    
    // Log success metrics
    Sentry.captureMessage('Resolution sync completed', {
      level: 'info',
      tags: { cron: 'resolution_sync' },
      extra: {
        fetched: syncResult.fetchedCount,
        processed: syncResult.processedCount,
        skipped: syncResult.skippedCount,
        errors: syncResult.errors.length,
        timeLimitReached: syncResult.timeLimitReached,
      }
    })
    
    return NextResponse.json({
      success: true,
      ...syncResult
    })
  } catch (error: any) {
    // Capture cron failure
    Sentry.captureException(error, {
      tags: { cron: 'resolution_sync' },
      level: 'error'
    })
    
    return NextResponse.json(
      { success: false, error: error.message },
      { status: 500 }
    )
  }
}

Database Health

Monitor database performance:
// Track slow queries
import { db } from '@/lib/drizzle'

async function trackQuery<T>(
  queryName: string,
  queryFn: () => Promise<T>
): Promise<T> {
  const startTime = performance.now()
  
  try {
    const result = await queryFn()
    const duration = performance.now() - startTime
    
    // Log slow queries
    if (duration > 1000) {
      Sentry.captureMessage(`Slow query: ${queryName}`, {
        level: 'warning',
        tags: { query: queryName },
        extra: { duration: `${duration}ms` }
      })
    }
    
    return result
  } catch (error) {
    Sentry.captureException(error, {
      tags: { query: queryName },
      extra: { duration: `${performance.now() - startTime}ms` }
    })
    throw error
  }
}

// Usage
const events = await trackQuery(
  'getActiveEvents',
  () => EventRepository.getActiveEvents()
)

Performance Monitoring

Transaction Tracking

Track performance of key operations:
import * as Sentry from '@sentry/nextjs'

// Start a transaction
const transaction = Sentry.startTransaction({
  name: 'Market Creation',
  op: 'market.create'
})

try {
  // Track sub-operations
  const slugCheckSpan = transaction.startChild({
    op: 'validation',
    description: 'Check slug uniqueness'
  })
  await checkSlugUniqueness(slug)
  slugCheckSpan.finish()
  
  const aiValidationSpan = transaction.startChild({
    op: 'ai',
    description: 'AI content validation'
  })
  await validateContentWithAI(content)
  aiValidationSpan.finish()
  
  const blockchainSpan = transaction.startChild({
    op: 'blockchain',
    description: 'Deploy to blockchain'
  })
  await deployMarket(marketData)
  blockchainSpan.finish()
  
  transaction.setStatus('ok')
} catch (error) {
  transaction.setStatus('internal_error')
  Sentry.captureException(error)
} finally {
  transaction.finish()
}

Custom Metrics

Track business metrics:
// Track user actions
Sentry.addBreadcrumb({
  category: 'user.action',
  message: 'User placed trade',
  level: 'info',
  data: {
    tradeValue: trade.amount,
    market: trade.marketId,
    outcome: trade.outcome
  }
})

// Track API performance
Sentry.addBreadcrumb({
  category: 'api.call',
  message: 'Kuest API order placement',
  level: 'info',
  data: {
    endpoint: '/orders',
    responseTime: `${duration}ms`,
    status: response.status
  }
})

Logging Best Practices

Structured Logging

Use consistent log formats:
// Good: Structured with context
console.log('Market created', {
  marketId: market.id,
  creator: market.creator,
  endDate: market.endDate,
  type: market.type
})

// Bad: Unstructured string
console.log(`Created market ${market.id}`)

Log Levels

Use appropriate log levels:
// Info: Normal operations
console.log('User logged in', { userId: user.id })

// Warning: Recoverable issues
console.warn('API rate limit approaching', {
  remaining: rateLimitRemaining,
  resetAt: rateLimitReset
})

// Error: Failures requiring attention
console.error('Failed to sync resolution', {
  error: error.message,
  marketId: market.id
})

Sensitive Data

Never log sensitive information:
// Bad: Logs API keys
console.log('API key:', process.env.KUEST_API_KEY)

// Good: Logs masked version
console.log('API key configured:', Boolean(process.env.KUEST_API_KEY))

// Bad: Logs user email
console.log('User email:', user.email)

// Good: Logs user ID only
console.log('User:', { id: user.id })

Alerting

Sentry Alerts

Configure alerts in Sentry dashboard:
1

Error Rate Alerts

Alert when error rate exceeds threshold:
  • Trigger: > 10 errors in 5 minutes
  • Notify: Email, Slack, PagerDuty
2

Performance Degradation

Alert on slow transactions:
  • Trigger: P95 latency > 3 seconds
  • Notify: Slack channel
3

Cron Job Failures

Alert when scheduled tasks fail:
  • Trigger: Cron job returns error status
  • Notify: On-call engineer
4

User Impact

Alert on issues affecting multiple users:
  • Trigger: Same error from > 10 users
  • Notify: Team lead

Custom Alerts

Implement application-level alerts:
// Monitor critical operations
async function monitorCriticalOperation(
  operationName: string,
  operationFn: () => Promise<void>
) {
  try {
    await operationFn()
  } catch (error) {
    // Send immediate alert
    Sentry.captureException(error, {
      level: 'fatal',
      tags: { critical: 'true', operation: operationName }
    })
    
    // Additional notification channels
    await sendSlackAlert({
      channel: '#critical-alerts',
      message: `Critical operation failed: ${operationName}`,
      error: error.message
    })
    
    throw error
  }
}

// Usage
await monitorCriticalOperation(
  'resolution_sync',
  () => syncResolutions()
)

Debugging

Error Context

Access full error context in Sentry:
  1. Stack Trace: See where the error occurred
  2. Breadcrumbs: User actions leading to the error
  3. User Context: Who experienced the issue
  4. Environment: Browser, OS, app version
  5. Request Data: URL, headers, body

Source Maps

Debug minified production code:
# Sentry automatically uploads source maps during build
npm run build

# Verify source maps in Sentry dashboard
# Navigate to: Settings > Projects > [Your Project] > Source Maps

Replay Sessions

Enable session replay for visual debugging:
// sentry.client.config.ts
Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  integrations: [
    new Sentry.Replay({
      maskAllText: true,
      blockAllMedia: true,
    }),
  ],
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
})

Monitoring Checklist

  • Check Sentry dashboard for new errors
  • Review error trends and patterns
  • Verify cron jobs executed successfully
  • Check API response times
  • Monitor database query performance
  • Analyze error rate trends
  • Review most common errors
  • Check performance regressions
  • Verify alert configurations
  • Update monitoring thresholds
  • Archive old Sentry issues
  • Review and update alert rules
  • Analyze monitoring costs
  • Update documentation
  • Test monitoring infrastructure

Troubleshooting

Sentry Not Capturing Errors

Issue: Errors occur but don’t appear in Sentry. Solution:
  1. Verify SENTRY_DSN is set in environment variables
  2. Check Sentry initialization in instrumentation.ts
  3. Ensure errors are not caught without re-throwing
  4. Verify network connectivity to Sentry servers
  5. Check browser console for Sentry initialization errors

Source Maps Not Working

Issue: Stack traces show minified code. Solution:
  1. Verify SENTRY_AUTH_TOKEN is configured
  2. Check build logs for source map upload
  3. Ensure sentry.properties file exists
  4. Verify organization and project names match
  5. Re-upload source maps manually if needed:
    npx @sentry/cli sourcemaps upload --org=your-org --project=your-project .next
    

High Error Volume

Issue: Too many errors flooding Sentry. Solution:
  1. Identify and fix the most common error first
  2. Add error grouping rules in Sentry
  3. Implement rate limiting on error capture
  4. Filter out known non-critical errors
  5. Use beforeSend to sample errors:
    Sentry.init({
      beforeSend(event) {
        // Only send 10% of low-severity errors
        if (event.level === 'warning' && Math.random() > 0.1) {
          return null
        }
        return event
      }
    })
    

Performance Overhead

Issue: Monitoring impacting app performance. Solution:
  1. Reduce tracesSampleRate (e.g., 0.01 for 1%)
  2. Disable session replay on low-end devices
  3. Limit breadcrumb collection
  4. Use edge runtime for critical paths
  5. Implement sampling strategies

Sentry Documentation

Official Sentry Next.js integration guide

Admin Panel

Monitor platform operations

Resolution Sync

Monitor resolution synchronization

Deployment

Production deployment best practices

Build docs developers (and LLMs) love