Skip to main content
Technology & EngineeringMonitoring Services265 lines

New Relic

New Relic: APM, distributed tracing, browser monitoring, custom events, NRQL queries, alert policies, Node.js and browser agents

Quick Summary16 lines
You are an expert in integrating New Relic for application monitoring.

## Key Points

- **Use distributed tracing over cross-application tracing** — distributed tracing is the modern replacement and works across all language agents and third-party services.
- **Send custom events for business metrics** — NRQL can query custom events just like built-in data, giving you dashboards that track revenue, signups, or feature usage alongside technical metrics.
- **Exclude sensitive headers and attributes** — configure the `attributes.exclude` list to strip cookies, auth tokens, and PII from trace data before it leaves your infrastructure.

## Quick Example

```html
<!-- Paste the browser agent snippet from New Relic UI into <head>,
     or use the npm package for SPA frameworks -->
```
skilldb get monitoring-services-skills/new-relicFull skill: 265 lines
Paste into your CLAUDE.md or agent config

New Relic — Application Monitoring

You are an expert in integrating New Relic for application monitoring.

Core Philosophy

Overview

New Relic is a full-stack observability platform that provides APM, infrastructure monitoring, browser monitoring, log management, and custom dashboards. Its query language NRQL (New Relic Query Language) is central to building dashboards, alerts, and ad-hoc investigations. The Node.js agent auto-instruments popular frameworks and the browser agent captures real user performance data.

Setup & Configuration

Node.js APM Agent

// newrelic.ts — must be at the project root or specified via NEW_RELIC_HOME
// The agent is loaded via require('newrelic') as the FIRST import in your entry point.

// newrelic.js (CommonJS config file required by the agent)
"use strict";

exports.config = {
  app_name: [process.env.NEW_RELIC_APP_NAME ?? "my-api-service"],
  license_key: process.env.NEW_RELIC_LICENSE_KEY,
  distributed_tracing: {
    enabled: true,
  },
  logging: {
    level: "info",
    filepath: "stdout",
  },
  allow_all_headers: true,
  application_logging: {
    forwarding: {
      enabled: true,
      max_samples_stored: 10000,
    },
  },
  attributes: {
    exclude: [
      "request.headers.cookie",
      "request.headers.authorization",
      "request.headers.proxyAuthorization",
      "request.headers.setCookie*",
      "request.headers.x*",
      "response.headers.cookie",
      "response.headers.authorization",
      "response.headers.proxyAuthorization",
      "response.headers.setCookie*",
    ],
  },
};
// app.ts — entry point: require newrelic FIRST
import "newrelic";
import express from "express";
import { router } from "./routes";

const app = express();
app.use(router);
app.listen(3000);

Browser Monitoring (SPA Agent)

<!-- Paste the browser agent snippet from New Relic UI into <head>,
     or use the npm package for SPA frameworks -->
// lib/newrelic-browser.ts — for React/Next.js apps
import { BrowserAgent } from "@newrelic/browser-agent/loaders/browser-agent";

const agent = new BrowserAgent({
  init: {
    distributed_tracing: { enabled: true },
    privacy: { cookies_enabled: true },
    ajax: { deny_list: ["bam.nr-data.net"] },
  },
  info: {
    beacon: "bam.nr-data.net",
    errorBeacon: "bam.nr-data.net",
    licenseKey: process.env.NEXT_PUBLIC_NEW_RELIC_BROWSER_KEY!,
    applicationID: process.env.NEXT_PUBLIC_NEW_RELIC_APP_ID!,
    sa: 1,
  },
  loader_config: {
    accountID: process.env.NEXT_PUBLIC_NEW_RELIC_ACCOUNT_ID!,
    trustKey: process.env.NEXT_PUBLIC_NEW_RELIC_TRUST_KEY!,
    agentID: process.env.NEXT_PUBLIC_NEW_RELIC_AGENT_ID!,
    licenseKey: process.env.NEXT_PUBLIC_NEW_RELIC_BROWSER_KEY!,
    applicationID: process.env.NEXT_PUBLIC_NEW_RELIC_APP_ID!,
  },
});

export { agent };

Core Patterns

Custom Events and Attributes

// lib/nr-events.ts
import newrelic from "newrelic";

export function recordBusinessEvent(
  eventType: string,
  attributes: Record<string, string | number | boolean>
) {
  newrelic.recordCustomEvent(eventType, {
    ...attributes,
    environment: process.env.NODE_ENV,
    service: process.env.NEW_RELIC_APP_NAME,
  });
}

// Usage: track domain-specific events queryable with NRQL
recordBusinessEvent("OrderPlaced", {
  orderId: "abc-123",
  total: 59.99,
  itemCount: 3,
  paymentMethod: "stripe",
});

// Query in New Relic:
// SELECT count(*) FROM OrderPlaced FACET paymentMethod SINCE 1 hour ago

Custom Transaction Instrumentation

// lib/nr-tracing.ts
import newrelic from "newrelic";

export function instrumentBackground<T>(
  name: string,
  group: string,
  fn: () => Promise<T>
): Promise<T> {
  return new Promise((resolve, reject) => {
    newrelic.startBackgroundTransaction(`${group}/${name}`, group, () => {
      const transaction = newrelic.getTransaction();

      fn()
        .then((result) => {
          transaction.end();
          resolve(result);
        })
        .catch((error) => {
          newrelic.noticeError(error as Error);
          transaction.end();
          reject(error);
        });
    });
  });
}

// Usage: instrument a queue worker or cron job
await instrumentBackground("processEmailQueue", "Worker", async () => {
  const messages = await queue.receive(10);
  for (const msg of messages) {
    await sendEmail(msg.body);
  }
});

Error Tracking with Context

// middleware/nr-error-handler.ts
import newrelic from "newrelic";
import type { Request, Response, NextFunction } from "express";

export function nrErrorHandler(
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) {
  newrelic.noticeError(err, {
    userId: (req as any).user?.id,
    route: req.route?.path ?? req.path,
    method: req.method,
    statusCode: res.statusCode,
  });

  next(err);
}

// Add custom attributes to the current transaction
export function addTransactionContext(req: Request) {
  if ((req as any).user) {
    newrelic.addCustomAttributes({
      "user.id": (req as any).user.id,
      "user.plan": (req as any).user.plan,
    });
  }
}

NRQL Alert Conditions (as Code via Terraform)

resource "newrelic_nrql_alert_condition" "high_error_rate" {
  account_id                   = var.new_relic_account_id
  policy_id                    = newrelic_alert_policy.api_policy.id
  type                         = "static"
  name                         = "API Error Rate > 5%"
  enabled                      = true
  violation_time_limit_seconds = 3600

  nrql {
    query = <<-NRQL
      SELECT percentage(count(*), WHERE error IS true)
      FROM Transaction
      WHERE appName = 'my-api-service'
    NRQL
  }

  critical {
    operator              = "above"
    threshold             = 5
    threshold_duration    = 300
    threshold_occurrences = "all"
  }

  warning {
    operator              = "above"
    threshold             = 2
    threshold_duration    = 300
    threshold_occurrences = "all"
  }
}

Best Practices

  • Use distributed tracing over cross-application tracing — distributed tracing is the modern replacement and works across all language agents and third-party services.
  • Send custom events for business metrics — NRQL can query custom events just like built-in data, giving you dashboards that track revenue, signups, or feature usage alongside technical metrics.
  • Exclude sensitive headers and attributes — configure the attributes.exclude list to strip cookies, auth tokens, and PII from trace data before it leaves your infrastructure.

Common Pitfalls

  • Forgetting to require the agent first — the New Relic Node.js agent must be the very first require/import in your entry point; otherwise auto-instrumentation of Express, pg, Redis, etc. silently fails.
  • High-cardinality custom attributes on transactions — adding unique IDs (like request IDs or session tokens) as transaction attributes inflates data volume and slows NRQL queries; use custom events for high-cardinality data instead.

Anti-Patterns

Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.

Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.

Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.

Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.

Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.

Install this skill directly: skilldb add monitoring-services-skills

Get CLI access →