Railsのログを理解する-ログの基礎知識-

Railsにおけるログの出力方法について理解が浅く、勘違いをしていた。
自分と同じような人が１０人くらいはそうなので、基礎知識から簡単な表示方法をまとめる。

## 環境
Ruby 2.6.3
Rails 5.2.3

## 目次
* [ログと標準出力](#sec1)
* [Railsにおけるログの取り扱い](#sec2)
* [ログへの書き込み](#sec3)
* [ログへの書き込み](#sec3)

<a id="sec1"></a>
## ログと標準出力
今回大きな間違えをしていたのが、**ログ**と**標準出力**を混同してしまっていたこと。
pなどで変数情報を表示する際に、pumaの出力(以下のようなrails sを立ち上げた際の画面)に表示されるものは、ログファイルに保存されるものと同じだと勘違いしていた。

```
Processing by SamplesController#index as HTML
  "-----デバッグ情報------"
  Rendering samples/index.html.erb within layouts/application
  Rendered samples/index.html.erb within layouts/application (1.3ms)
Completed 200 OK in 18ms (Views: 16.5ms | ActiveRecord: 0.0ms)
```
今回分かったことは

- 上の画面はあくまでもpumaの**標準出力**であるため、pやputsといったrubyの標準出力メソッドの結果が表示される
- ログファイルはRailsが**必要とする情報を、必要な形**で記録する
- もし**標準出力**が**必要**であればログに記録させることもできる(ログの内容はカスタム可能)



<a id="sec2"></a>
## Railsにおけるログの取り扱い

#### ログファイルの保存場所
Railsでは環境(production, development, test)ごとに、Rails.root/log/（環境）.logというログファイルが生成される(例：log/production.log)

#### ログレベルについて
Railsが生成するログには以下の５段階のレベルというものが存在する。
ログレベルは情報の必要度が低→高の順で、0→5の数値が充てられている。

|ログレベル|名称|内容|
|:---:|:---:|:--:|
|0|debug|デバッグ情報など|
|1|info|一般的な情報|
|2|warn|警告|
|3|error|制御可能なエラー|
|4|fatal|プログラムをクラッシュさせるような制御不可能なエラー|
|5|unknown|常に記録されるべき不明なエラー|
>https://docs.ruby-lang.org/ja/latest/library/logger.html
>

設定したログレベル以上のもののみファイルに保存される。（例：設定したログレベルが3の場合は3,4,5が保存される）

Railsのデフォルトのログレベルは0となっており、何も変更しなければ全てのログが上記のファイルに保存される。

#### デフォルトのログ出力レベルを変更する

config/環境/設定ファイルでconfig.log_levelを指定することにより、環境ごとに標準で保存するログレベルを変更できる。
config.log_levelをシンボル型の名称(:debug, :fatal)で指定する。

これにより、development環境では全てのログを表示するが、production環境ではエラーログのみ保存するなどと言ったことができる。

```ruby:config/environments/production.rb
# production環境ではログレベルがerror以上（error|fatal|unknown）のみ保存される
config.log_level = :error
```
```ruby:config/environments/development.rb
# development環境では全てのログレベルを保存する
# ※デフォルトがdebug=レベル0になっているため、実際にはlog_level = :debugを設定する必要はない
config.log_level = :debug
```



<a id="sec3"></a>
## ログへの書き込み
#### コード内でログへの書き込みを行う
コードの中で任意のログを書き込みたい場合には以下のようにRails.loggerか省略形のloggerを使用する。コントローラー、モデルいずれの場所でも使用可。

logger.(ログレベルの名称)の引数にメッセージを渡すことで、そのログレベルを持ったメッセージを保存することができる。


```ruby
class ArticlesController < ApplicationController
  # ...

  def create
    @article = Article.new(article_params)
    # ログレベルがdebugのメッセージを保存する
    logger.debug "新しい記事: #{@article.attributes.inspect}"
    logger.debug "記事が正しいかどうか: #{@article.valid?}"  

    if @article.save
      flash[:notice] = 'Article was successfully created.'
      logger.debug "記事は正常に保存され、ユーザーをリダイレクト中..."
      redirect_to @article, notice: '記事は正常に作成されました。'
    else
      logger.warn "記事の保存に失敗しました。" 
      render :new
    end
  end

  # ...
end
```
>https://railsguides.jp/debugging_rails_applications.html

#### 任意の場所でログレベルを変更する
保存するログレベルは、configだけでなく、他のコードの中でも変更することができる。
デフォルトではerrorのみを保存し、特定の箇所のみデバッグ情報を表示するなどと言ったことが可能。
ログレベルの変更はlogger(Rails.logger).leverで設定変更が可能。これは場所を問わない。

```ruby:config/environments/production.rb
# ここではデフォルトでdebugは表示されないように設定
config.log_level = :error
```

```ruby
class ArticlesController < ApplicationController
  # ...

  def create
    # ここでlogレベルを変更することにより、この処理ではデバッグログを保存することができる。
    Rails.logger.level = 0
    @article = Article.new(article_params)
    logger.debug "新しい記事: #{@article.attributes.inspect}"
    logger.debug "記事が正しいかどうか: #{@article.valid?}"  

    if @article.save
      flash[:notice] =  'Article was successfully created.'
      logger.debug "記事は正常に保存され、ユーザーをリダイレクト中..."
      redirect_to @article, notice: '記事は正常に作成されました。'
    else
      logger.warn 
      render :new
    end
  end

  # ...
end
```

<a id="sec3"></a>
## 豆知識：パフォーマンスへの影響

以下ような基本的なログ書き方は、比較的動作が重いStringオブジェクトのインスタンス化と、実行に時間のかかる変数の式展開 (interpolation) が行われるため、大量に呼び出された場合にはパフォーマンスに影響が出る。

そして、これはログの出力レベルがdebugを含んでいなかった場合でも評価されてしまうため、本番環境でログレベルを1（=info）以上にしていた場合にも処理時間に影響を与えることになる。

```ruby
logger.debug "Person attributes hash: #{@person.attributes.inspect}"
```

これを書き換えて、ブロックの処理とすることにより、ブロックの評価は出力レベルが設定レベル以上になった場合のみ行われるので、無駄な処理時間を減らすことができる。

```ruby
logger.debug {"Person attributes hash: #{@person.attributes.inspect}"}
```