Laravel5.6 で操作ログを自動で記録する

Laravel のミドルウェアを使ってサーバへのアクションを自動で記録してみます

アクションログはデータベースへ保存
ログイン済みであればユーザＩＤも自動で記録しときます


#ログテーブル作成

まずはアクションログを記録するテーブルを作成します

```php
$ php artisan make:migration create_actlogs_table --create=actlogs
$ vi database/migrations/*actlogs*
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateActlogsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('actlogs', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->nullable();
            $table->string('route')->nullable();
            $table->string('url')->nullable();
            $table->string('method')->nullable();
            $table->integer('status')->unsigned()->nullable();
            $table->text('message')->nullable();
            $table->string('remote_addr')->nullable();
            $table->string('user_agent')->nullable();
            $table->timestamps();

            $table->index(['created_at']);
            $table->index(['user_id']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('actlogs');
    }
}


$ php artisan migrate:refresh --seed
```

テーブルをmigrateで作成しときます。
ログに記録するのは以下
　・ユーザID
　・route 設定の name でつけといた名前
　・要求パス
　・要求メソッド（GETとかPOSTとか）
　・要求結果 200 OK とか 301 Move とか
　・要求内容（あれば）
　・クライアントIPアドレス
　・ブラウザ名（UserAgent）
　・作成時間（DB記録時間）

#テーブルに対応するモデルも作成

```php:app/Actlog.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Actlog extends Model
{
    protected $connection;

    const UPDATED_AT = null;

    public function __construct(array $attributes = []) {
        parent::__construct($attributes);
        $connection = env('DB_LOG_CONNECTION', env('DB_CONNECTION', 'mysql'));
    }

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'user_id',
        'route',
        'url',
        'method',
        'status',
        'message',
        'remote_addr',
        'user_agent',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
    ];
}
```

必要ないと思うけど。。一応ＤＢのコネクションを分けてます
あと、UPDATEはしないので記録しないように設定しときます

#ミドルウェア作成

実際にログを記録するミドルウェアを作成です

```bash
$ php artisan make:middleware ActlogMiddleware
```

```php:app/Http/Middleware/ActlogMiddleware.php
<?php

namespace App\Http\Middleware;

use Closure;
use App\Actlog;
use \Route;

class ActlogMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        $this->actlog($request, $response->status());

        return $response;
    }

    public function actlog($request, $status)
    {
        $user = $request -> user();
        $data = [
            'user_id' => $user ? $user->id : null,
            'route' => Route::currentRouteName(),
            'url' => $request -> path(),
            'method' => $request -> method(),
            'status' => $status,
            'message' => count($request->toArray()) != 0 ? json_encode($request->toArray()) : null,
            'remote_addr' => $request -> ip(),
            'user_agent' => $request -> userAgent(),
        ];
        Actlog::create($data);
    }
}
```

ルーティング情報を取得するために Route を利用してます
ログインユーザは$request -> user() から取得
要求内容はJson形式で保存しときます


#ミドルウェア登録

```php

$ vi app/Http/Kernel.php

    protected $middleware = [
        
        ～～～～
        
★      \App\Http\Middleware\ActlogMiddleware::class,
    ];

```

$middleware の中に追加すると グローバルとして必ず呼ばれるミドルウェアとして登録されるみたいです
詳しくはこちら
https://readouble.com/laravel/5.6/ja/middleware.html


#動作確認

DBはPostgresqlを利用している手順です。MySQLの方はMySQLのお作法で確認してくださいです

##１）ＤＢに接続してテーブル定義とテーブル内容を確認

```sql
$ psql -U lara_user -d lara -h 127.0.0.1
lara=# \d actlogs;
                                           テーブル "public.actlogs"
     列      |               型               | 照合順序 | Null 値を許容 |             デフォルト
-------------+--------------------------------+----------+---------------+-------------------------------------
 id          | integer                        |          | not null      | nextval('actlogs_id_seq'::regclass)
 user_id     | integer                        |          |               |
 route       | character varying(255)         |          |               |
 url         | character varying(255)         |          |               |
 method      | character varying(255)         |          |               |
 status      | integer                        |          |               |
 message     | text                           |          |               |
 remote_addr | character varying(255)         |          |               |
 user_agent  | character varying(255)         |          |               |
 created_at  | timestamp(0) without time zone |          |               |
 updated_at  | timestamp(0) without time zone |          |               |
インデックス:
    "actlogs_pkey" PRIMARY KEY, btree (id)
    "actlogs_created_at_index" btree (created_at)
    "actlogs_user_id_index" btree (user_id)

lara=#

lara=# select * from actlogs;
 id | user_id | route | url | method | status | message | remote_addr | user_agent | created_at | updated_at
----+---------+-------+-----+--------+--------+---------+-------------+------------+------------+------------
(0 行)

lara=#
```

##２）ログイン画面表示

ブラウザでログイン画面を開きます

```sql

lara=# select * from actlogs;
 id | user_id |   route    |  url  | method | status | message | remote_addr |                              user_agent                               |     created_at      | updated_at
----+---------+------------+-------+--------+--------+---------+-------------+-----------------------------------------------------------------------+---------------------+------------
  1 |         |            | /     | GET    |    302 |         | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
  2 |         | show.login | login | GET    |    200 |         | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
(2 行)

lara=#

```

最初 / にアクセスすると 302 でリダイレクト
login を表示してます



##３）ログイン実行

ブラウザでログイン操作

```sql
lara=# select * from actlogs;
 id | user_id |   route    |  url  | method | status |                                               message                                                | remote_addr |                              user_agent
     |     created_at      | updated_at
----+---------+------------+-------+--------+--------+------------------------------------------------------------------------------------------------------+-------------+-----------------------------------------------------------------------+---------------------+------------
  1 |         |            | /     | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
  2 |         | show.login | login | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
  3 |       6 | login      | login | POST   |    302 | {"_token":"As8VcC8GOhxfYcuaHfd5KNCfSAGhrEPSxOmT3ZeO","loginid":"mapleadmin","password":"mapleadmin"} | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:04 |
  4 |       6 |            | /     | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:04 |
  5 |       6 | show.login | login | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:05 |
  6 |       6 | home       | home  | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:05 |
(6 行)

lara=#

```


id:3 の行でログイン後、 
id:4  / に行って
id:5 login に行って 
id:6  home を表示というめんどくさいことしてますね。。
routes/web.php の設定を見直さなくちゃかな

要求内容「message」にパスワードが生で書かれてますね。。
これはよろしくない気が。。
ブラウザから送る前になんかしとかないとな。。



##４）ページ遷移

別記事で作成済みの「社員一覧」ページを開いてみます
https://qiita.com/nobu-maple/items/43c1228b8f04837d753b

```sql
lara=# select * from actlogs;
 id | user_id |   route    |      url       | method | status |                                               message                                                | remote_addr |                              user_agent
              |     created_at      | updated_at
----+---------+------------+----------------+--------+--------+------------------------------------------------------------------------------------------------------+-------------+-----------------------------------------------------------------------+---------------------+------------
  1 |         |            | /              | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
  2 |         | show.login | login          | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
  3 |       6 | login      | login          | POST   |    302 | {"_token":"As8VcC8GOhxfYcuaHfd5KNCfSAGhrEPSxOmT3ZeO","loginid":"mapleadmin","password":"mapleadmin"} | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:04 |
  4 |       6 |            | /              | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:04 |
  5 |       6 | show.login | login          | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:05 |
  6 |       6 | home       | home           | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:05 |
  7 |       6 | admin.user | api/admin/user | POST   |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:43 |
(7 行)

lara=#

```

id:7 でadmin.user ページを開いたログが記録されてます



##５）ログアウト

ログアウト操作をしてみます

```sql
lara=# select * from actlogs;
 id | user_id |   route    |      url       | method | status |                                               message                                                | remote_addr |                              user_agent
              |     created_at      | updated_at
----+---------+------------+----------------+--------+--------+------------------------------------------------------------------------------------------------------+-------------+-----------------------------------------------------------------------+---------------------+------------
  1 |         |            | /              | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
  2 |         | show.login | login          | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:58:36 |
  3 |       6 | login      | login          | POST   |    302 | {"_token":"As8VcC8GOhxfYcuaHfd5KNCfSAGhrEPSxOmT3ZeO","loginid":"mapleadmin","password":"mapleadmin"} | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:04 |
  4 |       6 |            | /              | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:04 |
  5 |       6 | show.login | login          | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:05 |
  6 |       6 | home       | home           | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:05 |
  7 |       6 | admin.user | api/admin/user | POST   |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 05:59:43 |
  8 |         | logout     | logout         | POST   |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 06:00:44 |
  9 |         |            | /              | GET    |    401 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 06:00:44 |
 10 |         |            | /              | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 06:00:44 |
 11 |         | show.login | login          | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko | 2018-08-23 06:00:44 |
(11 行)

lara=#

```

id:8 でログアウトして
id:9 で 401（未認証）だから / に行って
id:10 で / から 302 でリダイレクトして
id:11 で login を表示

ってことになってますね。
ログアウト後に直接 login　表示するように設定したほうがよさげカモ。。



#ログアウト時にユーザIDを記録

追記です．．
上記ログ（id:8）を見るとログアウト時にだれがログアウト操作をしたか分からないので、記録できるようにしときます。
必要ないかもだけど気になるので．．

「ミドルウェア」ではログアウト後に記録を行っているためにユーザIDが取得できないから
ログアウト操作の直前にログ記録を埋め込んでおきます。
ログアウト操作は 「 vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php 」で実行しているみたい
ログアウト操作をこっち「 app/Http/Controllers/Auth/LoginController.php 」で上書きしてやります

```php:app/Http/Controllers/Auth/LoginController.php

～～～
    /**
     * Log the user out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function logout(Request $request)
    {
        $actlog = new \App\Http\Middleware\ActlogMiddleware;
        $actlog -> actlog($request, 999);

        $this->guard()->logout();

        $request->session()->invalidate();

        return $this->loggedOut($request) ?: redirect('/');
    }
～～～

```
上記 logout を追加しときます。
ステータスは見分けがつくように 「 ９９９ 」をつけときます。


##６）ログアウト再び

```sql
lara=# select * from actlogs where id > 240;
 id  | user_id |   route    |  url   | method | status |                                               message                                                | remote_addr |                                                     user_agent                                                     |     created_at      | updated_at
-----+---------+------------+--------+--------+--------+------------------------------------------------------------------------------------------------------+-------------+---------------------------------------------------------------------------------------------------------------------+---------------------+------------
 241 |         |            | /      | GET    |    401 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:51:46 |
 242 |         |            | /      | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:51:46 |
 243 |         | show.login | login  | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:51:46 |
 244 |       6 | login      | login  | POST   |    302 | {"_token":"hMJtoWUCabj0AUSDY6jZP4XA0TVFluBsmwBXiiTN","loginid":"mapleadmin","password":"mapleadmin"} | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:32 |
 245 |       6 |            | /      | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:32 |
 246 |       6 | show.login | login  | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:34 |
 247 |       6 | home       | home   | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:34 |
 248 |       6 | logout     | logout | POST   |    999 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:37 |
 249 |         | logout     | logout | POST   |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:37 |
 250 |         |            | /      | GET    |    401 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:37 |
 251 |         |            | /      | GET    |    302 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:37 |
 252 |         | show.login | login  | GET    |    200 |                                                                                                      | 172.16.0.1  | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 | 2018-08-24 04:56:37 |
(12 行)

lara=#

```

id:248 に埋め込んだステータス 999 がありますね
ログアウト直前なのでユーザIDもちゃんと記録できてます
logout が２回記録されて冗長だけど、誰がログアウト操作をしたのかが分かるようになりました



#　


以上
いろいろありますが、ミドルウェアとして登録したことで
処理系には一切手を入れずにアクションログを自動で記録できるようになりました

Laravel ステキ