PHP アプリケーション向け New Relic APM 設定ガイド
PHP アプリケーションでの New Relic APM 導入は、Webアプリケーションの包括的な監視と最適化を実現します。PHP エージェントは、PHP エンジンレベルでの統合、主要フレームワークとの自動統合、レガシーシステムから最新のPHP 8.x まで幅広いバージョンサポートを提供します。
PHP エージェントの特徴
New Relic PHP エージェントは、Zend Engine の拡張機能として動作し、PHP の実行時動作を詳細に監視します。オペコードレベルでの計測により、関数呼び出し、データベースクエリ、外部API呼び出しを自動的に追跡し、パフォーマンスボトルネックを特定します。
Laravel、Symfony、CodeIgniter、CakePHP、Drupal、WordPress などの主要フレームワークとCMSとの自動統合により、既存のアプリケーションに最小限の変更で詳細な監視を導入できます。また、Composer ベースのモダンなPHPアプリケーションから従来のレガシーシステムまで幅広く対応しています。
導入前の準備
システム要件の確認
PHP エージェントは以下の環境をサポートしています:
- PHP バージョン: 7.0 以降(PHP 8.x 推奨)
- Web サーバー: Apache、Nginx、IIS
- PHP SAPI: mod_php、PHP-FPM、CGI
- オペレーティングシステム: Linux、macOS、Windows
必要な権限の確認
PHP エージェントのインストールには、PHP 拡張機能のインストール権限とWebサーバーの再起動権限が必要です。
エージェントのインストール
Linux(RPM ベース)での自動インストール
# New Relic リポジトリの追加
sudo rpm -Uvh http://yum.newrelic.com/pub/newrelic/el5/x86_64/newrelic-repo-5-3.noarch.rpm
# エージェントのインストール
sudo yum install newrelic-php5
# 設定スクリプトの実行
sudo newrelic-install install
Linux(DEB ベース)での自動インストール
# New Relic リポジトリの追加
echo 'deb http://apt.newrelic.com/debian/ newrelic non-free' | sudo tee /etc/apt/sources.list.d/newrelic.list
wget -O- https://download.newrelic.com/548C16BF.gpg | sudo apt-key add -
# パッケージリストの更新とインストール
sudo apt-get update
sudo apt-get install newrelic-php5
# 設定スクリプトの実行
sudo newrelic-install install
Docker コンテナでのインストール
FROM php:8.2-apache
# New Relic PHP エージェントのインストール
RUN curl -L https://download.newrelic.com/php_agent/release/newrelic-php5-10.13.0.3-linux.tar.gz | tar -C /tmp -zx \
&& export NR_INSTALL_USE_CP_NOT_LN=1 \
&& export NR_INSTALL_SILENT=1 \
&& /tmp/newrelic-php5-*/newrelic-install install \
&& rm -rf /tmp/newrelic-php5-* /tmp/nrinstall*
# PHP拡張機能の有効化
RUN echo "extension=newrelic.so" > /usr/local/etc/php/conf.d/newrelic.ini
# New Relic設定の追加
RUN echo "newrelic.license = \"YOUR_LICENSE_KEY\"" >> /usr/local/etc/php/conf.d/newrelic.ini \
&& echo "newrelic.appname = \"My PHP App\"" >> /usr/local/etc/php/conf.d/newrelic.ini
COPY . /var/www/html/
Composer を使用したランタイムインストール
一部の環境では、Composer パッケージを使用してエージェントを統合できます。
composer require newrelic/monolog-enricher
基本設定の構成
php.ini での設定
; New Relic PHP エージェントの基本設定
extension=newrelic.so
; ライセンスキーとアプリケーション名
newrelic.license = "YOUR_LICENSE_KEY_HERE"
newrelic.appname = "My PHP Application"
; エージェントの有効化
newrelic.enabled = true
; ログ設定
newrelic.logfile = "/var/log/newrelic/php_agent.log"
newrelic.loglevel = "info"
; トランザクション追跡設定
newrelic.transaction_tracer.enabled = true
newrelic.transaction_tracer.threshold = 2.0
newrelic.transaction_tracer.detail = 1
newrelic.transaction_tracer.record_sql = "obfuscated"
newrelic.transaction_tracer.explain_enabled = true
newrelic.transaction_tracer.explain_threshold = 500
; エラー収集設定
newrelic.error_collector.enabled = true
newrelic.error_collector.record_database_errors = true
; ブラウザ監視の自動注入
newrelic.browser_monitoring.auto_instrument = true
; 分散トレーシング
newrelic.distributed_tracing.enabled = true
newrelic.span_events.enabled = true
環境変数による設定
# .env ファイル
NEW_RELIC_LICENSE_KEY=your_license_key_here
NEW_RELIC_APP_NAME="My PHP Application"
NEW_RELIC_ENABLED=true
NEW_RELIC_LOG_LEVEL=info
フレームワーク別統合
Laravel アプリケーション
Laravel では、サービスプロバイダーとミドルウェアを使用して詳細な監視を実装できます。
config/app.php での設定
<?php
// config/app.php
return [
'providers' => [
// その他のプロバイダー
App\Providers\NewRelicServiceProvider::class,
],
];
New Relic サービスプロバイダーの作成
<?php
// app/Providers/NewRelicServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;
class NewRelicServiceProvider extends ServiceProvider
{
public function boot()
{
if (extension_loaded('newrelic')) {
// アプリケーション名の設定
newrelic_set_appname(config('app.name') . ' (' . config('app.env') . ')');
// ユーザー情報の追加
if (auth()->check()) {
newrelic_set_user_attributes(
auth()->id(),
auth()->user()->name ?? '',
auth()->user()->email ?? ''
);
}
}
}
}
ミドルウェアでのカスタム計測
<?php
// app/Http/Middleware/NewRelicMiddleware.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class NewRelicMiddleware
{
public function handle(Request $request, Closure $next)
{
if (extension_loaded('newrelic')) {
// トランザクション名の設定
$route = $request->route();
if ($route) {
$transactionName = $route->getActionMethod() ?: $route->uri();
newrelic_name_transaction("Web/" . $transactionName);
}
// カスタム属性の追加
newrelic_add_custom_parameter('request.method', $request->method());
newrelic_add_custom_parameter('request.url', $request->url());
if ($request->user()) {
newrelic_add_custom_parameter('user.id', $request->user()->id);
newrelic_add_custom_parameter('user.role', $request->user()->role ?? '');
}
}
$response = $next($request);
if (extension_loaded('newrelic')) {
// レスポンス属性の追加
newrelic_add_custom_parameter('response.status', $response->getStatusCode());
}
return $response;
}
}
Eloquent でのデータベース監視
<?php
// app/Models/Order.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
public static function boot()
{
parent::boot();
// データベースクエリの監視
static::creating(function ($order) {
if (extension_loaded('newrelic')) {
newrelic_record_custom_event('OrderCreated', [
'order_id' => $order->id,
'customer_id' => $order->customer_id,
'total_amount' => $order->total_amount,
]);
}
});
}
public function scopeExpensive($query, $threshold = 1000)
{
if (extension_loaded('newrelic')) {
newrelic_add_custom_parameter('query.type', 'expensive_orders');
newrelic_add_custom_parameter('query.threshold', $threshold);
}
return $query->where('total_amount', '>=', $threshold);
}
}
Symfony アプリケーション
Symfony では、イベントリスナーとサービスを使用して監視を統合します。
services.yaml での設定
# config/services.yaml
services:
App\EventListener\NewRelicListener:
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 255 }
- { name: kernel.event_listener, event: kernel.response }
- { name: kernel.event_listener, event: kernel.exception }
イベントリスナーの実装
<?php
// src/EventListener/NewRelicListener.php
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
class NewRelicListener
{
public function onKernelRequest(RequestEvent $event)
{
if (!extension_loaded('newrelic') || !$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
// トランザクション名の設定
$route = $request->attributes->get('_route');
if ($route) {
newrelic_name_transaction("Web/" . $route);
}
// リクエスト属性の追加
newrelic_add_custom_parameter('request.method', $request->getMethod());
newrelic_add_custom_parameter('request.path', $request->getPathInfo());
}
public function onKernelResponse(ResponseEvent $event)
{
if (!extension_loaded('newrelic') || !$event->isMainRequest()) {
return;
}
$response = $event->getResponse();
newrelic_add_custom_parameter('response.status_code', $response->getStatusCode());
}
public function onKernelException(ExceptionEvent $event)
{
if (!extension_loaded('newrelic')) {
return;
}
$exception = $event->getThrowable();
newrelic_notice_error($exception->getMessage(), $exception);
}
}
WordPress 統合
WordPress では、プラグインまたはテーマの functions.php でエージェントを統合します。
<?php
// wp-content/themes/your-theme/functions.php または プラグインファイル
class NewRelicWordPressIntegration
{
public function __construct()
{
if (extension_loaded('newrelic')) {
add_action('init', [$this, 'setTransactionName']);
add_action('wp_head', [$this, 'addBrowserTiming']);
add_action('wp_login', [$this, 'recordLogin'], 10, 2);
add_filter('wp_die_handler', [$this, 'recordError']);
}
}
public function setTransactionName()
{
global $wp_query;
$transactionName = 'WordPress/';
if (is_front_page()) {
$transactionName .= 'FrontPage';
} elseif (is_single()) {
$transactionName .= 'Post/' . get_post_type();
} elseif (is_page()) {
$transactionName .= 'Page';
} elseif (is_category()) {
$transactionName .= 'Category';
} elseif (is_admin()) {
$transactionName .= 'Admin/' . (isset($_GET['page']) ? $_GET['page'] : 'Dashboard');
} else {
$transactionName .= 'Other';
}
newrelic_name_transaction($transactionName);
// カスタム属性の追加
if (is_user_logged_in()) {
$user = wp_get_current_user();
newrelic_set_user_attributes($user->ID, $user->display_name, $user->user_email);
}
}
public function addBrowserTiming()
{
if (function_exists('newrelic_get_browser_timing_header')) {
echo newrelic_get_browser_timing_header();
}
}
public function recordLogin($user_login, $user)
{
newrelic_record_custom_event('UserLogin', [
'user_id' => $user->ID,
'user_login' => $user_login,
'user_role' => implode(',', $user->roles),
]);
}
public function recordError($handler)
{
if (is_wp_error($GLOBALS['wp_error'] ?? null)) {
$error = $GLOBALS['wp_error'];
newrelic_notice_error($error->get_error_message());
}
return $handler;
}
}
new NewRelicWordPressIntegration();
データベース監視
PDO でのカスタム監視
<?php
class MonitoredPDO extends PDO
{
public function __construct($dsn, $username = null, $password = null, $options = null)
{
parent::__construct($dsn, $username, $password, $options);
if (extension_loaded('newrelic')) {
newrelic_add_custom_parameter('database.dsn', $this->sanitizeDsn($dsn));
}
}
public function query($statement, $mode = PDO::ATTR_DEFAULT_FETCH_MODE, ...$args)
{
if (extension_loaded('newrelic')) {
$start = microtime(true);
newrelic_record_datastore_segment(function() use ($statement, $mode, $args) {
return parent::query($statement, $mode, ...$args);
}, [
'product' => 'MySQL', // またはPostgreSQL, SQLite など
'collection' => $this->extractTableName($statement),
'operation' => $this->extractOperation($statement),
'host' => $this->getHost(),
'portPathOrId' => $this->getPort(),
'databaseName' => $this->getDatabaseName(),
]);
$duration = microtime(true) - $start;
newrelic_record_custom_metric('Custom/Database/QueryTime', $duration);
}
return parent::query($statement, $mode, ...$args);
}
private function extractTableName($sql)
{
if (preg_match('/(?:FROM|INTO|UPDATE|JOIN)\s+`?(\w+)`?/i', $sql, $matches)) {
return $matches[1];
}
return 'unknown';
}
private function extractOperation($sql)
{
if (preg_match('/^\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)/i', $sql, $matches)) {
return strtoupper($matches[1]);
}
return 'UNKNOWN';
}
}
カスタム計測の実装
ビジネスメトリクスの記録
<?php
class ECommerceMetrics
{
public static function recordOrder($order)
{
if (!extension_loaded('newrelic')) {
return;
}
// カスタムメトリクスの記録
newrelic_record_custom_metric('Custom/Orders/Total', 1);
newrelic_record_custom_metric('Custom/Revenue/Amount', $order['total_amount']);
newrelic_record_custom_metric('Custom/Orders/ItemCount', count($order['items']));
// カスタムイベントの記録
newrelic_record_custom_event('OrderCompleted', [
'order_id' => $order['id'],
'customer_id' => $order['customer_id'],
'total_amount' => $order['total_amount'],
'item_count' => count($order['items']),
'payment_method' => $order['payment_method'],
'shipping_method' => $order['shipping_method'],
]);
// カスタム属性の追加
newrelic_add_custom_parameter('business.order_value', $order['total_amount']);
newrelic_add_custom_parameter('business.customer_segment', $this->getCustomerSegment($order['customer_id']));
}
public static function recordPayment($payment)
{
if (!extension_loaded('newrelic')) {
return;
}
$success = $payment['status'] === 'completed';
newrelic_record_custom_metric('Custom/Payments/Total', 1);
newrelic_record_custom_metric($success ? 'Custom/Payments/Success' : 'Custom/Payments/Failed', 1);
if ($success) {
newrelic_record_custom_metric('Custom/Payments/Amount', $payment['amount']);
} else {
newrelic_notice_error("Payment failed: " . $payment['error_message']);
}
}
}
関数レベルの詳細監視
<?php
function monitoredFunction($name, callable $callback, array $attributes = [])
{
if (!extension_loaded('newrelic')) {
return $callback();
}
// カスタム属性の設定
foreach ($attributes as $key => $value) {
newrelic_add_custom_parameter($key, $value);
}
$start = microtime(true);
try {
$result = $callback();
$duration = microtime(true) - $start;
newrelic_record_custom_metric("Custom/Function/{$name}/Duration", $duration);
newrelic_record_custom_metric("Custom/Function/{$name}/Success", 1);
return $result;
} catch (Exception $e) {
newrelic_notice_error($e);
newrelic_record_custom_metric("Custom/Function/{$name}/Error", 1);
throw $e;
}
}
// 使用例
$result = monitoredFunction('ProcessOrder', function() use ($orderData) {
return $this->processOrder($orderData);
}, [
'order.type' => $orderData['type'],
'order.priority' => $orderData['priority']
]);
パフォーマンス最適化
PHP-FPM での設定最適化
; php.ini でのパフォーマンス設定
newrelic.framework = "laravel" ; または "symfony", "drupal", "wordpress" など
newrelic.webtransaction.name.remove_trailing_path = true
newrelic.webtransaction.name.functions = ""
newrelic.transaction_tracer.max_segments_web = 2000
newrelic.transaction_tracer.max_segments_cli = 100000
; 本番環境での最適化
newrelic.daemon.loglevel = "error"
newrelic.daemon.max_threads = 8
newrelic.daemon.metric_address_time = 120
メモリ使用量の監視
<?php
class PerformanceMonitor
{
public static function recordMemoryUsage()
{
if (!extension_loaded('newrelic')) {
return;
}
$memoryUsage = memory_get_usage(true);
$peakMemory = memory_get_peak_usage(true);
newrelic_record_custom_metric('Custom/Memory/Current', $memoryUsage);
newrelic_record_custom_metric('Custom/Memory/Peak', $peakMemory);
// メモリ使用量が閾値を超えた場合のアラート
if ($memoryUsage > 128 * 1024 * 1024) { // 128MB
newrelic_record_custom_event('HighMemoryUsage', [
'memory_usage' => $memoryUsage,
'memory_limit' => ini_get('memory_limit'),
'script_name' => $_SERVER['SCRIPT_NAME'] ?? 'CLI',
]);
}
}
}
// リクエスト終了時にメモリ使用量を記録
register_shutdown_function([PerformanceMonitor::class, 'recordMemoryUsage']);
本番環境でのデプロイメント
Apache での設定
# .htaccess または Apache 設定ファイル
<IfModule mod_php5.c>
php_value newrelic.appname "MyApp (Production)"
php_value newrelic.enabled true
php_value newrelic.browser_monitoring.auto_instrument true
</IfModule>
<IfModule mod_php7.c>
php_value newrelic.appname "MyApp (Production)"
php_value newrelic.enabled true
php_value newrelic.browser_monitoring.auto_instrument true
</IfModule>
Nginx + PHP-FPM での設定
# nginx.conf
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param PHP_VALUE "
newrelic.appname=MyApp (Production)
newrelic.enabled=1
newrelic.browser_monitoring.auto_instrument=1
";
include fastcgi_params;
}
Docker Compose での設定
version: '3.8'
services:
web:
build: .
environment:
- NEW_RELIC_LICENSE_KEY=${NEW_RELIC_LICENSE_KEY}
- NEW_RELIC_APP_NAME=MyApp (Docker)
volumes:
- .:/var/www/html
ports:
- "80:80"
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: myapp
トラブルシューティング
エージェント状態の確認
<?php
// デバッグ用のエンドポイント
if ($_GET['debug'] === 'newrelic' && extension_loaded('newrelic')) {
echo "<h2>New Relic Status</h2>";
echo "<p>Extension loaded: " . (extension_loaded('newrelic') ? 'Yes' : 'No') . "</p>";
echo "<p>Agent version: " . phpversion('newrelic') . "</p>";
if (function_exists('newrelic_get_linking_metadata')) {
echo "<p>Linking metadata: " . json_encode(newrelic_get_linking_metadata()) . "</p>";
}
echo "<h3>Configuration</h3>";
$config = [
'newrelic.license',
'newrelic.appname',
'newrelic.enabled',
'newrelic.daemon.address',
'newrelic.transaction_tracer.enabled'
];
foreach ($config as $key) {
echo "<p>{$key}: " . ini_get($key) . "</p>";
}
}
PHP アプリケーションでの New Relic APM 導入により、Webアプリケーションの包括的な監視と継続的な最適化を実現できます。フレームワーク固有の統合と詳細なカスタマイズにより、PHP エコシステムの多様なアプリケーションパターンに対応した効果的な監視システムを構築できます。
関連記事: Ruby APM設定関連記事: APM設定完全リファレンス