Skip to content

[Monolog] Fixed ElasticsearchLogstashHandler with monolog 2+ #33241

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
*/
class ElasticsearchLogstashHandler extends AbstractHandler
{
use ProcessableHandlerTrait;
use FormattableHandlerTrait;

private $endpoint;
private $index;
private $client;
Expand Down Expand Up @@ -79,7 +82,13 @@ public function handleBatch(array $records): void

protected function getDefaultFormatter(): FormatterInterface
{
return new LogstashFormatter('application', null, null, 'ctxt_', LogstashFormatter::V1);
// Monolog 1.X
if (\defined(LogstashFormatter::class.'::V1')) {
return new LogstashFormatter('application', null, null, 'ctxt_', LogstashFormatter::V1);
}

// Monolog 2.X
return new LogstashFormatter('application');
}

private function sendToElasticsearch(array $records)
Expand Down
73 changes: 73 additions & 0 deletions src/Symfony/Bridge/Monolog/Handler/FormattableHandlerTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/*
* This file is part of the Monolog package.
*
* (c) Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Monolog\Handler;

use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;

/**
* Helper trait for implementing FormattableInterface.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @internal
*/
trait FormattableHandlerTrait
{
/**
* @var FormatterInterface
*/
protected $formatter;

/**
* {@inheritdoc}
*
* @suppress PhanTypeMismatchReturn
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
$this->formatter = $formatter;

return $this;
}

/**
* {@inheritdoc}
*/
public function getFormatter(): FormatterInterface
{
if (!$this->formatter) {
$this->formatter = $this->getDefaultFormatter();
}

return $this->formatter;
}

/**
* Gets the default formatter.
*
* Overwrite this if the LineFormatter is not a good default for your handler.
*/
protected function getDefaultFormatter(): FormatterInterface
{
return new LineFormatter();
}
}
74 changes: 74 additions & 0 deletions src/Symfony/Bridge/Monolog/Handler/ProcessableHandlerTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Monolog\Handler;

use Monolog\ResettableInterface;

/**
* Helper trait for implementing ProcessableInterface.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*
* @internal
*/
trait ProcessableHandlerTrait
{
/**
* @var callable[]
*/
protected $processors = [];

/**
* {@inheritdoc}
*
* @suppress PhanTypeMismatchReturn
*/
public function pushProcessor($callback): HandlerInterface
{
array_unshift($this->processors, $callback);

return $this;
}

/**
* {@inheritdoc}
*/
public function popProcessor(): callable
{
if (!$this->processors) {
throw new \LogicException('You tried to pop from an empty processor stack.');
}

return array_shift($this->processors);
}

/**
* Processes a record.
*/
protected function processRecord(array $record): array
{
foreach ($this->processors as $processor) {
$record = $processor($record);
}

return $record;
}

protected function resetProcessors()
{
foreach ($this->processors as $processor) {
if ($processor instanceof ResettableInterface) {
$processor->reset();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,17 @@ public function testHandle()
$responseFactory = function ($method, $url, $options) use (&$callCount) {
$body = <<<EOBODY
{"index":{"_index":"log","_type":"_doc"}}
{"@timestamp":"2020-01-01T00:00:00.000000+01:00","@version":1,"host":"my hostname","message":"My info message","type":"application","channel":"app","level":"INFO"}
{"@timestamp":"2020-01-01T00:00:00.000000+01:00","@version":1,"host":"my hostname","message":"My info message","type":"application","channel":"app","level":"INFO","monolog_level":200}


EOBODY;

// Monolog 1X
if (\defined(LogstashFormatter::class.'::V1')) {
$body = str_replace(',"monolog_level":200', '', $body);
$body = str_replace(',"monolog_level":300', '', $body);
}

$this->assertSame('POST', $method);
$this->assertSame('http://es:9200/_bulk', $url);
$this->assertSame($body, $options['body']);
Expand Down Expand Up @@ -64,14 +70,20 @@ public function testBandleBatch()
$responseFactory = function ($method, $url, $options) use (&$callCount) {
$body = <<<EOBODY
{"index":{"_index":"log","_type":"_doc"}}
{"@timestamp":"2020-01-01T00:00:00.000000+01:00","@version":1,"host":"my hostname","message":"My info message","type":"application","channel":"app","level":"INFO"}
{"@timestamp":"2020-01-01T00:00:00.000000+01:00","@version":1,"host":"my hostname","message":"My info message","type":"application","channel":"app","level":"INFO","monolog_level":200}

{"index":{"_index":"log","_type":"_doc"}}
{"@timestamp":"2020-01-01T00:00:01.000000+01:00","@version":1,"host":"my hostname","message":"My second message","type":"application","channel":"php","level":"WARNING"}
{"@timestamp":"2020-01-01T00:00:01.000000+01:00","@version":1,"host":"my hostname","message":"My second message","type":"application","channel":"php","level":"WARNING","monolog_level":300}


EOBODY;

// Monolog 1X
if (\defined(LogstashFormatter::class.'::V1')) {
$body = str_replace(',"monolog_level":200', '', $body);
$body = str_replace(',"monolog_level":300', '', $body);
}

$this->assertSame('POST', $method);
$this->assertSame('http://es:9200/_bulk', $url);
$this->assertSame($body, $options['body']);
Expand Down Expand Up @@ -114,6 +126,12 @@ class ElasticsearchLogstashHandlerWithHardCodedHostname extends ElasticsearchLog
{
protected function getDefaultFormatter(): FormatterInterface
{
return new LogstashFormatter('application', 'my hostname', null, 'ctxt_', LogstashFormatter::V1);
// Monolog 1.X
if (\defined(LogstashFormatter::class.'::V1')) {
return new LogstashFormatter('application', 'my hostname', null, 'ctxt_', LogstashFormatter::V1);
}

// Monolog 2.X
return new LogstashFormatter('application', 'my hostname');
}
}