Node.js アプリケーション向け New Relic APM 設定ガイド
Node.js アプリケーションでの New Relic APM 導入は、イベント駆動アーキテクチャと非同期処理の特性を活かした高度な監視ソリューションを提供します。Node.js エージェントは、非同期コンテキストの追跡、マイクロサービス間の連携監視、リアルタイム性能分析を実現します。
Node.js エージェントの特徴
New Relic Node.js エージェントは、V8 エンジンレベルでの統合により、JavaScript の非同期実行モデルを深く理解した監視を提供します。Promise、async/await、コールバック関数などの非同期パターンを自動的に追跡し、複雑な実行フローを可視化します。
Express、Koa、Fastify、NestJS などの主要フレームワークとの自動統合により、ルーティング、ミドルウェア、HTTP リクエスト処理を包括的に監視できます。また、WebSocket 通信、Server-Sent Events、GraphQL リゾルバーなど、現代的なWeb技術パターンにも対応しています。
導入前の準備
環境要件の確認
Node.js エージェントは Node.js 14.x 以降をサポートしており、LTS バージョンでの使用を推奨します。ES6+ モジュール、TypeScript、CommonJS の各モジュールシステムに対応しています。
プロジェクト構成の確認
パッケージ管理には npm、yarn、pnpm などの標準的なツールを使用でき、モノレポ構成でも適切に動作します。
エージェントのインストール
npm を使用したインストール
npm install newrelic
yarn を使用したインストール
yarn add newrelic
TypeScript プロジェクトでの型定義
npm install --save-dev @types/newrelic
基本設定の構成
設定ファイルの作成
プロジェクトルートに newrelic.js
設定ファイルを作成します。
// newrelic.js
'use strict'
exports.config = {
app_name: ['My Node.js Application'],
license_key: 'YOUR_LICENSE_KEY_HERE',
logging: {
level: 'info',
filepath: 'stdout'
},
allow_all_headers: true,
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*',
'response.headers.x*'
]
}
}
環境変数による設定
# .env ファイル
NEW_RELIC_APP_NAME="My Node.js Application"
NEW_RELIC_LICENSE_KEY="your_license_key_here"
NEW_RELIC_LOG_LEVEL="info"
NEW_RELIC_ENABLED="true"
エージェントの初期化
アプリケーション起動時の初期化
アプリケーションの最初に New Relic エージェントを読み込む必要があります。
// app.js または index.js の最上部
require('newrelic');
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
ES6 モジュールでの初期化
// main.js
import './newrelic.js';
import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000);
TypeScript での初期化
// main.ts
import './newrelic';
import express, { Request, Response } from 'express';
import newrelic from 'newrelic';
const app = express();
app.get('/', (req: Request, res: Response) => {
res.send('Hello World!');
});
app.listen(3000);
フレームワーク別設定
Express.js アプリケーション
Express フレームワークでは自動的にルート監視が有効になります。
const newrelic = require('newrelic');
const express = require('express');
const app = express();
// ミドルウェアの自動監視
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// ルートハンドラーの自動監視
app.get('/users/:id', async (req, res) => {
try {
const user = await getUserById(req.params.id);
res.json(user);
} catch (error) {
newrelic.noticeError(error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// エラーハンドリングミドルウェア
app.use((err, req, res, next) => {
newrelic.noticeError(err);
res.status(500).json({ error: 'Something went wrong!' });
});
Koa.js アプリケーション
const newrelic = require('newrelic');
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();
// ルートの定義
router.get('/api/data', async (ctx) => {
try {
const data = await fetchData();
ctx.body = data;
} catch (error) {
newrelic.noticeError(error);
ctx.status = 500;
ctx.body = { error: 'Internal Server Error' };
}
});
app.use(router.routes());
NestJS アプリケーション
// main.ts
import './newrelic';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
// controller例
import { Controller, Get } from '@nestjs/common';
import * as newrelic from 'newrelic';
@Controller('users')
export class UsersController {
@Get()
async findAll() {
return newrelic.startBackgroundTransaction('Users/findAll', async () => {
// ビジネスロジック
return await this.userService.findAll();
});
}
}
非同期処理の監視
Promise チェーンの監視
const newrelic = require('newrelic');
async function processOrder(orderId) {
return newrelic.startBackgroundTransaction('Order/process', async () => {
try {
const order = await fetchOrder(orderId);
const payment = await processPayment(order);
const fulfillment = await scheduleFulfillment(order);
newrelic.recordCustomEvent('OrderProcessed', {
orderId: orderId,
amount: order.amount,
processingTime: Date.now() - order.createdAt
});
return { order, payment, fulfillment };
} catch (error) {
newrelic.noticeError(error);
throw error;
}
});
}
async/await パターンの監視
const newrelic = require('newrelic');
class DataProcessor {
async processData(data) {
return newrelic.startSegment('DataProcessor/processData', true, async () => {
const validatedData = await this.validateData(data);
const enrichedData = await this.enrichData(validatedData);
const savedData = await this.saveData(enrichedData);
return savedData;
});
}
async validateData(data) {
return newrelic.startSegment('DataProcessor/validateData', true, async () => {
// データ検証ロジック
await new Promise(resolve => setTimeout(resolve, 100));
return data;
});
}
}
カスタム計測の実装
カスタムメトリクスの記録
const newrelic = require('newrelic');
class MetricsService {
recordBusinessMetrics(orderData) {
// ビジネスメトリクスの記録
newrelic.recordMetric('Custom/Revenue', orderData.amount);
newrelic.recordMetric('Custom/OrdersProcessed', 1);
// カスタムイベントの記録
newrelic.recordCustomEvent('BusinessEvent', {
eventType: 'order_completed',
revenue: orderData.amount,
customerId: orderData.customerId,
productCategory: orderData.category
});
}
trackPerformance(functionName, duration) {
newrelic.recordMetric(`Custom/Performance/${functionName}`, duration);
}
}
関数レベルの詳細監視
const newrelic = require('newrelic');
// デコレータ関数の作成
function traced(name) {
return function(target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
return newrelic.startSegment(name || `${target.constructor.name}/${propertyKey}`, true, () => {
return originalMethod.apply(this, args);
});
};
return descriptor;
};
}
class PaymentService {
@traced('PaymentService/processPayment')
async processPayment(paymentData) {
// 支払い処理ロジック
const result = await this.callPaymentGateway(paymentData);
return result;
}
}
データベース監視の設定
MongoDB 監視
const newrelic = require('newrelic');
const mongoose = require('mongoose');
// MongoDB接続の監視
mongoose.connect('mongodb://localhost/myapp');
const UserSchema = new mongoose.Schema({
name: String,
email: String
});
UserSchema.pre('save', function() {
newrelic.addCustomAttribute('mongodb.operation', 'save');
newrelic.addCustomAttribute('mongodb.collection', 'users');
});
const User = mongoose.model('User', UserSchema);
Redis 監視
const newrelic = require('newrelic');
const redis = require('redis');
const client = redis.createClient();
// Redis操作の監視
const originalGet = client.get.bind(client);
client.get = function(key, callback) {
return newrelic.startSegment('Redis/get', true, () => {
newrelic.addCustomAttribute('redis.key', key);
return originalGet(key, callback);
});
};
パフォーマンス最適化
エージェント設定の調整
// newrelic.js
exports.config = {
app_name: ['My Node.js Application'],
license_key: 'YOUR_LICENSE_KEY_HERE',
// パフォーマンス最適化設定
transaction_tracer: {
enabled: true,
transaction_threshold: 2.0,
top_n: 20,
record_sql: 'obfuscated'
},
error_collector: {
enabled: true,
ignore_status_codes: [404, 401]
},
slow_sql: {
enabled: true,
max_samples: 10
},
// カスタム属性の制御
attributes: {
enabled: true,
include: ['request.parameters.*'],
exclude: ['request.headers.cookie']
}
}
メモリ使用量の監視
const newrelic = require('newrelic');
setInterval(() => {
const memUsage = process.memoryUsage();
newrelic.recordMetric('Custom/Memory/RSS', memUsage.rss);
newrelic.recordMetric('Custom/Memory/HeapUsed', memUsage.heapUsed);
newrelic.recordMetric('Custom/Memory/HeapTotal', memUsage.heapTotal);
}, 60000); // 1分間隔
マイクロサービス環境での設定
分散トレーシングの有効化
// newrelic.js
exports.config = {
distributed_tracing: {
enabled: true
},
span_events: {
enabled: true,
max_samples_stored: 2000
}
}
サービス間通信の監視
const newrelic = require('newrelic');
const axios = require('axios');
class ApiClient {
async callExternalService(endpoint, data) {
return newrelic.startSegment('ExternalService/call', true, async () => {
try {
const response = await axios.post(endpoint, data, {
headers: {
// 分散トレーシングヘッダーの自動追加
...newrelic.getTraceMetadata()
}
});
newrelic.addCustomAttribute('external.service.endpoint', endpoint);
newrelic.addCustomAttribute('external.service.response_code', response.status);
return response.data;
} catch (error) {
newrelic.noticeError(error);
throw error;
}
});
}
}
本番環境でのデプロイメント
PM2 との統合
// ecosystem.config.js
module.exports = {
apps: [{
name: 'my-app',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
NEW_RELIC_ENABLED: 'true',
NEW_RELIC_APP_NAME: 'MyApp (Production)'
}
}]
}
Docker コンテナでの設定
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY newrelic.js ./
COPY . .
# New Relic エージェントが最初に読み込まれるように設定
CMD ["node", "-r", "./newrelic.js", "app.js"]
Kubernetes での設定
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-app
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
env:
- name: NEW_RELIC_APP_NAME
value: "MyApp (Kubernetes)"
- name: NEW_RELIC_LICENSE_KEY
valueFrom:
secretKeyRef:
name: newrelic-secret
key: license_key
トラブルシューティング
エージェント状態の確認
const newrelic = require('newrelic');
// エージェント状態の確認
console.log('New Relic Agent Version:', newrelic.version);
console.log('Application Name:', newrelic.config.app_name);
console.log('License Key Configured:', !!newrelic.config.license_key);
// アプリケーション登録状態の確認
newrelic.getTransaction(); // 現在のトランザクション取得
ログとデバッグ
// newrelic.js でのデバッグ設定
exports.config = {
logging: {
level: 'trace', // より詳細なログ出力
filepath: './newrelic_agent.log'
}
}
Node.js アプリケーションでの New Relic APM 導入により、非同期処理とイベント駆動アーキテクチャの特性を活かした詳細な監視を実現できます。適切な設定により、現代的なWebアプリケーションの複雑な実行パターンを包括的に可視化し、継続的な最適化を支援します。
関連記事: .NET APM設定関連記事: 分散トレーシング設定