Florian Plattner 11 лет назад
Родитель
Сommit
d6f11d6ba3

+ 105 - 0
src/Monolog/Formatter/MongoDBFormatter.php

@@ -0,0 +1,105 @@
+<?php
+
+/*
+ * 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 Monolog\Formatter;
+
+/**
+ * Formats a record for use with the MongoDBHandler.
+ *
+ * @author Florian Plattner <me@florianplattner.de>
+ */
+class MongoDBFormatter implements FormatterInterface
+{
+    private $exceptionTraceAsString;
+    private $maxNestingLevel;
+
+    /**
+     * @param int  $maxNestingLevel        0 means infinite nesting, the $record itself is level 1, $record['context'] is 2
+     * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
+     */
+    public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = true)
+    {
+        $this->maxNestingLevel = max($maxNestingLevel, 0);
+        $this->exceptionTraceAsString = $exceptionTraceAsString == true && $exceptionTraceAsString !== 'false';
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function format (array $record)
+    {
+        return $this->formatArray($record);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function formatBatch (array $records)
+    {
+        foreach ($records as $key => $record) {
+            $records[$key] = $this->format($record);
+        }
+
+        return $records;
+    }
+
+    protected function formatArray (array $record, $nestingLevel = 0)
+    {
+        if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
+            foreach ($record as $name => $value) {
+                if ($value instanceof \DateTime) {
+                    $record[$name] = $this->formatDate($value, $nestingLevel + 1);
+                } elseif ($value instanceof \Exception) {
+                    $record[$name] = $this->formatException($value, $nestingLevel + 1);
+                } elseif (is_array($value)) {
+                    $record[$name] = $this->formatArray($value, $nestingLevel + 1);
+                } elseif (is_object($value)) {
+                    $record[$name] = $this->formatObject($value, $nestingLevel + 1);
+                }
+            }
+        } else {
+            $record = '[...]';
+        }
+
+        return $record;
+    }
+
+    protected function formatObject ($value, $nestingLevel)
+    {
+        $objectVars = get_object_vars($value);
+        $objectVars['class_name'] = get_class($value);
+        return $this->formatArray($objectVars, $nestingLevel);
+    }
+
+    protected function formatException (\Exception $exception, $nestingLevel)
+    {
+        $formattedException = array(
+            'class_name' => get_class($exception),
+            'message' => $exception->getMessage(),
+            'code' => $exception->getCode(),
+            'file' => $exception->getFile(),
+            'line' => $exception->getLine(),
+        );
+
+        if ($this->exceptionTraceAsString === true) {
+            $formattedException['trace'] = $exception->getTraceAsString();
+        } else {
+            $formattedException['trace'] = $exception->getTrace();
+        }
+
+        return $this->formatArray($formattedException, $nestingLevel);
+    }
+
+    protected function formatDate (\DateTime $value, $nestingLevel)
+    {
+        return new \MongoDate($value->getTimestamp());
+    }
+}

+ 255 - 0
tests/Monolog/Formatter/MongoDBFormatterTest.php

@@ -0,0 +1,255 @@
+<?php
+
+namespace Monolog\Formatter;
+
+use Monolog\Logger;
+
+/**
+ * @author Florian Plattner <me@florianplattner.de>
+ */
+class MongoDBFormatterTest extends \PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        if (!class_exists('MongoDate')) {
+            $this->markTestSkipped('mongo extension not installed');
+        }
+    }
+
+    public function constructArgumentProvider()
+    {
+        return array(
+            array(1, true, 1, true),
+            array(0, false, 0, false),
+            array(100000, 'true', 100000, true),
+            array(-1, 'false', 0, false)
+        );
+    }
+
+    /**
+     * @param $traceDepth
+     * @param $traceAsString
+     * @param $expectedTraceDepth
+     * @param $expectedTraceAsString
+     *
+     * @dataProvider constructArgumentProvider
+     */
+    public function testConstruct($traceDepth, $traceAsString, $expectedTraceDepth, $expectedTraceAsString)
+    {
+        $formatter = new MongoDBFormatter($traceDepth, $traceAsString);
+
+        $reflTrace = new \ReflectionProperty($formatter, 'exceptionTraceAsString');
+        $reflTrace->setAccessible(true);
+        $this->assertEquals($expectedTraceAsString, $reflTrace->getValue($formatter));
+
+        $reflDepth = new\ReflectionProperty($formatter, 'maxNestingLevel');
+        $reflDepth->setAccessible(true);
+        $this->assertEquals($expectedTraceDepth, $reflDepth->getValue($formatter));
+    }
+
+    public function testSimpleFormat()
+    {
+        $record = array(
+            'message' => 'some log message',
+            'context' => array(),
+            'level' => Logger::WARNING,
+            'level_name' => Logger::getLevelName(Logger::WARNING),
+            'channel' => 'test',
+            'datetime' => new \DateTime('2014-02-01 00:00:00'),
+            'extra' => array(),
+        );
+
+        $formatter = new MongoDBFormatter();
+        $formattedRecord = $formatter->format($record);
+
+        $this->assertCount(7, $formattedRecord);
+        $this->assertEquals('some log message', $formattedRecord['message']);
+        $this->assertEquals(array(), $formattedRecord['context']);
+        $this->assertEquals(Logger::WARNING, $formattedRecord['level']);
+        $this->assertEquals(Logger::getLevelName(Logger::WARNING), $formattedRecord['level_name']);
+        $this->assertEquals('test', $formattedRecord['channel']);
+        $this->assertInstanceOf('\MongoDate', $formattedRecord['datetime']);
+        $this->assertEquals('0.00000000 1391212800', $formattedRecord['datetime']->__toString());
+        $this->assertEquals(array(), $formattedRecord['extra']);
+    }
+
+    public function testRecursiveFormat()
+    {
+        $someObject = new \stdClass();
+        $someObject->foo = 'something';
+        $someObject->bar = 'stuff';
+
+        $record = array(
+            'message' => 'some log message',
+            'context' => array(
+                'stuff' => new \DateTime('2014-02-01 02:31:33'),
+                'some_object' => $someObject,
+                'context_string' => 'some string',
+                'context_int' => 123456,
+                'except' => new \Exception('exception message', 987),
+            ),
+            'level' => Logger::WARNING,
+            'level_name' => Logger::getLevelName(Logger::WARNING),
+            'channel' => 'test',
+            'datetime' => new \DateTime('2014-02-01 00:00:00'),
+            'extra' => array(),
+        );
+
+        $formatter = new MongoDBFormatter();
+        $formattedRecord = $formatter->format($record);
+
+        $this->assertCount(5, $formattedRecord['context']);
+        $this->assertInstanceOf('\MongoDate', $formattedRecord['context']['stuff']);
+        $this->assertEquals('0.00000000 1391221893', $formattedRecord['context']['stuff']->__toString());
+        $this->assertEquals(
+            array(
+                'foo' => 'something',
+                'bar' => 'stuff',
+                'class_name' => 'stdClass',
+            ),
+            $formattedRecord['context']['some_object']
+        );
+        $this->assertEquals('some string', $formattedRecord['context']['context_string']);
+        $this->assertEquals(123456, $formattedRecord['context']['context_int']);
+
+        $this->assertCount(6, $formattedRecord['context']['except']);
+        $this->assertEquals('exception message', $formattedRecord['context']['except']['message']);
+        $this->assertEquals(987, $formattedRecord['context']['except']['code']);
+        $this->assertInternalType('string', $formattedRecord['context']['except']['file']);
+        $this->assertInternalType('integer', $formattedRecord['context']['except']['code']);
+        $this->assertInternalType('string', $formattedRecord['context']['except']['trace']);
+        $this->assertEquals('Exception', $formattedRecord['context']['except']['class_name']);
+    }
+
+    public function testFormatDepthArray()
+    {
+        $record = array(
+            'message' => 'some log message',
+            'context' => array(
+                'nest2' => array(
+                    'property' => 'anything',
+                    'nest3' => array(
+                        'nest4' => 'value',
+                        'property' => 'nothing'
+                    )
+                )
+            ),
+            'level' => Logger::WARNING,
+            'level_name' => Logger::getLevelName(Logger::WARNING),
+            'channel' => 'test',
+            'datetime' => new \DateTime('2014-02-01 00:00:00'),
+            'extra' => array(),
+        );
+
+        $formatter = new MongoDBFormatter(2);
+        $formattedResult = $formatter->format($record);
+
+        $this->assertEquals(
+            array(
+                'nest2' => array(
+                    'property' => 'anything',
+                    'nest3' => '[...]',
+                )
+            ),
+            $formattedResult['context']
+        );
+    }
+
+    public function testFormatDepthArrayInfiniteNesting()
+    {
+        $record = array(
+            'message' => 'some log message',
+            'context' => array(
+                'nest2' => array(
+                    'property' => 'something',
+                    'nest3' => array(
+                        'property' => 'anything',
+                        'nest4' => array(
+                            'property' => 'nothing',
+                        ),
+                    )
+                )
+            ),
+            'level' => Logger::WARNING,
+            'level_name' => Logger::getLevelName(Logger::WARNING),
+            'channel' => 'test',
+            'datetime' => new \DateTime('2014-02-01 00:00:00'),
+            'extra' => array(),
+        );
+
+        $formatter = new MongoDBFormatter(0);
+        $formattedResult = $formatter->format($record);
+
+        $this->assertEquals(
+            array(
+                'nest2' => array(
+                    'property' => 'something',
+                    'nest3' => array(
+                        'property' => 'anything',
+                        'nest4' => array(
+                            'property' => 'nothing',
+                        )
+                    ),
+                )
+            ),
+            $formattedResult['context']
+        );
+    }
+
+    public function testFormatDepthObjects()
+    {
+        $someObject = new \stdClass();
+        $someObject->property = 'anything';
+        $someObject->nest3 = new \stdClass();
+        $someObject->nest3->property = 'nothing';
+        $someObject->nest3->nest4 = 'invisible';
+
+        $record = array(
+            'message' => 'some log message',
+            'context' => array(
+                'nest2' => $someObject
+            ),
+            'level' => Logger::WARNING,
+            'level_name' => Logger::getLevelName(Logger::WARNING),
+            'channel' => 'test',
+            'datetime' => new \DateTime('2014-02-01 00:00:00'),
+            'extra' => array(),
+        );
+
+        $formatter = new MongoDBFormatter(2, true);
+        $formattedResult = $formatter->format($record);
+
+        $this->assertEquals(
+            array(
+                'nest2' => array(
+                    'property' => 'anything',
+                    'nest3' => '[...]',
+                    'class_name' => 'stdClass',
+                ),
+            ),
+            $formattedResult['context']
+        );
+    }
+
+    public function testFormatDepthException()
+    {
+        $record = array(
+            'message' => 'some log message',
+            'context' => array(
+                'nest2' => new \Exception('exception message', 987),
+            ),
+            'level' => Logger::WARNING,
+            'level_name' => Logger::getLevelName(Logger::WARNING),
+            'channel' => 'test',
+            'datetime' => new \DateTime('2014-02-01 00:00:00'),
+            'extra' => array(),
+        );
+
+        $formatter = new MongoDBFormatter(2, false);
+        $formattedRecord = $formatter->format($record);
+
+        $this->assertEquals('exception message', $formattedRecord['context']['nest2']['message']);
+        $this->assertEquals(987, $formattedRecord['context']['nest2']['code']);
+        $this->assertEquals('[...]', $formattedRecord['context']['nest2']['trace']);
+    }
+}