GCP(GCE)でLaravelのログをStackDriverに出力する

# GCPでLaravelのログをStackDriverに出力するには

## TL;DR
+ `monolog-stackdriver` を使うことで、LaravelログをstackDriverで表示できる
+ envファイルの設定で、ログの出力先を通常ログ or StackDriverの切り替え可能
+ インタンスを設定しないと、StackDriverの **グローバル** カテゴリに出力される
+ GCEの`instance_id` を指定することで、コンピュータに紐付けた出力もできるがid取得方法までは調べていない


## ログ送信手順

### composerでパッケージ導入


こちらのパッケージを使います。
https://packagist.org/packages/codeinternetapplications/monolog-stackdriver


```composer:ライブラリ導入コマンド
composer require codeinternetapplications/monolog-stackdriver
```

### ログの送信先を設定する

1.ENVファイルの設定

2.config/logging.php設定

config/logging.phpはStackDriverでの表示先で設定量が異なります


- グローバル ->共通の設定を行うだけ
- インタンス名単位(今回はGCE) ->インスタンス名のみ設定
- インスタンス(マシーン)単位 ->インスタンIDも設定



### ENVファイルを設定する

ログを表示したいレベルに合わせて、envを記載します。

```env:env
# 必須
LOG_CHANNEL=stackdriver

# インスタンス名別のログ保存する場合
GCP_LOG_RESOURCE_NAME=hoge_resource

# インスタンス(マシーン)単位のログ保存する場合
GCP_LOG_INSTANCE_ID=hoge_xxxxxx
GCP_LOG_PROJECT_ID=hoge_id
GCP_LOG_ZONE=hoge_zone
```


`hoge_` の値は適切な値に変更してください。


### config/logging.phpを設定する

#### グローバルで表示されるように設定する場合

```php:config/logging.php

'channels' => [

    // ( ... )

    'stackdriver' => [
        'driver' => 'custom',
        'via' => CodeInternetApplications\MonologStackdriver\Laravel\CreateStackdriverLogger::class,
        'logName' => env('APP_NAME', 'my-project-log'),

        //GCP内部からの保存には必要ない
        // 'loggingClientOptions' => [
            // 'keyFilePath' => '/path/to/service-account-key-file.json',
        // ],

        // 'loggerOptions' => [],
        // 'lineFormat' => '%message%',
        // 'entryOptionsWrapper' => 'stackdriver',
    ],
]
```

`logName`項目は適切な名前をつけてください

`loggingClientOptions` 項目で、jsonのキーファイルを指定しますが、GCP内部でのログ送信にjsonは必要ありません。
他のクラウド(AWSなど)から送信する場合に指定します。

##### ログ表示例

下で説明している項目でログを送信
![グローバル保存1_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90529/baf349a2-326f-3844-a0a2-771488cb4983.jpeg)


良い点
- 設定が簡単
- 多くの場合で適用できる
- 複数のインスタンのプログラムからのログを集約できる

悪い点
- 複数のマシーンからのログを識別する工夫が別途必要
-エラー調査の際、グローバルとマシンログを往復して比較する必要がある


#### インスタンス名別で表示されるように設定する場合

```php:config/logging.php

'channels' => [

    // ( ... )

    'stackdriver' => [
        'driver' => 'custom',
        'via' => CodeInternetApplications\MonologStackdriver\Laravel\CreateStackdriverLogger::class,
        'logName' => env('APP_NAME', 'my-project-log'),

        'loggerOptions' => [
              'labels' => [
                  'compute.googleapis.com/resource_name' => env('GCP_LOG_RESOURCE_NAME', 'dev-web-instance'),
              ],
              'resource' => [
                  'type' => 'gce_instance',
              ],
        ]

        //GCP内部からの保存には必要ない
        // 'loggingClientOptions' => [
            // 'keyFilePath' => '/path/to/service-account-key-file.json',
        // ],

        // 'loggerOptions' => [],
        // 'lineFormat' => '%message%',
        // 'entryOptionsWrapper' => 'stackdriver',
    ],
]
```

グローバル設定に、 `loggerOptions` を追加しています。
ここでは、GCEのログなのでインスタンにGCEを指定しています。
指定したい箇所のログで実際に指定されている項目を設定ファイルにコピーすれば、その箇所に入るようになります。


##### ログ表示例

下で説明している項目でログを送信
![インスタン別.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90529/6a93ace3-a05c-d83d-6316-c4d34aa91f0c.jpeg)




良い点
- サーバと同じカテゴリなので、時間軸に沿った把握ができる
- マシーンをグループ化していなければ、マシーン名とログが一致する
- オートスケールマシーンでもグループに集約される形で取得できる

悪い点
- マシーンの識別はできない
- instance_idが空でも表示できているが、正常なログの仕様として問題ないか不明(なんらかのログサービスと連携した時の挙動が心配)


#### インスタンス(マシーン)単位で表示されるように設定する場合

```php:config/logging.php

'channels' => [

    // ( ... )

    'stackdriver' => [
        'driver' => 'custom',
        'via' => CodeInternetApplications\MonologStackdriver\Laravel\CreateStackdriverLogger::class,
        'logName' => env('APP_NAME', 'my-project-log'),

        'loggerOptions' => [
              'labels' => [
                  'compute.googleapis.com/resource_name' => env('GCP_LOG_RESOURCE_NAME', 'dev-web-instance'),
              ],
              'resource' => [
                    'type' => 'gce_instance',
                    'labels' => [
                        'instance_id' =>  env('GCP_LOG_INSTANCE_ID'),
                        'project_id' =>  env('GCP_LOG_PROJECT_ID'),
                        'zone' =>  env('GCP_LOG_ZONE'),
                    ]
                ],
        ]

        //GCP内部からの保存には必要ない
        // 'loggingClientOptions' => [
            // 'keyFilePath' => '/path/to/service-account-key-file.json',
        // ],

        // 'loggerOptions' => [],
        // 'lineFormat' => '%message%',
        // 'entryOptionsWrapper' => 'stackdriver',
    ],
]
```

インスタンス名別の設定に、 `loggerOptions.resource.labels` を追加しています。

instance_idは、Compute Engineの詳細ページ
![インスタンスIDの場所.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90529/cb9dcab7-f1f9-b409-3858-e304800d437a.jpeg)

project_idは、TOPページに記載されています


##### ログ表示例

(省略)


良い点
- サーバの情報と同じ場所に格納されるので時間軸に沿った把握が簡単
- マシーンの識別もできる
悪い点
- オートスケールでidが変わる場合の対応を考える必要がある


## ログ送信

Laravelの標準的なコードで送信が行える

```php:ログ送信コード例

//infoレベルログ
Log::info('InfoLogInfoTest', ['logItem1' => 'メッセージはあとから分かりやすいものを', 'logItem2' => ['ItemSub1' => '配列形式で自由にデータを入れられる', 'ItemSub2' => '入れ子も問題なし'] ]);

// errorレベルログ
Log::error('InfoLogErrorTest', ['logItem1' => 'エラーもStackDriverで正しく識別', 'logItem2' => ['ItemSub1' => '配列形式で自由にデータを入れられる', 'ItemSub2' => '入れ子も問題なし'] ]);
```


第1引数は、 `message` に格納される
第2引数は、 `jsonPayload` に格納される
![グローバル保存1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90529/74227cc2-cffc-6649-2bec-9c1081242217.jpeg)


StackDriverにログレベルも認識されているので、 `Error`の絞り込みも行える
![グローバル保存2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90529/07da78e4-2e1e-f69e-964e-6cdeacd770c9.jpeg)



以上です。