From 996f420205febb4d5a920333e6aab9f0a4695c9a Mon Sep 17 00:00:00 2001 From: Julien Breux Date: Tue, 28 Jun 2016 18:02:23 +0200 Subject: [PATCH 1/4] Add Logmatic.io --- doc/02-handlers-formatters-processors.md | 1 + src/Monolog/Formatter/LogmaticFormatter.php | 66 ++++++++++++++ src/Monolog/Handler/LogmaticHandler.php | 89 +++++++++++++++++++ .../Formatter/LogmaticFormatterTest.php | 36 ++++++++ tests/Monolog/Handler/LogmaticTest.php | 84 +++++++++++++++++ 5 files changed, 276 insertions(+) create mode 100644 src/Monolog/Formatter/LogmaticFormatter.php create mode 100644 src/Monolog/Handler/LogmaticHandler.php create mode 100644 tests/Monolog/Formatter/LogmaticFormatterTest.php create mode 100644 tests/Monolog/Handler/LogmaticTest.php diff --git a/doc/02-handlers-formatters-processors.md b/doc/02-handlers-formatters-processors.md index 02e727e9..29f1f55d 100644 --- a/doc/02-handlers-formatters-processors.md +++ b/doc/02-handlers-formatters-processors.md @@ -55,6 +55,7 @@ - [_RollbarHandler_](../src/Monolog/Handler/RollbarHandler.php): Logs records to a [Rollbar](https://rollbar.com/) account. - [_SyslogUdpHandler_](../src/Monolog/Handler/SyslogUdpHandler.php): Logs records to a remote [Syslogd](http://www.rsyslog.com/) server. - [_LogEntriesHandler_](../src/Monolog/Handler/LogEntriesHandler.php): Logs records to a [LogEntries](http://logentries.com/) account. +- [_LogmaticHandler_](../src/Monolog/Handler/LogmaticHandler.php): Logs records to a [Logmatic](http://logmatic.io/) account. ### Logging in development diff --git a/src/Monolog/Formatter/LogmaticFormatter.php b/src/Monolog/Formatter/LogmaticFormatter.php new file mode 100644 index 00000000..cacd767b --- /dev/null +++ b/src/Monolog/Formatter/LogmaticFormatter.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Encodes message information into JSON in a format compatible with Logmatic. + * + * @author Julien Breux + */ +class LogmaticFormatter extends JsonFormatter +{ + /** + * @param string + */ + protected $hostname = ''; + + /** + * @param string + */ + protected $appname = ''; + + /** + * Set hostname + * + * @param string $hostname + */ + public function setHostname(String $hostname) { + $this->hostname = $hostname; + } + + /** + * Set appname + * + * @param string $appname + */ + public function setAppname(String $appname) { + $this->appname = $appname; + } + + /** + * Appends the 'hostname' and 'appname' parameter for indexing by Logmatic. + * + * @see http://doc.logmatic.io/docs/basics-to-send-data + * @see \Monolog\Formatter\JsonFormatter::format() + */ + public function format(array $record) + { + if (!empty($this->hostname)) { + $record['hostname'] = $this->hostname; + } + if (!empty($this->appname)) { + $record['appname'] = $this->appname; + } + + return parent::format($record); + } +} diff --git a/src/Monolog/Handler/LogmaticHandler.php b/src/Monolog/Handler/LogmaticHandler.php new file mode 100644 index 00000000..d45e9c93 --- /dev/null +++ b/src/Monolog/Handler/LogmaticHandler.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LogmaticFormatter; + +/** + * @author Julien Breux + */ +class LogmaticHandler extends SocketHandler +{ + const LOGMATIC_URI = '/v1/'; + + /** + * @var string + */ + protected $logToken; + + /** + * @var string + */ + protected $hostname; + + /** + * @var string + */ + protected $appname; + + /** + * @param string $hostname Host name supplied by Logmatic. + * @param string $appname Application name supplied by Logmatic. + * @param string $token Log token supplied by Logmatic. + * @param bool $useSSL Whether or not SSL encryption should be used. + * @param int $level The minimum logging level to trigger this handler. + * @param bool $bubble Whether or not messages that are handled should bubble up the stack. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct($token, $hostname = '', $appname = '', $useSSL = true, $level = Logger::DEBUG, $bubble = true) + { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogmaticHandler'); + } + + $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514'; + + parent::__construct($endpoint . self::LOGMATIC_URI, $level, $bubble); + + $this->logToken = $token; + $this->hostname = $hostname; + $this->appname = $appname; + } + + /** + * {@inheritdoc} + */ + protected function generateDataStream($record): String + { + return $this->logToken . ' ' . $record['formatted']; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter(): FormatterInterface + { + $formatter = new LogmaticFormatter(); + + if (!empty($this->hostname)) { + $formatter->setHostname($this->hostname); + } + if (!empty($this->appname)) { + $formatter->setAppname($this->appname); + } + + return $formatter; + } +} diff --git a/tests/Monolog/Formatter/LogmaticFormatterTest.php b/tests/Monolog/Formatter/LogmaticFormatterTest.php new file mode 100644 index 00000000..d27670fa --- /dev/null +++ b/tests/Monolog/Formatter/LogmaticFormatterTest.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Test\TestCase; + +/** + * @author Julien Breux + */ +class LogmaticFormatterTest extends TestCase +{ + /** + * @covers Monolog\Formatter\LogmaticFormatter::format + */ + public function testFormat() + { + $formatter = new LogmaticFormatter(); + $formatter->setHostname('testHostname'); + $formatter->setAppname('testAppname'); + $record = $this->getRecord(); + $formatted_decoded = json_decode($formatter->format($record), true); + $this->assertArrayHasKey('hostname', $formatted_decoded); + $this->assertArrayHasKey('appname', $formatted_decoded); + $this->assertEquals('testHostname', $formatted_decoded['hostname']); + $this->assertEquals('testAppname', $formatted_decoded['appname']); + } +} diff --git a/tests/Monolog/Handler/LogmaticTest.php b/tests/Monolog/Handler/LogmaticTest.php new file mode 100644 index 00000000..3948a412 --- /dev/null +++ b/tests/Monolog/Handler/LogmaticTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Test\TestCase; +use Monolog\Logger; + +/** + * @author Julien Breux + */ +class LogmaticHandlerTest extends TestCase +{ + /** + * @var resource + */ + private $res; + + /** + * @var LogmaticHandler + */ + private $handler; + + public function testWriteContent() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Critical write test')); + + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/testToken {"message":"Critical write test","context":\[\],"level":500,"level_name":"CRITICAL","channel":"test","datetime":"(.*)","extra":\[\],"hostname":"testHostname","appname":"testAppname"}/', $content); + } + + public function testWriteBatchContent() + { + $records = [ + $this->getRecord(), + $this->getRecord(), + $this->getRecord(), + ]; + $this->createHandler(); + $this->handler->handleBatch($records); + + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/testToken {"message":"test","context":\[\],"level":300,"level_name":"WARNING","channel":"test","datetime":"(.*)","extra":\[\],"hostname":"testHostname","appname":"testAppname"}/', $content); + } + + private function createHandler() + { + $useSSL = extension_loaded('openssl'); + $args = ['testToken', 'testHostname', 'testAppname', $useSSL, Logger::DEBUG, true]; + $this->res = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\LogmaticHandler', + ['fsockopen', 'streamSetTimeout', 'closeSocket'], + $args + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + } +} From b1f107d12d3ec4d29b8a0c7f2216086b965d2379 Mon Sep 17 00:00:00 2001 From: Julien Breux Date: Tue, 28 Jun 2016 18:39:44 +0200 Subject: [PATCH 2/4] Remove const --- src/Monolog/Handler/LogmaticHandler.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Monolog/Handler/LogmaticHandler.php b/src/Monolog/Handler/LogmaticHandler.php index d45e9c93..b6eb67f8 100644 --- a/src/Monolog/Handler/LogmaticHandler.php +++ b/src/Monolog/Handler/LogmaticHandler.php @@ -20,8 +20,6 @@ use Monolog\Formatter\LogmaticFormatter; */ class LogmaticHandler extends SocketHandler { - const LOGMATIC_URI = '/v1/'; - /** * @var string */ @@ -54,6 +52,7 @@ class LogmaticHandler extends SocketHandler } $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514'; + $endpoint .= '/v1/'; parent::__construct($endpoint . self::LOGMATIC_URI, $level, $bubble); From 4230ff5560a2f501321dd3076a31fc9d5c9ff4df Mon Sep 17 00:00:00 2001 From: Julien Breux Date: Tue, 28 Jun 2016 18:41:45 +0200 Subject: [PATCH 3/4] Fix scalar typehints --- src/Monolog/Formatter/LogmaticFormatter.php | 4 ++-- src/Monolog/Handler/LogmaticHandler.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Monolog/Formatter/LogmaticFormatter.php b/src/Monolog/Formatter/LogmaticFormatter.php index cacd767b..83b93019 100644 --- a/src/Monolog/Formatter/LogmaticFormatter.php +++ b/src/Monolog/Formatter/LogmaticFormatter.php @@ -33,7 +33,7 @@ class LogmaticFormatter extends JsonFormatter * * @param string $hostname */ - public function setHostname(String $hostname) { + public function setHostname(string $hostname) { $this->hostname = $hostname; } @@ -42,7 +42,7 @@ class LogmaticFormatter extends JsonFormatter * * @param string $appname */ - public function setAppname(String $appname) { + public function setAppname(string $appname) { $this->appname = $appname; } diff --git a/src/Monolog/Handler/LogmaticHandler.php b/src/Monolog/Handler/LogmaticHandler.php index b6eb67f8..043d89e8 100644 --- a/src/Monolog/Handler/LogmaticHandler.php +++ b/src/Monolog/Handler/LogmaticHandler.php @@ -64,7 +64,7 @@ class LogmaticHandler extends SocketHandler /** * {@inheritdoc} */ - protected function generateDataStream($record): String + protected function generateDataStream($record): string { return $this->logToken . ' ' . $record['formatted']; } From 3d74d961abe1211b83e5a6f49c44e0f788c4c11b Mon Sep 17 00:00:00 2001 From: Julien Breux Date: Tue, 28 Jun 2016 18:46:44 +0200 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=98=B1=20Remove=20const?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Monolog/Handler/LogmaticHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Monolog/Handler/LogmaticHandler.php b/src/Monolog/Handler/LogmaticHandler.php index 043d89e8..e78d3cf2 100644 --- a/src/Monolog/Handler/LogmaticHandler.php +++ b/src/Monolog/Handler/LogmaticHandler.php @@ -54,7 +54,7 @@ class LogmaticHandler extends SocketHandler $endpoint = $useSSL ? 'ssl://api.logmatic.io:10515' : 'api.logmatic.io:10514'; $endpoint .= '/v1/'; - parent::__construct($endpoint . self::LOGMATIC_URI, $level, $bubble); + parent::__construct($endpoint, $level, $bubble); $this->logToken = $token; $this->hostname = $hostname;