New Relic Browser Monitoring provides comprehensive observability for Angular Single Page Applications (SPAs). This article demonstrates how to integrate New Relic into an Angular application using the @newrelic/browser-agent NPM package, providing a more maintainable approach than traditional script-based implementations.
Prerequisites
Before starting, ensure you have:
Angular Application (Angular 15+ recommended)
New Relic Account with Browser Monitoring enabled
New Relic Credentials:
Account ID
License Key (Browser monitoring license key)
Application ID
Agent ID
Trust Key
You can find these credentials in your New Relic account:
Go to Account Settings → API keys → Browser monitoring
Why Use NPM Package Instead of Script Tag?
Advantages:
TypeScript support with full type safety
Environment-based configuration management
Better integration with Angular's build system
Custom service layer for easier usage
Framework-specific features (SPA route tracking, HTTP interception)
Version control through package. json
Installation
Install the New Relic Browser Agent Package
npm install @newrelic/browseConfiguration
Add New Relic Configuration to Environment Files
export const environment = {
newRelic: {
enabled: true,
accountID: 'YOUR_ACCOUNT_ID',
trustKey: 'YOUR_TRUST_KEY',
agentID: 'YOUR_AGENT_ID',
licenseKey: 'YOUR_LICENSE_KEY',
applicationID: 'YOUR_APPLICATION_ID'
}
};Create separate configurations for different environments (development, staging, production) with appropriate credentials.
Implementation
Step 1: Initialize New Relic in main.ts
The New Relic agent should be initialized after Angular bootstraps.
import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent';
import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { environment } from './environments/environment';
import { provideHttpClient, withInterceptorsFromDi, HTTP_INTERCEPTORS } from '@angular/common/http';
import {NewRelicHttpInterceptor} from './app/global/services/newrelic-handler/newrelic-http.interceptor';
// ... other imports and providers
if (environment.production) {
enableProdMode();
}
// Bootstrap Angular first
bootstrapApplication(AppComponent, {
providers : [
// ... your other providers
provideHttpClient(withInterceptorsFromDi()),
// NEW RELIC: Register HTTP Interceptor
{
provide : HTTP_INTERCEPTORS,
useClass : NewRelicHttpInterceptor,
multi : true
}
]
})
.then(() => {
if (environment.newRelic?.enabled) {
setTimeout(() => {
try {
// Capture native console functions before agent patches
const nativeWarn = console.warn.bind(console);
const nativeError = console.error.bind(console);
// Initialize New Relic Browser Agent
new BrowserAgent( {
init : {
distributed_tracing : { enabled : true },
privacy : { cookies_enabled : true },
ajax : { deny_list : [], enabled : true },
session_trace : { enabled : true },
session_replay : {
enabled : true,
sampling_rate : 10,
error_sampling_rate : 100
},
jserrors : {
enabled : true,
harvestConsoleErrors : false // Don't capture console.error
} as any,
logging : {
enabled : true,
harvestConsoleErrors : false,
harvestConsoleWarns : false,
harvestConsoleInfo : false
} as any,
metrics : { enabled : true },
page_action : { enabled : true }
},
info : {
beacon : 'bam.nr-data.net',
errorBeacon : 'bam.nr-data.net',
licenseKey : environment.newRelic.licenseKey,
applicationID : environment.newRelic.applicationID,
sa : 1
},
loader_config : {
accountID : environment.newRelic.accountID,
trustKey : environment.newRelic.trustKey,
agentID : environment.newRelic.agentID,
licenseKey : environment.newRelic.licenseKey,
applicationID : environment.newRelic.applicationID
}
});
// Restore native console functions to prevent console logs from
// being sent to New Relic
console.warn = nativeWarn;
console.error = nativeError;
// Optional: Customize New Relic logging behavior
const nr : any = (window as any).newrelic;
if (nr?.log) {
const originalLog = nr.log.bind(nr);
nr.log = function(message: string, attributes ?: any) {
const enhancedAttributes = { ... attributes };
return originalLog(message, enhancedAttributes);
};
}
} catch (error) {
console.error('New Relic initialization failed:', error);
}
}, 100);
}
})
.catch(err => console.log(err));Initialize after Angular bootstrap to ensure proper timing
Capture and restore console functions to prevent console logs from being sent to New Relic
Wrap initialization in try-catch for error handling
Step 2: Create New Relic Service Wrapper
Create a service to wrap New Relic functionality: src/app/global/services/newrelic-handler/newrelic.service.ts
import { Injectable } from '@angular/core';
@Injectable({providedIn: 'root'})
export class NewRelicService {
private isInitialized = false;
constructor() {
// Check if New Relic is already initialized (from main.ts)
if ((window as any).newrelic) {
this.isInitialized = true;
}
}
/**
* Report custom error to New Relic
* @param error - Error object
* @param customAttributes - Additional attributes to track
*/
noticeError(error: Error, customAttributes?: Record<string, any>): void {
if (!this.isReady()) return;
try {
const attributes = {
timestamp: new Date().toISOString(),
errorName: error.name,
errorMessage: error.message,
errorStack: (error as any).originalStack || error.stack,
userAgent: navigator.userAgent,
url: window.location.href,
...customAttributes
};
const nr = (window as any).newrelic;
if (nr.log) {
Object.entries(attributes).forEach(([key, value]) => {
if (value !== undefined && value !== null && typeof value !== 'object') {
try {
nr.setCustomAttribute(key, value);
} catch {}
}
});
nr.log(error.message || 'Error occurred', {
level: 'ERROR',
...attributes
});
}
} catch (e) {
console.error('New Relic error reporting failed:', e);
}
}
/**
* Track custom user action/event
* @param name - Name of the action
* @param attributes - Custom attributes for the action
*/
addPageAction(name: string, attributes?: Record<string, any>): void {
if (!this.isReady()) return;
try {
(window as any).newrelic.addPageAction(name, {
...attributes,
timestamp: new Date().toISOString(),
url: window.location.href,
userAgent: navigator.userAgent
});
} catch (e) {
console.error('New Relic page action failed:', e);
}
}
/**
* Set custom attribute for the current session
* @param name - Attribute name
* @param value - Attribute value
*/
setCustomAttribute(name: string, value: string | number | boolean): void {
if (!this.isReady()) return;
try {
(window as any).newrelic.setCustomAttribute(name, value);
} catch (e) {
console.error('New Relic custom attribute failed:', e);
}
}
/**
* Set user ID for tracking
* @param userId - Unique user identifier
*/
setUserId(userId: string): void {
this.setCustomAttribute('userId', userId);
this.setCustomAttribute('enduser.id', userId);
}
/**
* Set user information
* @param userInfo - User information object
*/
setUserInfo(userInfo: { userId: string; email?: string; name?: string; role?: string }): void {
if (userInfo.userId) this.setUserId(userInfo.userId);
if (userInfo.email) this.setCustomAttribute('userEmail', userInfo.email);
if (userInfo.name) this.setCustomAttribute('userName', userInfo.name);
if (userInfo.role) this.setCustomAttribute('userRole', userInfo.role);
}
/**
* Track page view manually (useful for SPA)
* @param pageName - Name of the page/route
*/
setPageViewName(pageName: string): void {
if (!this.isReady()) return;
try {
(window as any).newrelic.setPageViewName(pageName);
} catch (e) {
console.error('New Relic page view name failed:', e);
}
}
/**
* Add release version for tracking
* @param version - Application version
*/
setApplicationVersion(version: string): void {
this.setCustomAttribute('applicationVersion', version);
this.setCustomAttribute('release', version);
}
/**
* Check if New Relic is initialized and ready with full API
*/
isReady(): boolean {
const nr = (window as any).newrelic;
const isReady =
!!nr &&
typeof nr.addPageAction === 'function' &&
typeof nr.noticeError === 'function';
if (isReady && !this.isInitialized) {
this.isInitialized = true;
}
return isReady;
}
/**
* Track custom metric
* @param metricName - Name of the metric
* @param value - Metric value
* @param unit - Unit of measurement (default: 'ms')
*/
trackMetric(metricName: string, value: number, unit: string = 'ms'): void {
this.addPageAction('CustomMetric', { metricName, value, unit });
}
}
Join the conversation! Your thoughts help the community grow.