【Rails×Datadog】auditsとDatadogを掛け合わせることで調査が楽になった話

# はじめに
プロダクトの規模が大きくなってモデルのデータが変更される入口が増えてくると、
- 何の操作によってデータが変更されたのか？
- 何のController、Actionで変更がされたのか
- 変更された入口はどこなのか？

というのを特定し辛くなり、不具合やお問い合わせの調査に苦戦します。この記事ではauditsとDatadogを用いて上記問題が特定できるようになり調査が楽になりました。

※audits、Datadogの導入手順については省略します。

# auditsとは
RailsのGemで、モデルの変更を監視してくれるライブラリです。
auditsテーブルには誰がその変更を行ったかを記録することができます。
使い方、導入
https://github.com/collectiveidea/audited

※コールバックが発生しない変更やactiverecord-importのようなbulk_insertではauditsにデータは残りませんので注意

auditsテーブルの内容(一部抜粋)
| カラム名 | 説明 
|---------|---------|
| id | システム内ID |
| auditable_id | 監視先のテーブルのID |
| auditable_type | 監視先のモデル名 |
| user_id | 変更を行ったUserのID |
| action | 変更時のアクション名(update、create、destroyなど) |
| audited_changes | 変更内容 |
| request_uuid | **リクエストのUUID(Datadogでどういう操作で変更されたのかを特定するのに使用)** |
| created_at | 変更日時 |

# Datadogについて
Datadogとはインフラ、アプリケーション、及びログを一元的に監視・分析するためのツールです。
https://docs.datadoghq.com/ja/containers/

この記事では主にDatadogのLogsにRailsアプリケーションのログを送信して調査することを前提にします。

# RailsのログをDatadogに送信する
DatadogにRailsのログを送信する設定を行います。ドキュメント通りlogrageを使います。
https://docs.datadoghq.com/ja/logs/log_collection/ruby/?tab=lograge

1\. Gemをインストールする
```ruby
gem 'lograge'
```
2\. Docker環境なのでビルドする
```shell
docker-compose build
docker-compose up -d
```

3\. logrageの設定をする

app/controllers/application_controller.rb
```ruby
# frozen_string_literal: true

class ApplicationController < ActionController::Base

  private

  def append_info_to_payload(payload)
    super

    payload[:login_user] = current_user&.id
    payload[:ua] = request.user_agent
    payload[:referer] = request.referer
    payload[:remote_ip] = request.remote_ip
    payload[:request_id] = request.uuid
  end
end

```

config/initializers/lograge.rbを作成。下記内容を記述します。
```ruby
Rails.application.configure do
  if Rails.env.production? || Rails.env.staging?
    config.lograge.enabled = true
    config.lograge.formatter = Lograge::Formatters::Json.new

    config.lograge.custom_options = lambda do |event|
      {
        ddtrace: 'rails',
        env: Rails.env,
        ddsource: ['app_name'],
        login_user: event.payload[:login_user], # ユーザーIDが必要であれば
        remote_ip: event.payload[:remote_ip],
        ua: event.payload[:ua],
        referer: event.payload[:referer],
        controller: event.payload[:controller],
        action: event.payload[:action],
        params: event.payload[:params].except('controller', 'action').to_json,
        method: event.payload[:method],
        path: event.payload[:path],
        status: event.payload[:status],
        duration: event.duration,
        time: Time.zone.now,
        request_id: event.payload[:request_id]
      }
    end
  end
end
```

4\. ログの冒頭タグを外す
※ActiveSupport::TaggedLogging はログの頭に勝手にタグを付与するので、ログメッセージが JSON と見做されなくなってしまいます。
config/environments/production.rb
```Diff
-  config.log_tags = [ :request_id ]
-  config.logger   = ActiveSupport::TaggedLogging.logger(STDOUT)
+  config.logger   = ActiveSupport::Logger.new(STDOUT)
```
※ステージング環境も同様に設定が必要であれば同様に設定をお願いします。

以上でDataDogにRailsのログを送信する準備が整いました。
DataDogのLogsに下記画像のようなデータが飛んでいることを確認して下さい。

![スクリーンショット 2025-01-15 10.37.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2031981/abd42972-eba2-4f3c-d4ef-dc0889ab49a2.png)


# 調査手順
お客様より例えばあるデータが勝手に変わっているというお問い合わせを受けた際の手順を記載します。

1\. auditsよりお問い合わせ内容のデータが変更されたレコードを特定します。
例えばあるユーザーのデータが書き換わっていたというお問い合わせを受けたとします。
下記のようなSQLで特定します。
```sql
SELECT
	*
FROM
	audits
WHERE
	audits.auditable_id = 1
	AND audits.auditable_type = 'User'
ORDER BY
	created_at DESC
```

2\. auditsのデータが表示されるので、audited_changesより変更内容を特定。該当のデータのrequest_uuidを控えます。

3\. Datadog > Logsの検索窓に下記のように入力して検索します。
```
@request_id:<uuid>
```

4\. 変更されたログを開きます。

ログより
- どの画面で(referer)
- どのController、Actionによって
- どの環境で(ua)
- どのユーザーが行ったのか(login_user)

が特定できるようになりました！