Rails Default Logger
Rails標準のロギング機能。開発環境では詳細なログ出力、本番環境では必要最小限のログを自動出力。SQLクエリ、コントローラアクション、レンダリング時間等の情報を含む。設定なしで即座に利用可能。
ライブラリ
Rails Default Logger
概要
Rails Default Loggerは、Ruby on Railsフレームワークに標準で組み込まれているロギングシステムです。ActiveSupport::TaggedLoggingをベースとし、Rails 7.1以降はActiveSupportBroadcastLoggerとして複数出力先への同時ログ配信機能を提供します。アプリケーション起動時から自動的に利用可能で、開発・本番環境での適切なログレベル管理、タグ付きログ、構造化ログ、SQLクエリ追跡など、Railsアプリケーション開発に特化した包括的なロギング機能を提供します。
詳細
Rails Default LoggerはRails 1.0から進化を続け、現在は業界最高水準のロギング機能を提供する成熟したシステムです。Rails.loggerとしてアクセス可能で、debug、info、warn、error、fatalの5段階ログレベルをサポートし、環境別の自動設定(開発:debug、本番:info)を行います。ActiveSupport::TaggedLoggingによりリクエスト毎のコンテキスト情報をタグとして付与でき、大規模Webアプリケーションでのログ追跡を効率化します。Rails 7.1のBroadcastLogger機能により、ファイル・標準出力・外部ログサービスへの同時出力も可能です。
主な特徴
- 自動環境設定: 開発・テスト・本番環境での最適なログレベル自動設定
- タグ付きログ: リクエストID、ユーザー情報等のコンテキスト自動付与
- SQLクエリ追跡: データベースクエリとソースコード位置の自動ログ
- 複数出力先対応: ファイル、標準出力、外部サービスへの同時配信
- フォーマット制御: 環境別の出力形式とタイムスタンプ自動管理
- パフォーマンス最適化: ブロック記法による遅延評価でオーバーヘッド削減
メリット・デメリット
メリット
- Railsフレームワークとの完全統合で設定不要の即座利用
- 環境別の自動最適化により開発から本番まで一貫したログ管理
- ActiveRecord統合によるSQLクエリとソースコード追跡の自動化
- タグ付きログによる大規模アプリケーションでの効率的ログ分析
- BroadcastLogger機能による複数ログ出力先の柔軟な設定
- Rails Guidesによる豊富なドキュメントとベストプラクティス
デメリット
- Rails以外のフレームワークでは利用不可のフレームワーク依存
- 高度な構造化ログや外部ログサービス統合には追加設定が必要
- デフォルト設定では大容量ログファイルの自動ローテーション未対応
- マイクロサービス環境での分散ログ収集には専用ツールが必要
- JSON形式など特殊フォーマットには追加のフォーマッター設定が必要
- Rails内部ログと独自ログの混在により、ログ量が増加しがち
参考ページ
- Rails Guides - Debugging Rails Applications
- Rails API Documentation - ActiveSupport::Logger
- Rails API Documentation - TaggedLogging
書き方の例
基本セットアップ
# Rails.loggerは自動的に利用可能(設定不要)
# アプリケーション起動時に自動初期化される
# 基本的なログ出力
Rails.logger.debug "デバッグ情報: #{variable.inspect}"
Rails.logger.info "情報: 処理が完了しました"
Rails.logger.warn "警告: メモリ使用量が高くなっています"
Rails.logger.error "エラー: データベース接続に失敗"
Rails.logger.fatal "致命的: アプリケーションを停止します"
# 設定確認
puts Rails.logger.level # 現在のログレベル
puts Rails.logger.class # BroadcastLogger (Rails 7.1+)
基本的なログ出力
# コントローラでのログ出力
class UsersController < ApplicationController
def index
logger.info "ユーザー一覧取得開始"
@users = User.all
logger.debug "取得ユーザー数: #{@users.count}"
# パフォーマンスの良いブロック記法
logger.debug { "詳細なユーザー情報: #{@users.map(&:attributes).inspect}" }
logger.info "ユーザー一覧取得完了"
end
def create
logger.info "ユーザー作成開始: #{params[:user]}"
@user = User.new(user_params)
if @user.save
logger.info "ユーザー作成成功: ID=#{@user.id}, 名前=#{@user.name}"
redirect_to @user, notice: 'ユーザーが作成されました'
else
logger.warn "ユーザー作成失敗: #{@user.errors.full_messages.join(', ')}"
render :new
end
end
private
def user_params
permitted_params = params.require(:user).permit(:name, :email, :age)
logger.debug { "許可されたパラメータ: #{permitted_params.inspect}" }
permitted_params
end
end
# モデルでのログ出力
class User < ApplicationRecord
after_create :log_user_creation
before_destroy :log_user_deletion
private
def log_user_creation
Rails.logger.info "新しいユーザーが作成されました: ID=#{id}, 名前=#{name}"
end
def log_user_deletion
Rails.logger.warn "ユーザーが削除されます: ID=#{id}, 名前=#{name}"
end
end
# ジョブでのログ出力
class EmailSendJob < ApplicationJob
def perform(user_id, email_type)
Rails.logger.info "メール送信ジョブ開始: ユーザーID=#{user_id}, タイプ=#{email_type}"
user = User.find(user_id)
Rails.logger.debug { "ユーザー情報: #{user.attributes.inspect}" }
case email_type
when 'welcome'
UserMailer.welcome_email(user).deliver_now
Rails.logger.info "ウェルカムメール送信完了: #{user.email}"
when 'reminder'
UserMailer.reminder_email(user).deliver_now
Rails.logger.info "リマインダーメール送信完了: #{user.email}"
else
Rails.logger.error "不明なメールタイプ: #{email_type}"
raise ArgumentError, "Unknown email type: #{email_type}"
end
rescue => e
Rails.logger.error "メール送信ジョブでエラー: #{e.message}"
Rails.logger.error "バックトレース: #{e.backtrace.first(5).join("\n")}"
raise
end
end
# サービスクラスでのログ出力
class PaymentService
def self.process_payment(user, amount)
Rails.logger.info "決済処理開始: ユーザー=#{user.id}, 金額=#{amount}"
begin
# 決済処理のシミュレーション
if amount > 0
Rails.logger.debug "決済金額検証: #{amount}円"
# 外部API呼び出し等の処理
Rails.logger.debug "外部決済API呼び出し中..."
result = external_payment_api(user, amount)
Rails.logger.info "決済処理成功: トランザクションID=#{result[:transaction_id]}"
{ success: true, transaction_id: result[:transaction_id] }
else
Rails.logger.warn "不正な決済金額: #{amount}"
{ success: false, error: "Invalid amount" }
end
rescue => e
Rails.logger.error "決済処理エラー: #{e.message}"
Rails.logger.error "ユーザー: #{user.id}, 金額: #{amount}"
{ success: false, error: e.message }
end
end
private
def self.external_payment_api(user, amount)
# 外部API呼び出しのシミュレーション
{ transaction_id: SecureRandom.uuid }
end
end
高度な設定(環境設定、ログレベル、出力先)
# config/application.rb での基本設定
module MyApplication
class Application < Rails::Application
# ログレベルの設定
config.log_level = :info
# ログファイルサイズの制限
config.log_file_size = 100.megabytes
# ログタグの設定
config.log_tags = [:request_id, :subdomain]
# SQLクエリコメントの有効化
config.active_record.query_log_tags_enabled = true
# 詳細なジョブログの有効化
config.active_job.verbose_enqueue_logs = true
end
end
# config/environments/development.rb
Rails.application.configure do
# 開発環境ではすべてのログを出力
config.log_level = :debug
# SQLクエリの詳細ログ有効化
config.active_record.verbose_query_logs = true
# 外部ファイルへのログ出力追加
config.logger = ActiveSupport::Logger.new("log/development_detailed.log")
config.logger.formatter = config.log_formatter
end
# config/environments/production.rb
Rails.application.configure do
# 本番環境では必要最小限のログ
config.log_level = :info
# 標準出力への出力(Docker/Kubernetes対応)
if ENV["RAILS_LOG_TO_STDOUT"].present?
config.logger = ActiveSupport::Logger.new(STDOUT)
config.logger.formatter = config.log_formatter
end
# ログフォーマットのカスタマイズ
config.log_formatter = proc do |severity, datetime, progname, msg|
"#{datetime.iso8601} [#{severity}] #{progname}: #{msg}\n"
end
end
# config/initializers/logging.rb - 高度なログ設定
Rails.application.configure do
# カスタムログファイルの追加
error_logger = ActiveSupport::Logger.new("log/errors.log")
error_logger.level = Logger::ERROR
# BroadcastLoggerによる複数出力先設定
if Rails.logger.respond_to?(:broadcast_to)
Rails.logger.broadcast_to(error_logger)
end
# ログローテーション設定
if Rails.env.production?
Rails.logger = ActiveSupport::Logger.new(
"log/production.log",
10, # 保持ファイル数
50.megabytes # ファイルサイズ上限
)
end
end
# アプリケーション固有のログタグ設定
Rails.application.configure do
config.log_tags = [
:request_id,
-> request { request.env["HTTP_USER_AGENT"]&.split&.first },
-> request { "User:#{request.session[:user_id]}" if request.session[:user_id] }
]
end
# 構造化ログの実装例
class StructuredLogger
def self.log_event(event_name, data = {})
log_data = {
timestamp: Time.current.iso8601,
event: event_name,
data: data,
request_id: Current.request_id,
user_id: Current.user&.id
}
Rails.logger.info "[STRUCTURED] #{log_data.to_json}"
end
end
# 使用例
StructuredLogger.log_event("user_registration", {
user_id: user.id,
email: user.email,
referrer: request.referer
})
# パフォーマンス測定ログ
class PerformanceLogger
def self.measure(operation_name)
start_time = Time.current
Rails.logger.debug "[PERF] #{operation_name} 開始"
result = yield
end_time = Time.current
duration = ((end_time - start_time) * 1000).round(2)
Rails.logger.info "[PERF] #{operation_name} 完了: #{duration}ms"
result
end
end
# 使用例
def expensive_operation
PerformanceLogger.measure("データベースクエリ") do
User.includes(:posts).where(active: true).limit(100)
end
end
エラーハンドリングとログ
# application_controller.rb でのグローバルエラーハンドリング
class ApplicationController < ActionController::Base
rescue_from StandardError, with: :handle_standard_error
rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found
private
def handle_standard_error(exception)
Rails.logger.error "予期しないエラーが発生: #{exception.class.name}"
Rails.logger.error "メッセージ: #{exception.message}"
Rails.logger.error "リクエストパス: #{request.path}"
Rails.logger.error "パラメータ: #{params.inspect}"
Rails.logger.error "バックトレース:"
exception.backtrace.first(10).each do |line|
Rails.logger.error " #{line}"
end
if Rails.env.production?
render json: { error: "Internal server error" }, status: 500
else
raise exception
end
end
def handle_not_found(exception)
Rails.logger.warn "リソースが見つかりません: #{exception.message}"
Rails.logger.warn "リクエストパス: #{request.path}"
Rails.logger.warn "パラメータ: #{params.inspect}"
render json: { error: "Not found" }, status: 404
end
end
# モデルでのバリデーションエラーログ
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
validates :name, presence: true, length: { minimum: 2 }
after_validation :log_validation_errors
private
def log_validation_errors
if errors.any?
Rails.logger.warn "User バリデーションエラー:"
errors.full_messages.each do |message|
Rails.logger.warn " - #{message}"
end
Rails.logger.warn " 属性: #{attributes.inspect}"
end
end
end
# ジョブでのリトライエラーハンドリング
class ReliableJob < ApplicationJob
retry_on StandardError, wait: :exponentially_longer, attempts: 5
def perform(data)
Rails.logger.info "ReliableJob 実行開始: #{data.inspect}"
begin
# 何らかの処理
process_data(data)
Rails.logger.info "ReliableJob 実行成功"
rescue => e
Rails.logger.error "ReliableJob でエラー (試行 #{executions}/#{self.class.max_attempts}): #{e.message}"
Rails.logger.error "データ: #{data.inspect}"
if executions >= self.class.max_attempts
Rails.logger.fatal "ReliableJob 最大試行回数に達しました: #{data.inspect}"
# 管理者への通知等
AdminMailer.job_failure_notification(self.class.name, data, e).deliver_now
end
raise # リトライを継続
end
end
private
def process_data(data)
# 処理のシミュレーション
raise "Random error" if rand < 0.3
Rails.logger.debug "データ処理完了: #{data}"
end
end
# カスタム例外処理クラス
class ErrorHandler
def self.handle_and_log(error, context = {})
error_id = SecureRandom.uuid
Rails.logger.error "エラーID: #{error_id}"
Rails.logger.error "エラークラス: #{error.class.name}"
Rails.logger.error "エラーメッセージ: #{error.message}"
Rails.logger.error "コンテキスト: #{context.inspect}"
Rails.logger.error "発生時刻: #{Time.current.iso8601}"
if error.respond_to?(:backtrace) && error.backtrace
Rails.logger.error "バックトレース:"
error.backtrace.first(15).each_with_index do |line, index|
Rails.logger.error " #{index + 1}: #{line}"
end
end
# 外部エラートラッキングサービスへの送信(例:Sentry、Bugsnag)
# ExternalErrorService.report(error, error_id: error_id, context: context)
error_id
end
end
# 使用例
begin
dangerous_operation()
rescue => e
error_id = ErrorHandler.handle_and_log(e, {
user_id: current_user&.id,
request_path: request.path,
params: params.to_unsafe_h
})
render json: {
error: "処理中にエラーが発生しました",
error_id: error_id
}, status: 500
end
実用例(本番運用対応)
# config/initializers/logging_enhancements.rb
# 本番環境での高度なログ設定
# 1. リクエスト毎のユニークIDをログに含める
Rails.application.config.log_tags = [
:request_id,
-> request {
user_id = request.session[:user_id] || request.headers['X-User-ID']
"User:#{user_id}" if user_id
},
-> request { "IP:#{request.remote_ip}" }
]
# 2. SQLクエリの詳細ログ(開発環境のみ)
if Rails.env.development?
Rails.application.config.active_record.query_log_tags_enabled = true
Rails.application.config.active_record.query_log_tags = [
:application,
:controller,
:action,
:job,
{
current_user: -> { Current.user&.id },
request_id: -> { Current.request_id }
}
]
end
# 3. ジョブの詳細ログ設定
Rails.application.config.active_job.verbose_enqueue_logs = true
# 4. 外部ログサービス統合(例:Datadog、Splunk)
if Rails.env.production? && ENV['EXTERNAL_LOG_ENDPOINT']
require 'net/http'
require 'json'
class ExternalLogService
def self.send_log(level, message, context = {})
log_data = {
timestamp: Time.current.iso8601,
level: level,
message: message,
application: 'my-rails-app',
environment: Rails.env,
hostname: Socket.gethostname,
**context
}
Thread.new do
begin
uri = URI(ENV['EXTERNAL_LOG_ENDPOINT'])
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = "Bearer #{ENV['LOG_API_TOKEN']}"
request.body = log_data.to_json
http.request(request)
rescue => e
Rails.logger.error "外部ログサービス送信エラー: #{e.message}"
end
end
end
end
end
# アプリケーション全体のログミドルウェア
class LoggingMiddleware
def initialize(app)
@app = app
end
def call(env)
request = ActionDispatch::Request.new(env)
start_time = Time.current
Rails.logger.info "[REQUEST] #{request.method} #{request.path}"
Rails.logger.info "[REQUEST] User-Agent: #{request.user_agent}"
Rails.logger.info "[REQUEST] Params: #{request.params.except('controller', 'action').inspect}"
status, headers, response = @app.call(env)
end_time = Time.current
duration = ((end_time - start_time) * 1000).round(2)
Rails.logger.info "[RESPONSE] Status: #{status}, Duration: #{duration}ms"
# 遅いリクエストの警告
if duration > 1000
Rails.logger.warn "[SLOW_REQUEST] #{request.method} #{request.path} took #{duration}ms"
end
[status, headers, response]
end
end
# ミドルウェアの追加
Rails.application.config.middleware.use LoggingMiddleware
# データベースクエリの監視
ActiveSupport::Notifications.subscribe 'sql.active_record' do |name, started, finished, unique_id, data|
duration = ((finished - started) * 1000).round(2)
if duration > 100 # 100ms以上のクエリを警告
Rails.logger.warn "[SLOW_QUERY] #{duration}ms: #{data[:sql]}"
Rails.logger.warn "[SLOW_QUERY] Binds: #{data[:binds].inspect}" if data[:binds]
end
if duration > 1000 # 1秒以上のクエリはエラー
Rails.logger.error "[VERY_SLOW_QUERY] #{duration}ms: #{data[:sql]}"
end
end
# バックグラウンドジョブの監視
ActiveSupport::Notifications.subscribe 'perform.active_job' do |name, started, finished, unique_id, data|
duration = ((finished - started) * 1000).round(2)
job = data[:job]
Rails.logger.info "[JOB] #{job.class.name} completed in #{duration}ms"
if duration > 30000 # 30秒以上のジョブを警告
Rails.logger.warn "[SLOW_JOB] #{job.class.name} took #{duration}ms"
Rails.logger.warn "[SLOW_JOB] Arguments: #{job.arguments.inspect}"
end
end
# メモリ使用量の監視(開発環境)
if Rails.env.development?
Rails.application.config.after_initialize do
Thread.new do
loop do
memory_usage = `ps -o rss= -p #{Process.pid}`.to_i / 1024 # MB
Rails.logger.debug "[MEMORY] Current usage: #{memory_usage}MB"
if memory_usage > 500 # 500MB以上で警告
Rails.logger.warn "[MEMORY] High memory usage: #{memory_usage}MB"
end
sleep 60 # 1分間隔
end
end
end
end
# ヘルスチェック用ログ
class HealthCheckController < ApplicationController
def index
Rails.logger.info "[HEALTH_CHECK] Application status check"
checks = {
database: check_database,
redis: check_redis,
external_api: check_external_api
}
all_healthy = checks.values.all?
Rails.logger.info "[HEALTH_CHECK] Results: #{checks.inspect}"
if all_healthy
render json: { status: 'healthy', checks: checks }
else
Rails.logger.error "[HEALTH_CHECK] Some services are unhealthy"
render json: { status: 'unhealthy', checks: checks }, status: 503
end
end
private
def check_database
ActiveRecord::Base.connection.execute('SELECT 1')
true
rescue => e
Rails.logger.error "[HEALTH_CHECK] Database error: #{e.message}"
false
end
def check_redis
if defined?(Redis)
Redis.new.ping == 'PONG'
else
true # Redis未使用の場合はOK
end
rescue => e
Rails.logger.error "[HEALTH_CHECK] Redis error: #{e.message}"
false
end
def check_external_api
# 外部API等の重要な依存サービスのチェック
true
rescue => e
Rails.logger.error "[HEALTH_CHECK] External API error: #{e.message}"
false
end
end