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

distributed-tracing.mddocs/

Distributed Tracing

Cross-service tracing capabilities with trace metadata extraction and injection for microservices architectures.

Capabilities

Get Linking Metadata

Retrieve linking metadata for correlating logs with traces across services.

/**
 * Returns linking metadata for correlating logs with traces.
 * @param {boolean} [omitSupportability] - Whether to skip supportability metric (defaults to false)
 * @returns {object} Object containing trace.id, span.id, entity.name, entity.type, entity.guid, hostname
 */
function getLinkingMetadata(omitSupportability);

Usage Examples:

const newrelic = require('newrelic');

// Basic linking metadata for logging
function logWithTraceContext(message, level = 'info') {
  const metadata = newrelic.getLinkingMetadata();
  
  console.log(JSON.stringify({
    timestamp: new Date().toISOString(),
    level: level,
    message: message,
    ...metadata  // Includes trace.id, span.id, entity.name, etc.
  }));
}

// Usage
logWithTraceContext('Processing user order', 'info');
// Output: {"timestamp":"2023-10-01T12:00:00.000Z","level":"info","message":"Processing user order","trace.id":"abc123","span.id":"def456","entity.name":"my-service","entity.type":"SERVICE","entity.guid":"MTIzNDU2fEFQTXxBUFBMSUNBVElPTnw3ODkw","hostname":"server-1"}

// Structured logging with winston
const winston = require('winston');

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json(),
    winston.format.printf(info => {
      const metadata = newrelic.getLinkingMetadata(true); // Skip supportability metric
      return JSON.stringify({
        ...info,
        ...metadata
      });
    })
  ),
  transports: [new winston.transports.Console()]
});

logger.info('Order processed successfully', { orderId: '12345' });

Get Trace Metadata

Get the current trace and span identifiers for distributed tracing.

/**
 * Returns the current trace and span identifiers.
 * @returns {object} Object containing traceId and spanId
 */
function getTraceMetadata();

Usage Examples:

// Basic trace metadata
function getCurrentTraceInfo() {
  const metadata = newrelic.getTraceMetadata();
  console.log(`Current trace ID: ${metadata.traceId}`);
  console.log(`Current span ID: ${metadata.spanId}`);
  return metadata;
}

// Add trace context to API responses
app.get('/api/users/:id', (req, res) => {
  const user = getUserById(req.params.id);
  const traceMetadata = newrelic.getTraceMetadata();
  
  res.json({
    user: user,
    _meta: {
      traceId: traceMetadata.traceId,
      spanId: traceMetadata.spanId,
      timestamp: new Date().toISOString()
    }
  });
});

// Custom correlation IDs
function generateCorrelationId() {
  const traceMetadata = newrelic.getTraceMetadata();
  return `${traceMetadata.traceId}-${Date.now()}`;
}

Linking Metadata Structure

interface LinkingMetadata {
  /** Current trace identifier */
  'trace.id': string;
  /** Current span identifier */
  'span.id': string;
  /** Application name from New Relic configuration */
  'entity.name': string;
  /** Entity type, always "SERVICE" */
  'entity.type': string;
  /** New Relic entity GUID */
  'entity.guid': string;
  /** Hostname of the server */
  hostname: string;
}

interface TraceMetadata {
  /** Current distributed trace ID */
  traceId: string;
  /** Current span ID */
  spanId: string;
}

Transaction Handle Distributed Tracing

When using getTransaction(), the returned handle provides distributed tracing methods:

/**
 * Accept and process incoming distributed trace headers from upstream services
 * @param {string} transportType - Transport mechanism (e.g., 'HTTP', 'HTTPS', 'Queue')
 * @param {object} headers - Headers object containing distributed trace information
 */
TransactionHandle.prototype.acceptDistributedTraceHeaders = function(transportType, headers);

/**
 * Insert distributed trace headers for outgoing requests to downstream services
 * @param {object} headers - Headers object to modify with distributed trace information
 */
TransactionHandle.prototype.insertDistributedTraceHeaders = function(headers);

Common Integration Patterns

HTTP Client Instrumentation

const axios = require('axios');

// Automatic header injection for outgoing requests
axios.interceptors.request.use((config) => {
  const transaction = newrelic.getTransaction();
  if (transaction) {
    config.headers = config.headers || {};
    transaction.insertDistributedTraceHeaders(config.headers);
  }
  return config;
});

// Manual header injection
async function callDownstreamService(data) {
  const transaction = newrelic.getTransaction();
  const headers = { 'Content-Type': 'application/json' };
  
  if (transaction) {
    transaction.insertDistributedTraceHeaders(headers);
  }
  
  const response = await axios.post('https://downstream-service/api/process', data, {
    headers: headers
  });
  return response.data;
}

HTTP Server Instrumentation

// Express middleware to accept incoming distributed trace headers
app.use((req, res, next) => {
  const transaction = newrelic.getTransaction();
  if (transaction) {
    transaction.acceptDistributedTraceHeaders('HTTP', req.headers);
  }
  next();
});

// Manual header processing
app.post('/api/webhook', (req, res) => {
  const transaction = newrelic.getTransaction();
  if (transaction) {
    transaction.acceptDistributedTraceHeaders('HTTP', req.headers);
  }
  
  // Process webhook...
  res.json({ status: 'processed' });
});

Message Queue Integration

// Producer - add trace headers to message
function publishMessage(queueName, messageData) {
  const transaction = newrelic.getTransaction();
  const messageHeaders = {};
  
  if (transaction) {
    transaction.insertDistributedTraceHeaders(messageHeaders);
  }
  
  return messageQueue.publish(queueName, {
    data: messageData,
    headers: messageHeaders,
    timestamp: Date.now()
  });
}

// Consumer - accept trace headers from message
function processMessage(message) {
  return newrelic.startBackgroundTransaction('ProcessMessage', 'Queue', () => {
    const transaction = newrelic.getTransaction();
    
    if (transaction && message.headers) {
      transaction.acceptDistributedTraceHeaders('Queue', message.headers);
    }
    
    // Process the message
    return handleMessage(message.data);
  });
}

Microservices Architecture

// Service A - initiating a distributed trace
async function processUserRequest(userData) {
  // Start with web transaction (automatically creates trace)
  const validation = await callValidationService(userData);
  const enriched = await callEnrichmentService(userData, validation);
  const stored = await callStorageService(enriched);
  
  return stored;
}

async function callValidationService(userData) {
  const transaction = newrelic.getTransaction();
  const headers = { 'Content-Type': 'application/json' };
  
  if (transaction) {
    transaction.insertDistributedTraceHeaders(headers);
  }
  
  const response = await fetch('http://validation-service/validate', {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(userData)
  });
  
  return response.json();
}

// Service B - validation service
app.post('/validate', (req, res) => {
  const transaction = newrelic.getTransaction();
  if (transaction) {
    transaction.acceptDistributedTraceHeaders('HTTP', req.headers);
  }
  
  const result = validateUserData(req.body);
  res.json(result);
});

Database Operation Correlation

// Add trace context to database operations for correlation
async function performDatabaseOperation(query, params) {
  const traceMetadata = newrelic.getTraceMetadata();
  
  // Add trace context to database session or query comments
  const contextualQuery = `/* traceId: ${traceMetadata.traceId}, spanId: ${traceMetadata.spanId} */ ${query}`;
  
  return await database.execute(contextualQuery, params);
}

Log Correlation

const winston = require('winston');

// Custom winston format that adds trace context
const traceFormat = winston.format((info) => {
  const metadata = newrelic.getLinkingMetadata(true);
  return { ...info, ...metadata };
});

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    traceFormat(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'app.log' })
  ]
});

// Usage - logs will automatically include trace context
logger.info('User order processed', { 
  orderId: '12345', 
  userId: 'user-789' 
});

Custom Correlation Strategies

// Create custom correlation IDs based on trace metadata
function createCorrelationId(prefix = 'corr') {
  const traceMetadata = newrelic.getTraceMetadata();
  const timestamp = Date.now();
  
  if (traceMetadata.traceId) {
    return `${prefix}-${traceMetadata.traceId}-${timestamp}`;
  }
  
  // Fallback for when no trace is active
  return `${prefix}-${require('crypto').randomUUID()}-${timestamp}`;
}

// Add correlation ID to all responses
app.use((req, res, next) => {
  const correlationId = createCorrelationId('req');
  res.set('X-Correlation-ID', correlationId);
  req.correlationId = correlationId;
  next();
});

// Use correlation ID in logging
app.use((req, res, next) => {
  const originalSend = res.send;
  res.send = function(data) {
    logger.info('Request completed', {
      correlationId: req.correlationId,
      method: req.method,
      url: req.url,
      statusCode: res.statusCode
    });
    return originalSend.call(this, data);
  };
  next();
});

Cross-Platform Tracing

// When calling non-Node.js services, ensure trace context is propagated
async function callPythonService(data) {
  const transaction = newrelic.getTransaction();
  const headers = {
    'Content-Type': 'application/json',
    'X-Service-Context': 'node-service'
  };
  
  if (transaction) {
    transaction.insertDistributedTraceHeaders(headers);
    
    // Add additional context for non-New Relic instrumented services
    const traceMetadata = newrelic.getTraceMetadata();
    headers['X-Trace-ID'] = traceMetadata.traceId;
    headers['X-Span-ID'] = traceMetadata.spanId;
  }
  
  return await fetch('http://python-service/process', {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(data)
  });
}

Configuration Requirements

Distributed tracing requires:

  1. Agent Configuration: distributed_tracing.enabled: true (default)
  2. Network Connectivity: Services must be able to communicate
  3. Header Propagation: HTTP clients and servers must propagate trace headers
  4. Transaction Context: Trace metadata is only available within active transactions

Best Practices

  1. Automatic Instrumentation: Use automatic instrumentation when possible (most HTTP libraries are auto-instrumented)
  2. Manual Header Management: For custom protocols or clients, manually manage header injection/acceptance
  3. Error Handling: Handle cases where trace metadata might not be available
  4. Performance: Trace header operations are lightweight, but avoid unnecessary calls in hot paths
  5. Security: Be cautious about trace headers in logs or external systems
  6. Consistency: Ensure all services in your architecture participate in distributed tracing
  7. Monitoring: Use New Relic's distributed tracing UI to visualize and troubleshoot trace flows

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