npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@watchteam/node

v0.1.0

Published

Node.js SDK for WatchTeam AI

Readme

@watchteam/node

npm version License: MIT Node.js

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/node

Basic 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

📄 License

MIT License - see LICENSE file for details.


Ready to get started? Sign up for WatchTeam.AI and get your DSN in minutes.