npm package: @hunghoangdev123/sentry-sdk
Description: A comprehensive Sentry SDK wrapper with full configuration options support via DSN and environment variables
Author: Hung Hoang
Latest version: 1.0.0

# @hunghoang/sentry-sdk

🚀 A comprehensive Sentry SDK wrapper for Node.js with full configuration support via DSN and environment variables.

## ✨ Features

- 🔧 **Full Configuration Support**: All Sentry options available through code or environment variables
- 🌍 **Environment Variable Support**: Configure everything via `.env` file
- 📝 **TypeScript Support**: Full type definitions included
- 🎯 **Easy to Use**: Simple initialization with sensible defaults
- 🔒 **Production Ready**: Battle-tested configuration patterns
- 📦 **Lightweight**: Minimal dependencies
- 🎨 **Flexible**: Override any option programmatically

## 📦 Installation

```bash
npm install @hunghoang/sentry-sdk @sentry/node
```

Or with yarn:

```bash
yarn add @hunghoang/sentry-sdk @sentry/node
```

## ✅ Compatibility

- ✅ **Node.js >= 12.0.0** (tested on v20.18.3)
- ✅ **JavaScript** (no TypeScript required)
- ✅ **CommonJS** (`require`)
- ✅ **ES Modules** (`import`)
- ✅ **TypeScript** (full type definitions included)

See [COMPATIBILITY.md](./COMPATIBILITY.md) for detailed JavaScript usage examples.

## 🚀 Quick Start

### Basic Usage

```typescript
import { initSentry } from '@hunghoang/sentry-sdk';

// Initialize with DSN from environment variable SENTRY_DSN
initSentry();
```

### With Custom Options

```typescript
import { initSentry } from '@hunghoang/sentry-sdk';

initSentry({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  environment: 'production',
  release: 'my-app@1.0.0',
  tracesSampleRate: 1.0,
  debug: false,
});
```

### Express.js Example

```typescript
import express from 'express';
import { initSentry, Sentry } from '@hunghoang/sentry-sdk';

const app = express();

// Initialize Sentry before any routes
initSentry({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 1.0,
});

// Request handler must be the first middleware
app.use(Sentry.Handlers.requestHandler());

// Tracing handler for performance monitoring
app.use(Sentry.Handlers.tracingHandler());

// Your routes
app.get('/', (req, res) => {
  res.send('Hello World!');
});

// Error handler must be before any other error middleware and after all controllers
app.use(Sentry.Handlers.errorHandler());

app.listen(3000);
```

## 🔐 Environment Variables

Create a `.env` file in your project root:

```env
# Required
SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0

# Optional
SENTRY_ENVIRONMENT=production
SENTRY_RELEASE=my-app@1.0.0
SENTRY_DEBUG=false

# Sampling
SENTRY_SAMPLE_RATE=1.0
SENTRY_TRACES_SAMPLE_RATE=0.1
SENTRY_PROFILES_SAMPLE_RATE=0.1

# Breadcrumbs
SENTRY_MAX_BREADCRUMBS=100
SENTRY_MAX_VALUE_LENGTH=250

# Features
SENTRY_ATTACH_STACKTRACE=true
SENTRY_SEND_DEFAULT_PII=false
SENTRY_AUTO_SESSION_TRACKING=true
SENTRY_ENABLE_TRACING=true

# Server
SENTRY_SERVER_NAME=my-server
SENTRY_MAX_QUEUE_SIZE=30
SENTRY_SHUTDOWN_TIMEOUT=2000

# Filtering (comma-separated)
SENTRY_IGNORE_ERRORS=NetworkError,TimeoutError
SENTRY_IGNORE_TRANSACTIONS=/health,/ping
SENTRY_DENY_URLS=localhost,127.0.0.1
SENTRY_ALLOW_URLS=myapp.com,api.myapp.com

# Distribution
SENTRY_DIST=build-123
```

## 📖 Configuration Options

### Core Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `dsn` | `string` | - | The DSN tells the SDK where to send events |
| `environment` | `string` | `NODE_ENV` | Environment name (production, staging, etc.) |
| `release` | `string` | - | Release version of your application |
| `debug` | `boolean` | `false` | Enable debug mode for troubleshooting |

### Sampling Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `sampleRate` | `number` | `1.0` | Sample rate for error events (0.0 to 1.0) |
| `tracesSampleRate` | `number` | - | Sample rate for performance monitoring (0.0 to 1.0) |
| `profilesSampleRate` | `number` | - | Sample rate for profiling (0.0 to 1.0) |

### Breadcrumbs Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `maxBreadcrumbs` | `number` | `100` | Maximum number of breadcrumbs |
| `maxValueLength` | `number` | `250` | Maximum length of breadcrumb values |

### Feature Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `attachStacktrace` | `boolean` | `true` | Attach stack traces to messages |
| `sendDefaultPii` | `boolean` | `false` | Send personally identifiable information |
| `autoSessionTracking` | `boolean` | `true` | Automatically track sessions |
| `enableTracing` | `boolean` | - | Enable performance monitoring |

### Filtering Options

| Option | Type | Description |
|--------|------|-------------|
| `ignoreErrors` | `Array<string \| RegExp>` | Error messages to ignore |
| `ignoreTransactions` | `Array<string \| RegExp>` | Transaction names to ignore |
| `denyUrls` | `Array<string \| RegExp>` | URLs to exclude from tracking |
| `allowUrls` | `Array<string \| RegExp>` | URLs to include in tracking |

## 🎯 API Reference

### `initSentry(options?: SentrySDKOptions): SentrySDKResult`

Initialize the Sentry SDK with the provided options. Options passed directly will override environment variables.

```typescript
const result = initSentry({
  dsn: 'https://...',
  environment: 'production',
});

console.log(result.initialized); // true
```

### `closeSentry(timeout?: number): Promise<boolean>`

Close the Sentry SDK and flush pending events.

```typescript
await closeSentry(5000); // Wait up to 5 seconds
```

### `flushSentry(timeout?: number): Promise<boolean>`

Flush pending events without closing the SDK.

```typescript
await flushSentry(2000);
```

### `captureException(exception: any, context?: any): string`

Capture an exception.

```typescript
try {
  throw new Error('Something went wrong');
} catch (error) {
  captureException(error, {
    tags: { section: 'checkout' },
    extra: { orderId: '12345' },
  });
}
```

### `captureMessage(message: string, level?: string): string`

Capture a message.

```typescript
captureMessage('Payment processed successfully', 'info');
```

### `addBreadcrumb(breadcrumb: Breadcrumb): void`

Add a breadcrumb for better error context.

```typescript
addBreadcrumb({
  category: 'auth',
  message: 'User logged in',
  level: 'info',
  data: {
    userId: '123',
    username: 'john_doe',
  },
});
```

### `setUser(user: User | null): void`

Set user information.

```typescript
setUser({
  id: '123',
  email: 'user@example.com',
  username: 'john_doe',
  ip_address: '{{auto}}',
});

// Clear user
setUser(null);
```

### `setTag(key: string, value: string): void`

Set a single tag.

```typescript
setTag('page_locale', 'en-US');
```

### `setTags(tags: object): void`

Set multiple tags.

```typescript
setTags({
  page_locale: 'en-US',
  user_type: 'premium',
  feature_flag: 'new_ui',
});
```

### `setExtra(key: string, value: any): void`

Set extra context data.

```typescript
setExtra('shopping_cart', {
  items: 3,
  total: 99.99,
});
```

### `setContext(name: string, context: object | null): void`

Set custom context.

```typescript
setContext('character', {
  name: 'John Doe',
  age: 30,
  occupation: 'Developer',
});
```

### `configureScope(callback: (scope: Scope) => void): void`

Configure the current scope.

```typescript
configureScope((scope) => {
  scope.setTag('section', 'admin');
  scope.setLevel('warning');
  scope.setFingerprint(['custom-fingerprint']);
});
```

### `startTransaction(context: any): Transaction`

Start a performance monitoring transaction.

```typescript
const transaction = startTransaction({
  name: 'process-order',
  op: 'task',
});

try {
  // Your code here
  const span = transaction.startChild({
    op: 'db',
    description: 'Fetch user data',
  });
  
  // Do database work
  
  span.finish();
} finally {
  transaction.finish();
}
```

### `withErrorHandling<T>(fn: T): T`

Wrap a function with automatic error handling.

```typescript
const processOrder = withErrorHandling(async (orderId: string) => {
  // Your code here
  // Any errors will be automatically captured by Sentry
});

await processOrder('12345');
```

## 🔥 Advanced Usage

### Custom Error Filtering

```typescript
initSentry({
  dsn: process.env.SENTRY_DSN,
  beforeSend(event, hint) {
    // Don't send events with specific error messages
    if (event.exception) {
      const error = hint.originalException;
      if (error && error.message?.includes('User cancelled')) {
        return null; // Don't send to Sentry
      }
    }
    
    // Remove sensitive data
    if (event.request?.data) {
      delete event.request.data.password;
      delete event.request.data.creditCard;
    }
    
    return event;
  },
});
```

### Custom Breadcrumbs

```typescript
initSentry({
  dsn: process.env.SENTRY_DSN,
  beforeBreadcrumb(breadcrumb, hint) {
    // Filter out breadcrumbs with sensitive data
    if (breadcrumb.category === 'console') {
      return null;
    }
    
    // Modify breadcrumbs
    if (breadcrumb.category === 'fetch') {
      breadcrumb.data = {
        ...breadcrumb.data,
        timestamp: Date.now(),
      };
    }
    
    return breadcrumb;
  },
});
```

### Performance Monitoring

```typescript
import { initSentry, startTransaction } from '@hunghoang/sentry-sdk';

initSentry({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.2, // Sample 20% of transactions
  enableTracing: true,
});

async function processData() {
  const transaction = startTransaction({
    name: 'process-data',
    op: 'task',
  });

  try {
    // Database query
    const dbSpan = transaction.startChild({
      op: 'db',
      description: 'SELECT * FROM users',
    });
    await db.query('SELECT * FROM users');
    dbSpan.finish();

    // HTTP request
    const httpSpan = transaction.startChild({
      op: 'http.client',
      description: 'GET https://api.example.com/data',
    });
    await fetch('https://api.example.com/data');
    httpSpan.finish();

    transaction.setStatus('ok');
  } catch (error) {
    transaction.setStatus('internal_error');
    throw error;
  } finally {
    transaction.finish();
  }
}
```

### Context and Scope Management

```typescript
import { configureScope, setUser, setTag } from '@hunghoang/sentry-sdk';

// Set global context that applies to all events
setUser({
  id: '12345',
  email: 'user@example.com',
});

setTag('server', 'us-east-1');

// Use local scope for specific operations
import { Sentry } from '@hunghoang/sentry-sdk';

Sentry.withScope((scope) => {
  scope.setTag('section', 'payment');
  scope.setLevel('warning');
  Sentry.captureMessage('Payment processing started');
});
```

## 🧪 Testing

When testing your application, you might want to disable Sentry:

```typescript
// In your test setup
process.env.SENTRY_DSN = '';

// Or
initSentry({
  dsn: process.env.NODE_ENV === 'test' ? '' : process.env.SENTRY_DSN,
});
```

## 📝 Best Practices

1. **Always set environment and release**: This helps you track errors across different deployments

```typescript
initSentry({
  environment: process.env.NODE_ENV,
  release: `my-app@${process.env.npm_package_version}`,
});
```

2. **Use appropriate sample rates**: Don't send 100% of transactions in production

```typescript
initSentry({
  tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
});
```

3. **Filter sensitive data**: Use `beforeSend` to remove PII

```typescript
initSentry({
  beforeSend(event) {
    // Remove sensitive data
    if (event.request) {
      delete event.request.cookies;
    }
    return event;
  },
});
```

4. **Add context to errors**: Use breadcrumbs, tags, and extra data

```typescript
addBreadcrumb({
  category: 'user-action',
  message: 'User clicked checkout button',
  level: 'info',
});

try {
  await processPayment();
} catch (error) {
  captureException(error, {
    tags: { payment_method: 'credit_card' },
    extra: { amount: 99.99 },
  });
}
```

5. **Close Sentry gracefully**: Flush events before shutdown

```typescript
process.on('SIGTERM', async () => {
  await closeSentry(5000);
  process.exit(0);
});
```

## 🔧 Troubleshooting

### Events not appearing in Sentry

1. Check that your DSN is correct
2. Enable debug mode: `SENTRY_DEBUG=true`
3. Check network connectivity
4. Verify sample rates are not filtering out events

### High event volume

1. Reduce sample rates
2. Use `ignoreErrors` to filter common errors
3. Implement rate limiting with `beforeSend`

### Missing context

1. Ensure breadcrumbs are enabled
2. Check that context is set before errors occur
3. Verify `attachStacktrace` is enabled

## 📄 License

MIT

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 📞 Support

For issues and questions:
- Create an issue on GitHub
- Check Sentry documentation: https://docs.sentry.io/

## 🔗 Links

- [Sentry Documentation](https://docs.sentry.io/)
- [Sentry Node SDK](https://docs.sentry.io/platforms/node/)
- [NPM Package](https://www.npmjs.com/package/@hunghoang/sentry-sdk)

---

Made with ❤️ by Hung Hoang