CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-newrelic

Application Performance Monitoring (APM) agent for Node.js applications with transaction tracing, error tracking, custom metrics, and distributed tracing capabilities.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

Comprehensive error tracking with custom attributes, error grouping, and expected error marking.

Capabilities

Notice Error

Record errors that have been handled by your application code.

/**
 * Send errors to New Relic that you've already handled. Does not obey
 * ignore_status_codes configuration.
 * @param {Error|string} error - The error to be traced
 * @param {object} [customAttributes] - Optional custom attributes for the error
 * @param {boolean} [expected] - Whether the error is expected (defaults to false)
 * @returns {false|undefined} Returns false when disabled/errored, undefined on success
 */
function noticeError(error, customAttributes, expected);

Usage Examples:

const newrelic = require('newrelic');

// Basic error reporting
try {
  performSomeTask();
} catch (err) {
  newrelic.noticeError(err);
  // Handle the error in your application
  res.status(500).json({ error: 'Internal server error' });
}

// Error with custom attributes
try {
  processPayment(paymentData);
} catch (err) {
  newrelic.noticeError(err, {
    userId: user.id,
    paymentMethod: paymentData.method,
    amount: paymentData.amount,
    orderContext: 'checkout'
  });
  throw err; // Re-throw if needed
}

// Expected errors (tracked but don't affect error rate)
try {
  validateUserInput(input);
} catch (validationError) {
  newrelic.noticeError(validationError, {
    inputType: 'userRegistration',
    validationRule: validationError.rule
  }, true); // Mark as expected
  
  return res.status(400).json({ 
    error: 'Validation failed',
    details: validationError.message 
  });
}

// String errors (automatically converted to Error objects)
if (user.permissions < requiredLevel) {
  newrelic.noticeError('Insufficient permissions', {
    userId: user.id,
    requiredLevel: requiredLevel,
    userLevel: user.permissions
  });
}

Set Error Group Callback

Define custom logic for grouping similar errors together in New Relic's Errors Inbox.

/**
 * Set a custom callback function to generate error group names for the Errors Inbox.
 * The callback receives error metadata and must return a string.
 * @param {Function} callback - Synchronous function to generate error.group.name attribute
 */
function setErrorGroupCallback(callback);

Usage Examples:

// Basic error grouping by error type and HTTP status
newrelic.setErrorGroupCallback((metadata) => {
  const errorType = metadata.error?.name || 'UnknownError';
  const statusCode = metadata['http.statusCode'];
  
  if (statusCode) {
    return `${errorType}-${statusCode}`;
  }
  
  return errorType;
});

// Advanced error grouping with business context
newrelic.setErrorGroupCallback((metadata) => {
  const error = metadata.error;
  const customAttrs = metadata.customAttributes || {};
  
  // Group validation errors by the field that failed
  if (error?.name === 'ValidationError' && customAttrs.field) {
    return `ValidationError-${customAttrs.field}`;
  }
  
  // Group payment errors by payment method
  if (error?.message?.includes('payment') && customAttrs.paymentMethod) {
    return `PaymentError-${customAttrs.paymentMethod}`;
  }
  
  // Group API errors by endpoint
  if (metadata['request.uri'] && error?.name === 'APIError') {
    const endpoint = metadata['request.uri'].split('?')[0]; // Remove query params
    return `APIError-${endpoint}`;
  }
  
  // Default grouping
  return error?.name || 'UnknownError';
});

// Error grouping with user context
newrelic.setErrorGroupCallback((metadata) => {
  const error = metadata.error;
  const isExpected = metadata['error.expected'];
  const userTier = metadata.customAttributes?.userTier;
  
  // Don't group expected errors with unexpected ones
  const prefix = isExpected ? 'Expected' : 'Unexpected';
  
  // Group by user tier for business insights
  if (userTier) {
    return `${prefix}-${error?.name}-${userTier}`;
  }
  
  return `${prefix}-${error?.name || 'UnknownError'}`;
});

Error Metadata Structure

The error group callback receives a metadata object with the following structure:

interface ErrorMetadata {
  /** Custom attributes passed to noticeError() */
  customAttributes?: object;
  /** HTTP request URI */
  'request.uri'?: string;
  /** HTTP status code */
  'http.statusCode'?: string;
  /** HTTP method */
  'http.method'?: string;
  /** The actual Error object */
  error?: Error;
  /** Whether the error was marked as expected */
  'error.expected'?: boolean;
}

Configuration and Security

High Security Mode

When high security mode is enabled:

  • Custom attributes in noticeError() are ignored and logged as debug message
  • The error itself is still recorded

Configuration Controls

  • api.notice_error_enabled: false - Disables error recording
  • api.custom_attributes_enabled: false - Disables custom attributes on errors

Common Usage Patterns

API Error Handling with Context

app.use('/api', (req, res, next) => {
  try {
    next();
  } catch (error) {
    const errorContext = {
      endpoint: req.path,
      method: req.method,
      userId: req.user?.id,
      userAgent: req.get('User-Agent'),
      ipAddress: req.ip,
      requestId: req.headers['x-request-id']
    };
    
    newrelic.noticeError(error, errorContext);
    
    res.status(500).json({
      error: 'Internal server error',
      requestId: errorContext.requestId
    });
  }
});

Database Error Handling

async function performDatabaseOperation(query, params) {
  try {
    return await database.execute(query, params);
  } catch (error) {
    const errorContext = {
      queryType: query.type,
      tableCount: query.tables?.length || 0,
      paramCount: params.length,
      connectionPool: database.pool.name
    };
    
    // Mark timeout errors as expected if they're common
    const isExpected = error.code === 'TIMEOUT' && query.type === 'SELECT';
    
    newrelic.noticeError(error, errorContext, isExpected);
    throw error;
  }
}

Validation Error Handling

function validateUserData(userData, schema) {
  try {
    return schema.validate(userData);
  } catch (validationError) {
    // These are expected business logic errors
    newrelic.noticeError(validationError, {
      validationType: 'userRegistration',
      failedField: validationError.field,
      providedValue: validationError.value,
      validationRule: validationError.rule
    }, true); // Mark as expected
    
    throw validationError;
  }
}

External Service Error Handling

async function callExternalAPI(endpoint, data) {
  try {
    const response = await httpClient.post(endpoint, data);
    return response.data;
  } catch (error) {
    const errorContext = {
      externalService: 'payment-processor',
      endpoint: endpoint,
      httpStatusCode: error.response?.status,
      retryAttempt: data.retryCount || 0,
      requestSize: JSON.stringify(data).length
    };
    
    // 4xx errors are typically expected (bad requests)
    const isExpected = error.response?.status >= 400 && error.response?.status < 500;
    
    newrelic.noticeError(error, errorContext, isExpected);
    throw error;
  }
}

Business Logic Error Grouping

// Set up comprehensive error grouping
newrelic.setErrorGroupCallback((metadata) => {
  const error = metadata.error;
  const customAttrs = metadata.customAttributes || {};
  const uri = metadata['request.uri'] || '';
  const statusCode = metadata['http.statusCode'];
  
  // Payment processing errors
  if (customAttrs.paymentMethod || uri.includes('/payment')) {
    return `Payment-${customAttrs.paymentMethod || 'Unknown'}-${error?.name}`;
  }
  
  // User authentication errors
  if (uri.includes('/auth') || error?.name === 'AuthenticationError') {
    return `Auth-${error?.name}-${statusCode}`;
  }
  
  // Data validation errors grouped by entity type
  if (error?.name === 'ValidationError' && customAttrs.entityType) {
    return `Validation-${customAttrs.entityType}-${customAttrs.failedField || 'Unknown'}`;
  }
  
  // Third-party service errors
  if (customAttrs.externalService) {
    return `External-${customAttrs.externalService}-${error?.name}`;
  }
  
  // Database errors grouped by operation type
  if (error?.name?.includes('Database') && customAttrs.queryType) {
    return `Database-${customAttrs.queryType}-${error?.name}`;
  }
  
  // Default grouping by error name and status code
  return `${error?.name || 'UnknownError'}${statusCode ? `-${statusCode}` : ''}`;
});

Error Rate Monitoring

// Monitor error rates and alert on thresholds
let errorCount = 0;
let totalRequests = 0;

app.use((req, res, next) => {
  totalRequests++;
  
  res.on('finish', () => {
    if (res.statusCode >= 500) {
      errorCount++;
      
      // Calculate error rate every 100 requests
      if (totalRequests % 100 === 0) {
        const errorRate = errorCount / totalRequests;
        
        newrelic.recordMetric('ErrorRate/Server', errorRate);
        
        if (errorRate > 0.05) { // Alert if > 5% error rate
          newrelic.recordCustomEvent('HighErrorRate', {
            errorRate: errorRate,
            errorCount: errorCount,
            totalRequests: totalRequests,
            alertLevel: 'critical'
          });
        }
      }
    }
  });
  
  next();
});

Async Error Handling

// Proper async error handling with context preservation
async function processAsyncTask(taskData) {
  const taskContext = {
    taskId: taskData.id,
    taskType: taskData.type,
    userId: taskData.userId,
    priority: taskData.priority
  };
  
  try {
    const result = await performAsyncOperation(taskData);
    return result;
  } catch (error) {
    // Preserve context when handling async errors
    newrelic.noticeError(error, {
      ...taskContext,
      errorOccurredAt: 'asyncTaskProcessing',
      asyncContext: true
    });
    
    // Re-throw to maintain error flow
    throw error;
  }
}

// Handling unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
  newrelic.noticeError(reason instanceof Error ? reason : new Error(String(reason)), {
    source: 'unhandledRejection',
    promiseLocation: promise.toString()
  });
});

docs

aws-lambda.md

browser-monitoring.md

custom-attributes.md

custom-instrumentation.md

distributed-tracing.md

error-handling.md

index.md

llm-monitoring.md

metrics-events.md

segments-timing.md

transaction-management.md

url-naming-rules.md

utilities.md

tile.json