15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails デバッグ・エラー処理について整理してみた

Last updated at Posted at 2015-02-14

先日Railsのデバッグ中にあれこれ疑問に思った事について整理してみました。

ざっくり書くと、以下の内容です。

  • 想定外の入力に対する備え
  • ログ出力を動的に変更するには
  • pry-byebugのブレークポイントの設定方法
  • RSpecで特定のテストケースを行うには

1. 想定外の入力に備える

Railsを使っていて入力に対するチェックが甘くなるのは、私だけでしょうか?
つい例外に頼ってしまったりとか...

想定外の入力が発生した時点で、その入力に対して適切に処理を行う事で、エラーを抑止できたり、エラーの原因特定が早まる可能性があります。

def foo(name)
  model = Model.where(name: name).first
  puts model.name # modelがnilなら例外.
end
def foo(name)
  model = Model.where(name: name).first
  if model
    puts model.name
  else
    logger.warn("#{name} is not found.") # ログファイルに情報を残しておく.
  end
end

1.1 防御的プログラミング

CODE COMPLETE 防御的プログラミングの章には、以下の点が述べられています。

  • 外部ソース(ネットワークやDB、ユーザー)からの入力データ、メソッドへの入力データが妥当である(想定内の範囲か、nilでないかなど)かを確認する。
  • 想定外の入力に対して、どのように対処するかの指針。

以下は、基本に立ち返り、今後取り入れようとしている3つの指針です。

a. assertの利用.

ミッションクリティカルなシステムでは、入力チェックは必須ですが、Webアプリケーションでも、エラーの原因を早期に特定したり、エラーそのものに発生させないために有効だと思います。

rubyでは言語仕様にassertは無い様なので、raiseの利用を検討しています。
(Test::Unitを利用する方法もあるようです。(Ruby/assertしたい))

def foo(name)
  raise "name is nil."  unless name
end
  • assertの指針
  • 発生してはならない状況に対して、アサーションを利用する
  • assertには実行コード(副作用のある処理,状態が変化する処理)を組み込まない

b. エラー発生時の指針

  • ログファイルにエラー内容を書き込む。
  • エラーコード(又は例外)を返す

c. バリゲード

  • パブリックメソッドで受け取るデータは安全でないと見なし、データを検査し、適切なデータに変換する。入力データは、入力された時に適切な型に変換する。

2. ログを適切に記録する

エラー時や状態変化時にログを出力する事で、デバッガに頼らずとも、早期に原因が特定できる場合があります。

2.1 Railsでのログレベル

Railsの場合は、以下のレベルがあります。

ログレベル 説明
unknown 不明なエラー
fatal 致命的なエラー
error エラー
warn 警告
info 通知
debug デバック情報

どのような事象に対して、どのログレベルを設定するかを、チームで決定しておく必要があります。

2.2. 出力レベルの設定

config/environments/#{RAILS_ENV}.rbのファイルの以下で変更できます。

# config/environments/development.rb

config.log_level = :debug

2.3. 出力レベルの動的変更

実行中のRailsに対して、ログレベルを動的には変更できないようなので、
productionでデバッグログを出力したい場合は、動的に変更できるようにします。

参考:
http://rails.hatenadiary.jp/entry/2013/02/06/181430

2.4. Rails標準以外のクラスに対するログ出力

modelやcontrollerからは、loggerでログ出力できますが、独自クラスの場合は
Rails.loggerにします。

logger.info('info')
Rails.logger.info('info')

2.5. ログローテーション

ログファイルを1つにすると、ファイルの表示に時間がかかったり、ディスクスペースも圧迫してしまうので、ローテーションするようにします。

参考:
http://rails.hatenadiary.jp/entry/2013/02/06/201302

3. 例外

準備中

4. binding.pry(pry-byebug)を効果的に使う

pry-byebugによるデバッグはものすごく便利です。しかしデバッガに頼りすぎると、動作確認に時間がかかる事もあるため、ログ等で事前に原因を把握できた方が良いと思います。マルチスレッド環境の場合、タイミングに依存する事もあるため、デバッガでの原因特定が難しい事もあります。けどやっぱりデバッガは便利です。

Railsの開発効率をあげる - Pryを使ってRailsのコンソールをパワーアップ & デバッグをする

4.1 pryの入力の効率化(ちょっとだけ)

以下の設定をしておくと、改行で直前のコマンドが実行でき少しだけ楽ができます。s,s,s,s,...とかしなくてもs, 改行, 改行,改行で同じ事ができるようになります。

# ~/.pryrc

# Hit Enter to repeat last command
Pry::Commands.command /^$/, "repeat last command" do
  _pry_.run_command Pry.history.to_a.last
end

参考:
https://github.com/deivid-rodriguez/pry-byebug

4.2 処理停止中のブレークポイントの設定

pry中にブレークポイントを設定するには次のようにします。

# pry中

> break 51 # 51行目にブレークポイント設定
> break --delete 2 # 2番目のブレークポイントを削除
> break app/models/users.rb:15 指定したファイルの15行目にブレークポイント
> break --condition 2 x > 2 # 2番目のブレークポイントに条件設定
> break --condition 2 str != \"abc\" # 文字列の場合は要エスケープ
> break --condition 2 # 2番目のブレークポイントの条件解除
> break --show 2 # 2番目のブレークポイント確認
> break --disable-all # 削除.

参考:https://github.com/deivid-rodriguez/pry-byebug

5. 単体テストを作成する

参考:
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」

参考:
Everyday Rails - RSpecによるRailsテスト入門

5.1 specでの無駄なテストは省く

5.2. 特定のファイルのみ試験する

rake spec SPEC="spec/path/hoge_spec.rb"

参考:http://qiita.com/snaka/items/e0808f9cfbad87c49b4e

5.3. 特定のテストケースのみ試験する 1

rake spec SPEC="spec/path/hoge_spec.rb:39" # 39行目のメソッドをテストする

参考:http://labs.timedia.co.jp/2013/03/rspec.html

5.4. 特定のテストケースのみ試験する 2

# spec_helper.rb
  RSpec.configure do |config|
    config.filter_run :focus => true
    config.run_all_when_everything_filtered = true
  end
it "testcase", focus: true do # focus:trueのみ試験される.
end

参考:http://qiita.com/snaka/items/e0808f9cfbad87c49b4e

5.5 DatabaseCleaner利用時にデータの再投入の無駄を省く

DatabaseCleanerを利用している場合、次のように設定すると、マスターデータの内容を削除せず試験をする事ができます。

RSpec.configure do |config|
  config.before(:suite) do
    DatabaseClean.erclean_with :truncation, { except: %w(m_table1 m_table2) }

参考: http://blog.inouetakuya.info/entry/2013/11/10/201040

15
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?