Fluentdへログを送信するための公式ライブラリとしてFluent Loggerがあります。
https://github.com/fluent/fluent-logger-php
以下のようにFluentdへログを飛ばすことができます。
$logger = new FluentLogger();
$logger->post('app', ['message' => 'ok']);
AWS for Fluent Bit Container Image Version 2.21.4Fluent Bit v1.8.10
* Copyright (C) 2019-2021 The Fluent Bit Authors
* Copyright (C) 2015-2018 Treasure Data
* Fluent Bit is a CNCF sub-project under the umbrella of Fluentd
* https://fluentbit.io
[2021/12/22 15:38:38] [ info] [engine] started (pid=1)
[2021/12/22 15:38:38] [ info] [storage] version=1.1.5, initializing...
[2021/12/22 15:38:38] [ info] [storage] in-memory
[2021/12/22 15:38:38] [ info] [storage] normal synchronization mode, checksum disabled, max_chunks_up=128
[2021/12/22 15:38:38] [ info] [cmetrics] version=0.2.2
[2021/12/22 15:38:38] [ info] [input:tcp:tcp.0] listening on 0.0.0.0:5170
[2021/12/22 15:38:38] [ info] [sp] stream processor started
[{"date":1640187688.673237,"msg":["app",1640187688,{"message":"ok"}]}]
しかし、SymfonyやLaravelのDIコンテナを使って、DIをしているとログが送信されません。
public function func(FluentLogger $logger)
{
$logger->post('data', ['message' => 'ok']);
}
原因は以下の通りです。
Fluent LoggerはFluentdのサーバーへ接続するためのソケットを作るのですが、このソケットは一定時間で切断されます。
しかし、DIコンテナによってFluent Loggerのインスタンスがシングルトンとなってしまっているため、ソケットが切断されているにもかかわらず、Fluent Loggerのインスタンスが再利用されてしまいます。
Fluent Loggerがログの送信をするメソッドがこちらです。
protected function postImpl(Entity $entity)
{
$buffer = $packed = $this->packer->pack($entity);
$length = strlen($packed);
$retry = $written = 0;
try {
$this->reconnect();
} catch (\Exception $e) {
$this->close();
$this->processError($entity, $e->getMessage());
return false;
}
try {
// PHP socket looks weired. we have to check the implementation.
while ($written < $length) {
$nwrite = $this->write($buffer); // ログの送信を行う
....
ログの送信前にreconnect()
メソッドを呼んでいますが、このメソッドはソケットが存在していることしか確認していないため、実際にソケットが生きているかまでは保証していません。
protected function reconnect()
{
if (!is_resource($this->socket)) {
$this->connect();
}
}
対策としては、Fluent Loggerのシングルトン化をやめるか、reconnect()
メソッドを次のようにオーバーライドしたクラスを作成し、そちらを使うようにすればOKです。
protected function reconnect()
{
$this->connect();
}