はじめに
MonologのSlackHandlerの使い方については、MonologのSlackHandlerでSlackに通知するを参照いただくとして、Slackに通知される情報が十分でないと感じるようになってきました。
上記の例は、/aaa という存在しないページにアクセスしようとして、NotFoundExceptionが発生したログですが、この内容では、下記のようにいろいろ不満があります。
- Webサーバーが複数あった場合、どのサーバーで発生したか分からない
- リクエストがあったURLが特定できないケースがある
- アクセス元がまったく分からない
そこで、MonologのREADMEなどをあらためて確認したところ、Monologには、あらかじめ、Processorがいくつか用意されていて、WebProcessorを追加すれば、不満が解消しそうです。
WebProcessorの追加
WebProcessorを追加するには、以下のようにします。
(Silexの場合)
$processor = new Monolog\Processor\WebProcessor();
$app['monolog']->pushProcessor($processor);
初期状態では、下記の情報が出力されるようになります。
// 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',
);
$_SERVER['SERVER_ADDR']と$_SERVER['HTTP_USER_AGENT']も出力したいので、addExtraFieldにて追加します。
$processor->addExtraField('server_ip', 'SERVER_ADDR');
$processor->addExtraField('user_agent', 'HTTP_USER_AGENT');
しかし、WebProcessorで追加したはずの情報が何も出力されない
結論から言うと、1.11.0時点では、Processorを追加してもSlackに通知することはできません。
WebProcessorはログレコードの'extra'フィールドに情報を付加するんですが、SlackHandlerが'extra'フィールドを全く出力しません。
// 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(),
);
// 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
)
)
)
)
);
強引に継承クラスを作って対応
もう、かなり強引ですが、SlackHandlerを継承したサブクラスを自作して、処理を書き換えるしかないです。
SlackHandlerはおそらく継承されることを想定していないので、肝心な部分がprivate変数、private関数なので、泣く泣く本家からコードをコピペしてます。
早く捨てたい。。
Gistにコードをあげたので、詳しくはこちらを参照ください。
'extra'フィールドを追加している部分だけ抜粋します。
$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
Extraというタイトルで、「 | 」区切りでごちゃっとまとめられてます。
正直、これは嬉しくない。
Extra Fieldであることを利用する側は意識する必要ないし、「 | 」にするために、Line Formatterをわざわざ使うのもちょっと理解できない。
あまりにも酷いので、PR出しました(1/18追記)
ということで、PR出しました。
Improve a view of extra fields in SlackHandler.
正直、元々のPR#435が許されるのなら、これも許されるはずと思ってます。
PRは却下、しかし、v1.13.0 (2015-03-05) がリリース(3/22追記)
先日、v1.13.0がリリースされ、結果、私のPR(#496)は却下されたのですが、かわりに別のPR(#507)がMergeされました。
結果、Call Stackが出力されるように!
試してみたところ、下記のように通知されるようになってました。
なお、コンストラクタの設定は
$bubble: true, $useShortAttachment: false, $includeContextAndExtra: true
にしました。
// 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が含まれていることは正直知りませんでした。)
詳しくは、下記コードを参照ください。
ちなみに、$useShortAttachment: true
にすると、下記のようになります。
(うーん、やっぱり見づらい)
さいごに(3/22更新)
これで、ようやく独自のSlackHandlerは捨てることができます。
MonologのProcessorやHandler、'extra'および'context'フィールドなど理解が深まったですし、PRも無駄ではなかったな〜と。
個人的にはいい経験ができました。