Ruby アプリケーション向け New Relic APM 設定ガイド
Ruby アプリケーションでの New Relic APM 導入は、Ruby エコシステムの豊富なフレームワークとライブラリとの深い統合を通じて、詳細なパフォーマンス監視を実現します。Ruby エージェントは、Rails の規約に従った自動計測、Rubyの動的な性質を活かした柔軟な監視オプション、豊富なgemエコシステムとの統合を提供します。
Ruby エージェントの特徴
New Relic Ruby エージェントは、Ruby の動的メソッド呼び出しとメタプログラミング機能を活用して、既存のコードを最小限の変更で監視対象にできます。メソッドエイリアシングとモジュール拡張により、フレームワークの内部動作を詳細に追跡し、パフォーマンスボトルネックを特定します。
Rails、Sinatra、Padrino、Hanami などの主要フレームワークとの自動統合により、MVC パターンの各層、Active Record のデータベース操作、Action Cable のWebSocket通信まで包括的に監視できます。また、Sidekiq、Resque、Delayed Job などのバックグラウンドジョブ処理も詳細に追跡します。
導入前の準備
環境要件の確認
Ruby エージェントは以下のバージョンをサポートしています:
- Ruby バージョン: 2.7 以降(Ruby 3.x 推奨)
- フレームワーク: Rails 6.0 以降、Sinatra 2.0 以降
- Web サーバー: Puma、Unicorn、Passenger、Thin
- プラットフォーム: Linux、macOS、Windows(WSL2)
Bundler プロジェクトの確認
Ruby アプリケーションでは、Bundler を使用した依存関係管理が前提となります。
エージェントのインストール
Gemfile への追加
# Gemfile
gem 'newrelic_rpm'
# 開発・テスト環境での詳細なログが必要な場合
group :development, :test do
gem 'newrelic_rpm', require: false
end
Bundle install の実行
bundle install
Rails での自動設定
Rails アプリケーションでは、gem の追加だけで基本的な監視が自動的に有効になります。
基本設定の構成
newrelic.yml 設定ファイルの生成
# 設定ファイルの生成
bundle exec newrelic install YOUR_LICENSE_KEY "My Ruby Application"
newrelic.yml の詳細設定
# config/newrelic.yml
common: &default_settings
license_key: '<%= ENV["NEW_RELIC_LICENSE_KEY"] %>'
app_name: My Ruby Application
distributed_tracing:
enabled: true
span_events:
enabled: true
max_samples_stored: 2000
production:
<<: *default_settings
app_name: My Ruby Application (Production)
log_level: info
# トランザクション トレーサー設定
transaction_tracer:
enabled: true
transaction_threshold: apdex_f
record_sql: obfuscated
explain_enabled: true
explain_threshold: 0.5
stack_trace_threshold: 0.5
# エラー コレクター設定
error_collector:
enabled: true
capture_source: true
ignore_errors: "ActionController::RoutingError,ActionController::InvalidAuthenticityToken"
# ブラウザ監視設定
browser_monitoring:
auto_instrument: true
development:
<<: *default_settings
app_name: My Ruby Application (Development)
log_level: debug
developer_mode: true
test:
<<: *default_settings
app_name: My Ruby Application (Test)
monitor_mode: false
環境変数による設定
# .env ファイル
NEW_RELIC_LICENSE_KEY=your_license_key_here
NEW_RELIC_APP_NAME="My Ruby Application"
NEW_RELIC_ENABLED=true
NEW_RELIC_LOG_LEVEL=info
Rails アプリケーションでの統合
application.rb での設定
# config/application.rb
module MyApp
class Application < Rails::Application
# New Relic の初期化設定
config.after_initialize do
if defined?(NewRelic::Agent)
NewRelic::Agent.manual_start(
app_name: ENV['NEW_RELIC_APP_NAME'],
license_key: ENV['NEW_RELIC_LICENSE_KEY']
)
end
end
end
end
コントローラーでのカスタム計測
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include NewRelic::Agent::Instrumentation::ControllerInstrumentation
before_action :set_newrelic_user_attributes
around_action :monitor_performance
private
def set_newrelic_user_attributes
if current_user
NewRelic::Agent.set_user_attributes(
user_id: current_user.id,
user_name: current_user.name,
user_email: current_user.email
)
NewRelic::Agent.add_custom_attributes(
user_role: current_user.role,
user_plan: current_user.plan&.name,
user_created_at: current_user.created_at
)
end
end
def monitor_performance
start_time = Time.current
yield
ensure
if defined?(NewRelic::Agent)
duration = Time.current - start_time
NewRelic::Agent.record_custom_metric("Custom/Controller/ResponseTime", duration)
end
end
end
# app/controllers/orders_controller.rb
class OrdersController < ApplicationController
add_transaction_tracer :create, category: :controller
add_transaction_tracer :complex_calculation, category: :task
def index
NewRelic::Agent.add_custom_attributes(
orders_count: current_user.orders.count,
filter_applied: params[:filter].present?
)
@orders = NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/Orders/FetchUserOrders") do
current_user.orders.includes(:items).page(params[:page])
end
end
def create
@order = NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/Orders/Create") do
OrderCreationService.new(current_user, order_params).call
end
if @order.persisted?
# 注文成功のメトリクス記録
NewRelic::Agent.record_custom_metric("Custom/Orders/Created", 1)
NewRelic::Agent.record_custom_metric("Custom/Revenue", @order.total_amount)
# カスタムイベントの記録
NewRelic::Agent.record_custom_event("OrderCreated", {
order_id: @order.id,
customer_id: current_user.id,
total_amount: @order.total_amount,
item_count: @order.items.count
})
redirect_to @order, notice: 'Order was successfully created.'
else
NewRelic::Agent.record_custom_metric("Custom/Orders/CreationFailed", 1)
render :new
end
end
private
def complex_calculation
# 複雑な計算処理の監視
result = NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/Orders/ComplexCalculation") do
# 時間のかかる計算処理
sleep(1)
42
end
NewRelic::Agent.add_custom_attributes(calculation_result: result)
result
end
end
Active Record モデルでの監視
# app/models/order.rb
class Order < ApplicationRecord
include NewRelic::Agent::MethodTracer
belongs_to :user
has_many :order_items
after_create :record_order_metrics
after_update :record_status_change
def calculate_total
NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/Order/CalculateTotal") do
order_items.sum { |item| item.price * item.quantity }
end
end
def process_payment
NewRelic::Agent.start_transaction(name: "OrderPayment/process") do
payment_result = PaymentGateway.charge(
amount: total_amount,
card_token: payment_token
)
if payment_result.success?
NewRelic::Agent.record_custom_metric("Custom/Payments/Success", 1)
update!(status: 'paid', payment_id: payment_result.id)
else
NewRelic::Agent.notice_error("Payment failed: #{payment_result.error}")
NewRelic::Agent.record_custom_metric("Custom/Payments/Failed", 1)
raise PaymentError, payment_result.error
end
end
end
private
def record_order_metrics
NewRelic::Agent.record_custom_event("OrderCreated", {
order_id: id,
user_id: user_id,
total_amount: total_amount,
items_count: order_items.count
})
end
def record_status_change
if saved_change_to_status?
NewRelic::Agent.record_custom_event("OrderStatusChanged", {
order_id: id,
old_status: status_change[0],
new_status: status_change[1]
})
end
end
add_method_tracer :calculate_total, 'Custom/Order/calculate_total'
add_method_tracer :process_payment, 'Custom/Order/process_payment'
end
サービスオブジェクトでの監視
# app/services/order_creation_service.rb
class OrderCreationService
include NewRelic::Agent::MethodTracer
def initialize(user, order_params)
@user = user
@order_params = order_params
end
def call
NewRelic::Agent.start_transaction(name: "OrderCreation/execute") do
ActiveRecord::Base.transaction do
create_order
validate_inventory
process_payment
send_confirmation_email
@order
end
rescue => e
NewRelic::Agent.notice_error(e)
raise
end
end
private
def create_order
NewRelic::Agent.add_custom_attributes(
service_action: 'create_order',
user_id: @user.id
)
@order = @user.orders.build(@order_params)
@order.save!
NewRelic::Agent.record_custom_metric("Custom/OrderCreation/OrderCreated", 1)
end
def validate_inventory
@order.order_items.each do |item|
unless item.product.sufficient_inventory?(item.quantity)
raise InsufficientInventoryError, "Not enough inventory for #{item.product.name}"
end
end
NewRelic::Agent.record_custom_metric("Custom/OrderCreation/InventoryValidated", 1)
end
def process_payment
payment_service = PaymentService.new(@order)
payment_service.process
NewRelic::Agent.record_custom_metric("Custom/OrderCreation/PaymentProcessed", 1)
end
def send_confirmation_email
OrderMailer.confirmation_email(@order).deliver_later
NewRelic::Agent.record_custom_metric("Custom/OrderCreation/EmailQueued", 1)
end
add_method_tracer :call, 'Custom/OrderCreationService/call'
add_method_tracer :create_order, 'Custom/OrderCreationService/create_order'
add_method_tracer :validate_inventory, 'Custom/OrderCreationService/validate_inventory'
end
バックグラウンドジョブの監視
Sidekiq 統合
# app/workers/order_processing_worker.rb
class OrderProcessingWorker
include Sidekiq::Worker
include NewRelic::Agent::Instrumentation::Sidekiq
sidekiq_options retry: 3, backtrace: true
def perform(order_id)
NewRelic::Agent.start_transaction(name: "OrderProcessing/#{self.class.name}") do
order = Order.find(order_id)
NewRelic::Agent.add_custom_attributes(
order_id: order.id,
order_status: order.status,
worker_class: self.class.name
)
process_order(order)
NewRelic::Agent.record_custom_metric("Custom/Workers/OrderProcessed", 1)
rescue => e
NewRelic::Agent.notice_error(e)
raise
end
end
private
def process_order(order)
NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/Workers/ProcessOrder") do
# 注文処理ロジック
order.update!(status: 'processing')
sleep(2) # 模擬処理時間
order.update!(status: 'completed')
end
end
end
Delayed Job 統合
# app/jobs/email_notification_job.rb
class EmailNotificationJob < ActiveJob::Base
include NewRelic::Agent::Instrumentation::ActiveJob
queue_as :default
def perform(user_id, notification_type)
user = User.find(user_id)
NewRelic::Agent.add_custom_attributes(
user_id: user.id,
notification_type: notification_type,
job_class: self.class.name
)
case notification_type
when 'welcome'
send_welcome_email(user)
when 'order_confirmation'
send_order_confirmation(user)
end
NewRelic::Agent.record_custom_metric("Custom/Jobs/EmailSent", 1)
end
private
def send_welcome_email(user)
NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/Email/Welcome") do
UserMailer.welcome_email(user).deliver_now
end
end
def send_order_confirmation(user)
NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/Email/OrderConfirmation") do
order = user.orders.recent.first
OrderMailer.confirmation_email(order).deliver_now
end
end
end
Sinatra アプリケーションでの統合
# app.rb
require 'sinatra'
require 'newrelic_rpm'
class MyApp < Sinatra::Base
configure do
NewRelic::Agent.manual_start(
app_name: 'My Sinatra App',
license_key: ENV['NEW_RELIC_LICENSE_KEY']
)
end
before do
NewRelic::Agent.add_custom_attributes(
request_method: request.request_method,
request_path: request.path,
user_agent: request.user_agent
)
end
get '/api/users/:id' do
NewRelic::Agent.start_transaction(name: 'Web/Users/show') do
user_id = params[:id]
user = NewRelic::Agent::MethodTracer.trace_execution_scoped("Database/Users/find") do
User.find(user_id)
end
if user
NewRelic::Agent.add_custom_attributes(user_id: user.id)
content_type :json
user.to_json
else
status 404
{ error: 'User not found' }.to_json
end
end
rescue => e
NewRelic::Agent.notice_error(e)
status 500
{ error: 'Internal server error' }.to_json
end
post '/api/orders' do
NewRelic::Agent.start_transaction(name: 'Web/Orders/create') do
order_data = JSON.parse(request.body.read)
order = NewRelic::Agent::MethodTracer.trace_execution_scoped("Business/Orders/create") do
Order.create!(order_data)
end
NewRelic::Agent.record_custom_event("OrderCreated", {
order_id: order.id,
total_amount: order.total_amount
})
status 201
content_type :json
order.to_json
end
rescue => e
NewRelic::Agent.notice_error(e)
status 500
{ error: 'Failed to create order' }.to_json
end
end
カスタム計測の詳細実装
メソッドレベルの詳細監視
# lib/performance_monitor.rb
module PerformanceMonitor
extend ActiveSupport::Concern
included do
extend NewRelic::Agent::MethodTracer
end
class_methods do
def monitor_method(method_name, metric_name = nil)
metric_name ||= "Custom/#{self.name}/#{method_name}"
add_method_tracer method_name, metric_name
end
def monitor_class_method(method_name, metric_name = nil)
metric_name ||= "Custom/#{self.name}/#{method_name}"
add_method_tracer method_name, metric_name, push_scope: false
end
end
def with_monitoring(operation_name)
start_time = Time.current
result = NewRelic::Agent::MethodTracer.trace_execution_scoped("Custom/#{operation_name}") do
yield
end
duration = Time.current - start_time
NewRelic::Agent.record_custom_metric("Custom/#{operation_name}/Duration", duration)
result
rescue => e
NewRelic::Agent.notice_error(e)
NewRelic::Agent.record_custom_metric("Custom/#{operation_name}/Error", 1)
raise
end
end
ビジネスメトリクスの包括的な記録
# app/services/analytics_service.rb
class AnalyticsService
include PerformanceMonitor
def self.record_user_action(user, action, metadata = {})
NewRelic::Agent.record_custom_event("UserAction", {
user_id: user.id,
action: action,
timestamp: Time.current.to_i,
**metadata
})
NewRelic::Agent.record_custom_metric("Custom/UserActions/#{action}", 1)
end
def self.record_business_metric(metric_name, value, attributes = {})
NewRelic::Agent.record_custom_metric("Business/#{metric_name}", value)
if attributes.any?
NewRelic::Agent.record_custom_event("BusinessMetric", {
metric_name: metric_name,
value: value,
**attributes
})
end
end
def self.track_conversion(funnel_step, user, metadata = {})
NewRelic::Agent.record_custom_event("ConversionFunnel", {
user_id: user.id,
step: funnel_step,
timestamp: Time.current.to_i,
**metadata
})
NewRelic::Agent.record_custom_metric("Custom/Conversions/#{funnel_step}", 1)
end
monitor_class_method :record_user_action
monitor_class_method :record_business_metric
monitor_class_method :track_conversion
end
パフォーマンス最適化
設定の最適化
# config/newrelic.yml での最適化設定
production:
# トランザクション サンプリングの調整
transaction_tracer:
transaction_threshold: 1.0 # 1秒以上のトランザクションのみトレース
top_n: 20 # 保存するトレース数を制限
# スロークエリの設定
slow_sql:
enabled: true
explain_threshold: 0.5 # 0.5秒以上のクエリを説明
# エラー レート制限
error_collector:
max_stack_trace_lines: 50 # スタックトレースの行数制限
# メトリクス制限
agent_limits:
transaction_traces_nodes: 2000
custom_attributes: 64
custom_events_max_samples_stored: 1200
メモリ使用量の監視
# config/initializers/performance_monitoring.rb
if Rails.env.production?
Thread.new do
loop do
begin
# メモリ使用量の監視
memory_usage = `ps -o rss= -p #{Process.pid}`.to_i * 1024 # バイト単位
NewRelic::Agent.record_custom_metric("Custom/Memory/RSS", memory_usage)
# GC統計の記録
gc_stat = GC.stat
NewRelic::Agent.record_custom_metric("Custom/GC/Count", gc_stat[:count])
NewRelic::Agent.record_custom_metric("Custom/GC/HeapUsed", gc_stat[:heap_used])
NewRelic::Agent.record_custom_metric("Custom/GC/HeapLength", gc_stat[:heap_length])
sleep 60 # 1分間隔
rescue => e
Rails.logger.error "Performance monitoring error: #{e.message}"
end
end
end
end
本番環境でのデプロイメント
Dockerfile での設定
FROM ruby:3.2-alpine
WORKDIR /app
# Gemfile のコピーと bundle install
COPY Gemfile Gemfile.lock ./
RUN bundle install --without development test
# アプリケーションコードのコピー
COPY . .
# New Relic 設定の環境変数
ENV NEW_RELIC_ENABLED=true
ENV NEW_RELIC_LOG_LEVEL=info
# Rails アプリケーションの起動
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
Kubernetes での設定
apiVersion: apps/v1
kind: Deployment
metadata:
name: ruby-app
spec:
template:
spec:
containers:
- name: app
image: myrubyapp:latest
env:
- name: NEW_RELIC_LICENSE_KEY
valueFrom:
secretKeyRef:
name: newrelic-secret
key: license_key
- name: NEW_RELIC_APP_NAME
value: "MyRubyApp (Kubernetes)"
- name: RAILS_ENV
value: "production"
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
Capistrano でのデプロイメント連携
# config/deploy.rb
after 'deploy:updated', 'newrelic:notice_deployment'
namespace :newrelic do
desc 'Notify New Relic of deployment'
task :notice_deployment do
on roles(:app) do
within release_path do
execute :bundle, :exec, 'newrelic', 'deployments',
'--revision', fetch(:current_revision),
'--description', "Deployed at #{Time.current}"
end
end
end
end
トラブルシューティング
エージェント状態の診断
# app/controllers/health_controller.rb
class HealthController < ApplicationController
def newrelic_status
status = {
agent_enabled: defined?(NewRelic::Agent),
connected: NewRelic::Agent.agent&.connected?,
app_name: NewRelic::Agent.config[:app_name],
license_key_configured: NewRelic::Agent.config[:license_key].present?
}
if defined?(NewRelic::Agent)
status.merge!(
agent_version: NewRelic::VERSION::STRING,
host: NewRelic::Agent.config[:host],
port: NewRelic::Agent.config[:port]
)
end
render json: status
end
end
Ruby アプリケーションでの New Relic APM 導入により、Ruby エコシステムの豊富なフレームワークとライブラリを活かした詳細なパフォーマンス監視を実現できます。Rails の規約に従った自動計測と柔軟なカスタマイズオプションにより、アプリケーションの継続的な最適化を効果的に支援します。
関連記事: 分散トレーシング設定関連記事: APM設定完全リファレンス