@watchteam/node
v0.1.0
Published
Node.js SDK for WatchTeam AI
Maintainers
Readme
@watchteam/node
Intelligent observability for modern Node.js applications
WatchTeam.AI provides automatic log analysis, intelligent alerting, and root cause analysis powered by AI. This SDK seamlessly integrates with your Node.js applications using OpenTelemetry standards.
✨ Features
- 🚀 Zero-config setup - Just set an environment variable
- 📊 OpenTelemetry integration - Industry standard observability
- 🤖 AI-powered analysis - Automatic incident detection and root cause analysis
- ⚡ Non-blocking - Background processing with zero performance impact
- 🔍 Distributed tracing - Full request flow visibility
- 🛠️ Express middleware - Automatic HTTP request instrumentation
- 🎯 Structured logging - Rich context and semantic attributes
- 📈 GraphRAG integration - Advanced telemetry relationship analysis
🚀 Quick Start
Installation
npm install @watchteam/nodeBasic Setup
import watchteam from '@watchteam/node';
// Set your DSN (get this from your WatchTeam dashboard)
export WATCHTEAM_DSN="https://ingest.watchteam.ai/api/ingest/eyJjdXN0b21lcl9pZCI6..."
// Initialize once at app startup
watchteam.init();
// Start logging and tracing
watchteam.info('Application started', { version: '1.0.0' });That's it! Your application is now sending telemetry data to WatchTeam.AI.
📖 Usage Examples
Structured Logging
// Rich, searchable logs with semantic attributes
watchteam.info('User logged in', {
userId: 123,
sessionId: 'abc123',
'user.email': '[email protected]',
'auth.method': 'oauth'
});
watchteam.error('Database connection failed', {
'db.operation': 'connect',
'db.connection_string': 'postgres://...',
'error.message': 'connection timeout',
'error.code': 'ETIMEDOUT'
});Distributed Tracing
// Automatic span creation with rich context
const result = await watchteam.traceAsync('process_order', async (span) => {
span.setAttributes({
'order.id': 'order_123',
'order.total': 99.99,
'order.currency': 'USD',
'customer.tier': 'premium'
});
const order = await fetchOrder('order_123');
// Add events for important milestones
span.addEvent('order_validated', {
'validation.duration_ms': 45,
'order.items': order.items.length
});
return await processPayment(order);
});Express.js Integration
import express from 'express';
import watchteam from '@watchteam/node';
const app = express();
// Initialize WatchTeam
watchteam.init();
// Add automatic request instrumentation
app.use(watchteam.middleware());
// Add error tracking
app.use(watchteam.errorMiddleware());
app.get('/users/:id', async (req, res) => {
// req.watchteam is automatically available with request context
req.watchteam.info('Fetching user profile', {
userId: req.params.id,
'http.route': '/users/:id'
});
// Create custom spans for database operations
const user = await watchteam.traceAsync('db.get_user', async (span) => {
span.setAttributes({
'db.operation': 'select',
'db.table': 'users',
'user.id': req.params.id
});
return getUserFromDB(req.params.id);
});
req.watchteam.info('User profile retrieved', {
userId: user.id,
'user.email': user.email,
'response.size_bytes': JSON.stringify(user).length
});
res.json(user);
});
app.listen(3000);Context-Aware Logging
// Create bound loggers with persistent context
const userLogger = watchteam.child({
userId: 123,
sessionId: 'abc123',
'service.component': 'user-service'
});
// All logs from this logger include the bound context
userLogger.info('Profile updated', {
action: 'update_profile',
'profile.fields': ['name', 'email'],
'update.source': 'web_app'
});
userLogger.warn('Rate limit approaching', {
'rate_limit.current': 95,
'rate_limit.max': 100,
'rate_limit.window': '1m'
});Advanced Span Management
import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
const span = watchteam.startSpan('complex_operation', {
kind: SpanKind.SERVER,
attributes: {
'operation.type': 'data_processing',
'operation.priority': 'high',
'batch.size': inputData.length
}
});
try {
span.addEvent('processing_started', {
'input.size_bytes': Buffer.byteLength(JSON.stringify(inputData)),
'worker.count': 4
});
const result = await processDataBatch(inputData);
span.setAttributes({
'operation.result_count': result.length,
'operation.success': true,
'processing.duration_ms': Date.now() - startTime
});
span.addEvent('processing_completed', {
'output.size_bytes': Buffer.byteLength(JSON.stringify(result)),
'records.processed': result.length,
'records.failed': result.filter(r => r.error).length
});
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.recordException(error);
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
throw error;
} finally {
span.end();
}⚙️ Configuration
Environment Variables
# Required: Your WatchTeam DSN
export WATCHTEAM_DSN="https://ingest.watchteam.ai/api/ingest/YOUR_APP_HASH"Initialization Options
watchteam.init({
dsn: 'https://ingest.watchteam.ai/...', // Optional if WATCHTEAM_DSN is set
autoInstrument: true, // Enable auto-instrumentation (default: true)
batchSize: 100, // Logs per batch (default: 100)
flushInterval: 5000, // Flush interval in ms (default: 5000)
timeout: 10000 // HTTP timeout in ms (default: 10000)
});🔧 API Reference
Logging Methods
| Method | Description | Example |
|--------|-------------|---------|
| debug(message, attributes?) | Debug-level logging | watchteam.debug('Cache miss', { key: 'user:123' }) |
| info(message, attributes?) | Info-level logging | watchteam.info('User created', { userId: 123 }) |
| warn(message, attributes?) | Warning-level logging | watchteam.warn('High memory usage', { usage: '85%' }) |
| error(message, attributes?) | Error-level logging | watchteam.error('API timeout', { endpoint: '/users' }) |
| fatal(message, attributes?) | Fatal-level logging | watchteam.fatal('Database unreachable') |
Tracing Methods
| Method | Description | Example |
|--------|-------------|---------|
| startSpan(name, options?) | Create a new span | const span = watchteam.startSpan('db_query') |
| trace(name, fn, options?) | Trace a synchronous function | watchteam.trace('calculate', () => compute()) |
| traceAsync(name, fn, options?) | Trace an async function | watchteam.traceAsync('fetch', async () => getData()) |
Utility Methods
| Method | Description | Example |
|--------|-------------|---------|
| child(context) | Create bound logger | const userLogger = watchteam.child({ userId: 123 }) |
| middleware() | Express middleware | app.use(watchteam.middleware()) |
| errorMiddleware() | Express error middleware | app.use(watchteam.errorMiddleware()) |
| flush() | Force flush pending data | await watchteam.flush() |
| generateDSN(config) | Generate customer DSN | const dsn = watchteam.generateDSN({ ... }) |
🛡️ Best Practices
Semantic Attributes
Use OpenTelemetry semantic conventions for better data correlation:
// ✅ Good: Semantic attributes
watchteam.info('HTTP request completed', {
'http.method': 'GET',
'http.status_code': 200,
'http.url': '/api/users/123',
'user.id': '123'
});
// ❌ Avoid: Non-semantic attributes
watchteam.info('Request done', {
method: 'GET',
code: 200,
path: '/api/users/123'
});Error Handling
Always include rich context in error logs:
try {
await riskyOperation();
} catch (error) {
watchteam.error('Operation failed', {
'error.type': error.constructor.name,
'error.message': error.message,
'error.stack': error.stack,
'operation.id': operationId,
'operation.retry_count': retryCount,
'user.id': userId
});
throw error;
}Performance
- Use bound loggers for consistent context
- Leverage automatic instrumentation when possible
- Add custom spans only for important operations
- Include relevant business metrics in spans
Graceful Shutdown
// Ensure all telemetry is flushed before shutdown
process.on('SIGTERM', async () => {
console.log('Shutting down gracefully...');
await watchteam.flush();
process.exit(0);
});
process.on('SIGINT', async () => {
console.log('Received interrupt signal...');
await watchteam.flush();
process.exit(0);
});🔗 Integration Examples
Database Queries
// Trace database operations with rich context
const users = await watchteam.traceAsync('db.query.users', async (span) => {
span.setAttributes({
'db.system': 'postgresql',
'db.operation': 'select',
'db.table': 'users',
'db.query.limit': 100,
'db.connection_pool.size': pool.totalCount
});
const result = await db.query('SELECT * FROM users LIMIT $1', [100]);
span.setAttributes({
'db.rows_affected': result.rowCount,
'db.query.duration_ms': Date.now() - startTime
});
return result.rows;
});External API Calls
// Trace external service calls
const apiResponse = await watchteam.traceAsync('http.client.payment_api', async (span) => {
span.setAttributes({
'http.method': 'POST',
'http.url': 'https://api.stripe.com/v1/charges',
'payment.amount': 2999,
'payment.currency': 'usd'
});
const response = await fetch('https://api.stripe.com/v1/charges', {
method: 'POST',
body: JSON.stringify(chargeData)
});
span.setAttributes({
'http.status_code': response.status,
'http.response.size': response.headers.get('content-length'),
'payment.charge_id': (await response.json()).id
});
return response;
});Background Jobs
// Trace background job processing
const processJob = async (job) => {
return watchteam.traceAsync('job.process', async (span) => {
span.setAttributes({
'job.id': job.id,
'job.type': job.type,
'job.queue': job.queue,
'job.priority': job.priority,
'job.retry_count': job.attempts
});
const logger = watchteam.child({
'job.id': job.id,
'job.type': job.type
});
logger.info('Job processing started');
try {
const result = await job.process();
span.setAttributes({
'job.status': 'completed',
'job.result.size': JSON.stringify(result).length
});
logger.info('Job completed successfully', {
'job.duration_ms': Date.now() - job.startTime
});
return result;
} catch (error) {
span.setAttributes({
'job.status': 'failed',
'job.error.type': error.constructor.name
});
logger.error('Job failed', {
'error.message': error.message,
'job.will_retry': job.attempts < job.maxAttempts
});
throw error;
}
});
};📦 Dependencies
{
"dependencies": {
"@watchteam/node": "^1.0.0",
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/sdk-node": "^0.45.0",
"@opentelemetry/resources": "^1.18.0",
"@opentelemetry/semantic-conventions": "^1.18.0"
}
}🤝 Support
- Documentation: docs.watchteam.ai
- Community: Discord
- Issues: GitHub Issues
- Email: [email protected]
📄 License
MIT License - see LICENSE file for details.
Ready to get started? Sign up for WatchTeam.AI and get your DSN in minutes.
