Files
2025-11-29 18:46:44 +08:00

440 lines
8.9 KiB
Markdown

# Sentry Best Practices
## Error Handling Patterns
### 1. Structured Error Handling
Use consistent error handling across the application:
```typescript
import { logger } from '@/lib/logger';
import * as Sentry from '@sentry/nextjs';
export async function performOperation() {
try {
// Operation logic
const result = await someOperation();
logger.info('Operation succeeded', { result });
return result;
} catch (error) {
// Log with context
logger.error('Operation failed', {
error: error as Error,
operation: 'performOperation',
timestamp: new Date().toISOString(),
});
// Re-throw for caller to handle
throw error;
}
}
```
### 2. User-Facing vs Internal Errors
Distinguish between errors shown to users and internal errors:
```typescript
class UserFacingError extends Error {
constructor(
message: string,
public userMessage: string,
public statusCode: number = 400
) {
super(message);
this.name = 'UserFacingError';
}
}
// Usage
try {
await createEntity(data);
} catch (error) {
if (error instanceof UserFacingError) {
// Show to user
return { error: error.userMessage };
}
// Internal error - log to Sentry, show generic message
logger.error('Entity creation failed', { error });
return { error: 'An unexpected error occurred' };
}
```
### 3. Add Context to Errors
Enrich errors with relevant context:
```typescript
export async function updateEntity(entityId: string, data: unknown) {
// Set context for all errors in this scope
Sentry.setContext('entity', {
id: entityId,
type: 'character',
data,
});
try {
const result = await prisma.entity.update({
where: { id: entityId },
data,
});
return result;
} catch (error) {
// Error automatically includes entity context
logger.error('Entity update failed', { error, entityId });
throw error;
}
}
```
## Performance Monitoring
### 1. Custom Transactions
Track important operations:
```typescript
import * as Sentry from '@sentry/nextjs';
export async function generateWorld(params: WorldParams) {
const transaction = Sentry.startTransaction({
name: 'generate-world',
op: 'task',
tags: {
worldType: params.type,
complexity: params.complexity,
},
});
try {
// Track each step
const terrainSpan = transaction.startChild({
op: 'generate-terrain',
description: 'Generate terrain data',
});
const terrain = await generateTerrain(params);
terrainSpan.finish();
const biomesSpan = transaction.startChild({
op: 'generate-biomes',
description: 'Generate biome data',
});
const biomes = await generateBiomes(terrain);
biomesSpan.finish();
transaction.setStatus('ok');
return { terrain, biomes };
} catch (error) {
transaction.setStatus('internal_error');
throw error;
} finally {
transaction.finish();
}
}
```
### 2. Database Query Monitoring
Track slow queries:
```typescript
import { prisma } from '@/lib/prisma';
// Prisma automatically creates spans for queries when OTel is configured
const users = await prisma.user.findMany({
where: { active: true },
include: { profile: true },
});
// Shows up in Sentry as:
// - db.query
// - duration
// - query details
```
### 3. API Call Monitoring
Track external API calls:
```typescript
export async function fetchExternalData(url: string) {
return await Sentry.startSpan(
{
name: 'external-api-call',
op: 'http.client',
attributes: {
'http.url': url,
},
},
async () => {
const response = await fetch(url);
// Add response details to span
Sentry.getCurrentScope().setContext('http', {
status: response.status,
statusText: response.statusText,
});
return response.json();
}
);
}
```
## Quota Management
### 1. Sample Rates
Adjust sampling to control quota usage:
```typescript
Sentry.init({
// Errors
sampleRate: 1.0, // 100% of errors
// Performance monitoring
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
// Session replay
replaysSessionSampleRate: 0.05, // 5% of normal sessions
replaysOnErrorSampleRate: 1.0, // 100% of error sessions
});
```
### 2. Filter Out Noise
Prevent known non-issues from consuming quota:
```typescript
Sentry.init({
beforeSend(event, hint) {
const error = hint.originalException;
// Filter by error message
if (error && typeof error === 'object' && 'message' in error) {
const message = String(error.message);
const ignoredPatterns = [
'ResizeObserver loop',
'Non-Error promise rejection',
'Loading chunk',
'Script error.',
];
if (ignoredPatterns.some((pattern) => message.includes(pattern))) {
return null;
}
}
// Filter by URL
if (event.request?.url?.includes('localhost')) {
return null;
}
return event;
},
});
```
### 3. Inbound Filters
Configure in Sentry dashboard:
- Settings > Inbound Filters
- Filter by:
- Browser version
- Error message
- Release version
- IP address
## User Context
### 1. Set User on Authentication
```typescript
// In middleware or auth utility
import * as Sentry from '@sentry/nextjs';
import { getCurrentUser } from '@/lib/auth/utils';
export async function setUserContext() {
const user = await getCurrentUser();
if (user) {
Sentry.setUser({
id: user.id,
email: user.email,
username: user.name,
});
}
}
```
### 2. Clear User on Logout
```typescript
export async function logout() {
// Clear Sentry context
Sentry.setUser(null);
logger.clearUser();
// Logout logic
await supabase.auth.signOut();
}
```
## Breadcrumbs
Track user journey:
```typescript
// Navigation
Sentry.addBreadcrumb({
category: 'navigation',
message: 'User navigated to world editor',
level: 'info',
data: { worldId: 'world-123' },
});
// User actions
Sentry.addBreadcrumb({
category: 'user.action',
message: 'Created new character',
level: 'info',
data: {
characterName: 'Hero',
worldId: 'world-123',
},
});
// Data changes
Sentry.addBreadcrumb({
category: 'data',
message: 'Updated world settings',
level: 'info',
data: {
worldId: 'world-123',
settings: { theme: 'dark' },
},
});
```
## Source Maps
### 1. Verify Upload
Check source maps are uploaded:
```bash
# Build with source maps
npm run build
# Verify in Sentry dashboard
# Settings > Source Maps > [Release]
```
### 2. Configure Properly
```typescript
// next.config.js
const { withSentryConfig } = require('@sentry/nextjs');
module.exports = withSentryConfig(
nextConfig,
{
// Sentry webpack plugin options
silent: true,
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
},
{
// Sentry SDK options
hideSourceMaps: true, // Don't expose source maps to public
widenClientFileUpload: true, // Upload more files for better stack traces
disableLogger: true, // Reduce noise in build logs
}
);
```
### 3. Troubleshooting
**Source maps not working:**
- Verify `SENTRY_AUTH_TOKEN` is set
- Check build logs for upload errors
- Ensure release version matches between app and Sentry
## Alerting
### 1. Configure Alert Rules
In Sentry dashboard:
- Alerts > Create Alert Rule
- Set conditions (frequency, affected users)
- Choose notification channels (email, Slack, PagerDuty)
**Recommended alerts:**
- High error rate (>10 errors/minute)
- New error type
- Regression (error in new release)
- Performance degradation
### 2. Alert Fatigue
Avoid alert fatigue:
- Use appropriate thresholds
- Filter out noisy errors
- Set up different alerts for different severity
- Use digest emails instead of immediate notifications
## Release Tracking
Associate errors with releases:
```typescript
Sentry.init({
release: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA || 'dev',
environment: process.env.NODE_ENV,
});
```
**Benefits:**
- Track which release introduced errors
- Compare error rates between releases
- Verify if deploy fixed issues
## Testing
### 1. Test Error Capture
```typescript
// Test error handling
export async function testSentry() {
try {
throw new Error('Test error from Sentry setup');
} catch (error) {
logger.error('Testing Sentry integration', { error });
}
}
```
### 2. Verify in Dashboard
After testing:
1. Go to Sentry dashboard
2. Check Issues for test error
3. Verify context, breadcrumbs, and user info
4. Check source maps resolve correctly
## Common Pitfalls
1. **Not filtering development errors** - Always disable Sentry in development or filter out
2. **Missing source maps** - Stack traces are unreadable without them
3. **Not setting user context** - Makes debugging user-specific issues hard
4. **Over-sampling in production** - Wastes quota and money
5. **Ignoring performance monitoring** - Only tracking errors misses slow operations
6. **Not reviewing regularly** - Set aside time weekly to review Sentry issues