MonologのSlackHandlerに、サーバー情報および実行時の環境情報（$_SERVERの各情報）を出力させるには


## はじめに
MonologのSlackHandlerの使い方については、[MonologのSlackHandlerでSlackに通知する](http://qiita.com/imunew/items/d0c2066306094a872cbe)を参照いただくとして、Slackに通知される情報が十分でないと感じるようになってきました。

![monolog_slack_ss_01.png](https://qiita-image-store.s3.amazonaws.com/0/26422/8bbf010e-49f5-125a-fa58-ec44074acf1a.png "monolog_slack_ss_01.png")


上記の例は、/aaa という存在しないページにアクセスしようとして、NotFoundExceptionが発生したログですが、この内容では、下記のようにいろいろ不満があります。

- Webサーバーが複数あった場合、どのサーバーで発生したか分からない
- リクエストがあったURLが特定できないケースがある
- アクセス元がまったく分からない

そこで、MonologのREADMEなどをあらためて確認したところ、Monologには、あらかじめ、Processorがいくつか用意されていて、WebProcessorを追加すれば、不満が解消しそうです。

https://github.com/Seldaek/monolog#processors

## WebProcessorの追加

WebProcessorを追加するには、以下のようにします。
（Silexの場合）

```php
$processor = new Monolog\Processor\WebProcessor();
$app['monolog']->pushProcessor($processor);
```

初期状態では、下記の情報が出力されるようになります。

```php
// Monolog/Processor/WebProcessor.php
/**
 * @var array
 */
protected $extraFields = array(
    'url'         => 'REQUEST_URI',
    'ip'          => 'REMOTE_ADDR',
    'http_method' => 'REQUEST_METHOD',
    'server'      => 'SERVER_NAME',
    'referrer'    => 'HTTP_REFERER',
);
```
https://github.com/Seldaek/monolog/blob/master/src/Monolog/Processor/WebProcessor.php#L29-L35

\$_SERVER['SERVER_ADDR']と\$_SERVER['HTTP_USER_AGENT']も出力したいので、addExtraFieldにて追加します。

```php
$processor->addExtraField('server_ip', 'SERVER_ADDR');
$processor->addExtraField('user_agent', 'HTTP_USER_AGENT');
```

## しかし、WebProcessorで追加したはずの情報が何も出力されない

結論から言うと、1.11.0時点では、Processorを追加してもSlackに通知することはできません。
WebProcessorはログレコードの'extra'フィールドに情報を付加するんですが、SlackHandlerが'extra'フィールドを全く出力しません。

```php
// Monolog/Logger.php
$record = array(
    'message' => (string) $message,
    'context' => $context,
    'level' => $level,
    'level_name' => $levelName,
    'channel' => $this->name,
    'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone)->setTimezone(static::$timezone),
    'extra' => array(),
);
```

https://github.com/Seldaek/monolog/blob/master/src/Monolog/Logger.php#L251-L259

```php
// Monolog/Handler/SlackHandler.php
$dataArray['attachments'] = json_encode(
    array(
        array(
            'fallback' => $record['message'],
            'color' => $this->getAttachmentColor($record['level']),
            'fields' => array(
                array(
                    'title' => 'Message',
                    'value' => $record['message'],
                    'short' => false
                ),
                array(
                    'title' => 'Level',
                    'value' => $record['level_name'],
                    'short' => true
                )
            )
        )
    )
);
```

https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/SlackHandler.php#L78-L137


## 強引に継承クラスを作って対応

もう、かなり強引ですが、SlackHandlerを継承したサブクラスを自作して、処理を書き換えるしかないです。
SlackHandlerはおそらく継承されることを想定していないので、肝心な部分がprivate変数、private関数なので、泣く泣く本家からコードをコピペしてます。
早く捨てたい。。

Gistにコードをあげたので、詳しくは[こちら](https://gist.github.com/imunew/26d9f59460e6211c959f)を参照ください。

'extra'フィールドを追加している部分だけ抜粋します。

```php
$fields = array(
                array(
                    'title' => 'Message',
                    'value' => $record['message'],
                    'short' => false
                ),
                array(
                    'title' => 'Level',
                    'value' => $record['level_name'],
                    'short' => true
                )
            );

if ((isset($record['extra'])) && (is_array($record['extra']))) {
    foreach ($record['extra'] as $title => $value) {
        $fields[] = array(
            'title' => $title,
            'value' => $value,
            'short' => true
        );
    }
}

$dataArray['attachments'] = json_encode(
    array(
        array(
            'fallback' => $record['message'],
            'color' => $this->getAttachmentColor($record['level']),
            'fields' => $fields,
        )
    )
);
```

PR出そうかと思いましたが、既出のものがありましたので、それがMergeされることを願うばかりです。
https://github.com/Seldaek/monolog/pull/435


## ついに本家が対応、しかし（1/18追記）

ついに、v1.12.0 (2014-12-29)にて、上記PRがMergeされました。
[Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data](https://github.com/Seldaek/monolog/blob/1fbe8c2641f2b163addf49cc5e18f144bec6b19f/CHANGELOG.mdown#1120-2014-12-29)

早速試してみました。しかし...
![monolog_slack_v1.12.0.png](https://qiita-image-store.s3.amazonaws.com/0/26422/1722a908-501f-44bd-999f-50e1972f4325.png "monolog_slack_v1.12.0.png")

Extraというタイトルで、「 | 」区切りでごちゃっとまとめられてます。
正直、これは嬉しくない。
Extra Fieldであることを利用する側は意識する必要ないし、「 | 」にするために、Line Formatterをわざわざ使うのもちょっと理解できない。

## あまりにも酷いので、PR出しました（1/18追記）
ということで、PR出しました。
[Improve a view of extra fields in SlackHandler.](https://github.com/Seldaek/monolog/pull/496)
正直、元々のPR[#435](https://github.com/Seldaek/monolog/pull/435)が許されるのなら、これも許されるはずと思ってます。


## PRは却下、しかし、v1.13.0 (2015-03-05) がリリース（3/22追記）

先日、[v1.13.0](https://github.com/Seldaek/monolog/releases/tag/1.13.0)がリリースされ、結果、私のPR([#496](https://github.com/Seldaek/monolog/pull/496))は却下されたのですが、かわりに別のPR([#507](https://github.com/Seldaek/monolog/pull/506))がMergeされました。

### 結果、Call Stackが出力されるように！

試してみたところ、下記のように通知されるようになってました。
![monolog_slack_v1.13.0.png](https://qiita-image-store.s3.amazonaws.com/0/26422/bf4c6f50-c551-f72f-9fed-079409531142.png "monolog_slack_v1.13.0.png")

なお、コンストラクタの設定は
`$bubble: true, $useShortAttachment: false, $includeContextAndExtra: true`
にしました。

```php
// vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php
/** 
 * __construct
 * (中略)...
 * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
 * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style
 * @param bool $includeContextAndExtra Whether the attachment should include context and extra data
 */
```

分かりにくいですが、Call Stack が出力されています。
これは、'extra'フィールドと同様に、'context'フィールドも出力するようにしているためです。
（'context'フィールドにCall Stackが含まれていることは正直知りませんでした。）
詳しくは、下記コードを参照ください。

参考）
https://github.com/Seldaek/monolog/blob/6f0e7f666e39b6e606d9837b146404312bec1a03/src/Monolog/Handler/SlackHandler.php#L182-L202

ちなみに、`$useShortAttachment: true`にすると、下記のようになります。
（うーん、やっぱり見づらい）

![monolog_slack_v1.13.0_short.png](https://qiita-image-store.s3.amazonaws.com/0/26422/fea21c8f-48bf-8fc2-9b61-0d14ee005329.png "monolog_slack_v1.13.0_short.png")


## さいごに（3/22更新）

これで、ようやく独自のSlackHandlerは捨てることができます。
MonologのProcessorやHandler、'extra'および'context'フィールドなど理解が深まったですし、PRも無駄ではなかったな〜と。
個人的にはいい経験ができました。