├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src └── Dubture │ └── Monolog │ ├── Parser │ ├── LineLogParser.php │ └── LogParserInterface.php │ └── Reader │ ├── AbstractReader.php │ └── LogReader.php └── tests ├── Dubture └── Monolog │ └── Reader │ └── Test │ ├── LogReaderTest.php │ └── ParserTest.php ├── bootstrap.php └── files └── test.log /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vendor 3 | composer.phar 4 | composer.lock 5 | .buildpath 6 | .settings 7 | .project 8 | bin 9 | /nbproject/* 10 | *.iml 11 | .idea 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | 9 | before_script: composer install -n 10 | 11 | script: ./vendor/bin/phpunit 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Robert Gruendler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Monolog Parser 2 | ============== 3 | 4 | [![Build Status](https://travis-ci.org/pulse00/monolog-parser.svg?branch=master)](https://travis-ci.org/pulse00/monolog-parser) 5 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/pulse00/monolog-parser/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/pulse00/monolog-parser/?branch=master) 6 | 7 | [![Latest Stable Version](https://poser.pugx.org/pulse00/monolog-parser/v/stable.svg)](https://packagist.org/packages/pulse00/monolog-parser) [![Total Downloads](https://poser.pugx.org/pulse00/monolog-parser/downloads.svg)](https://packagist.org/packages/pulse00/monolog-parser) [![Latest Unstable Version](https://poser.pugx.org/pulse00/monolog-parser/v/unstable.svg)](https://packagist.org/packages/pulse00/monolog-parser) [![License](https://poser.pugx.org/pulse00/monolog-parser/license.svg)](https://packagist.org/packages/pulse00/monolog-parser) 8 | 9 | 10 | A simple library for parsing [monolog](https://github.com/Seldaek/monolog) logfiles. 11 | 12 | ## Installation 13 | 14 | You can install the library using [composer]('http://getcomposer.org/) by adding `pulse00/monolog-parser` to your `composer.json`. 15 | 16 | ## Usage 17 | 18 | ```php 19 | require_once 'path/to/vendor/autoload.php'; 20 | 21 | use Dubture\Monolog\Reader\LogReader; 22 | 23 | $logFile = '/path/to/some/monolog.log'; 24 | $reader = new LogReader($logFile); 25 | 26 | foreach ($reader as $log) { 27 | echo sprintf("The log entry was written at %s. \n", $log['date']->format('Y-m-d h:i:s')); 28 | } 29 | 30 | $lastLine = $reader[count($reader)-1]; 31 | echo sprintf("The last log entry was written at %s. \n", $lastLine['date']->format('Y-m-d h:i:s')); 32 | 33 | ``` 34 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pulse00/monolog-parser", 3 | "description": "A parser for monolog log entries", 4 | "require": { 5 | "monolog/monolog": "1.*" 6 | }, 7 | "require-dev": { 8 | "phpunit/phpunit": "~4.5" 9 | }, 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Robert Gruendler", 14 | "email": "r.gruendler@gmail.com" 15 | } 16 | ], 17 | "autoload": { 18 | "psr-0": {"Dubture": "src/"} 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | tests/Dubture/ 7 | 8 | 9 | 10 | 11 | 12 | src/Dubture/ 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Dubture/Monolog/Parser/LineLogParser.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Dubture\Monolog\Parser; 13 | 14 | /** 15 | * Class LineLogParser 16 | * @package Dubture\Monolog\Parser 17 | */ 18 | class LineLogParser implements LogParserInterface 19 | { 20 | protected $pattern = '/\[(?P.*)\] (?P\w+).(?P\w+): (?P.*[^ ]+) (?P[^ ]+) (?P[^ ]+)/'; 21 | 22 | /** 23 | * Constructor 24 | * @param string $pattern 25 | */ 26 | public function __construct($pattern = null) 27 | { 28 | $this->pattern = ($pattern) ?: $this->pattern; 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function parse($log) 35 | { 36 | if( !is_string($log) || strlen($log) === 0) { 37 | return array(); 38 | } 39 | 40 | preg_match($this->pattern, $log, $data); 41 | 42 | if (!isset($data['date'])) { 43 | return array(); 44 | } 45 | 46 | return array( 47 | 'date' => \DateTime::createFromFormat('Y-m-d H:i:s', $data['date']), 48 | 'logger' => $data['logger'], 49 | 'level' => $data['level'], 50 | 'message' => $data['message'], 51 | 'context' => json_decode($data['context'], true), 52 | 'extra' => json_decode($data['extra'], true) 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Dubture/Monolog/Parser/LogParserInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Dubture\Monolog\Parser; 13 | 14 | /** 15 | * Interface LogParserInterface 16 | * @package Dubture\Monolog\Parser 17 | */ 18 | interface LogParserInterface 19 | { 20 | /** 21 | * @param string $log 22 | */ 23 | function parse($log); 24 | } 25 | -------------------------------------------------------------------------------- /src/Dubture/Monolog/Reader/AbstractReader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Dubture\Monolog\Reader; 13 | 14 | use Dubture\Monolog\Parser\LineLogParser; 15 | 16 | /** 17 | * Class AbstractReader 18 | * @package Dubture\Monolog\Reader 19 | */ 20 | class AbstractReader 21 | { 22 | /** @var string */ 23 | protected $defaultParserPattern; 24 | 25 | /** 26 | * @param $defaultParserPattern 27 | */ 28 | public function __construct($defaultParserPattern) 29 | { 30 | $this->defaultParserPattern = $defaultParserPattern; 31 | } 32 | 33 | /** 34 | * @return \Dubture\Monolog\Parser\LineLogParser 35 | */ 36 | protected function getDefaultParser() 37 | { 38 | return new LineLogParser($this->defaultParserPattern); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Dubture/Monolog/Reader/LogReader.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Dubture\Monolog\Reader; 13 | use Dubture\Monolog\Parser\LogParserInterface; 14 | 15 | /** 16 | * Class LogReader 17 | * @package Dubture\Monolog\Reader 18 | */ 19 | class LogReader extends AbstractReader implements \Iterator, \ArrayAccess, \Countable 20 | { 21 | /** 22 | * @var \SplFileObject 23 | */ 24 | protected $file; 25 | 26 | /** 27 | * @var integer 28 | */ 29 | protected $lineCount; 30 | 31 | /** 32 | * @var LogParserInterface 33 | */ 34 | protected $parser; 35 | 36 | /** 37 | * @param $file 38 | * @param null $defaultPatternPattern 39 | */ 40 | public function __construct($file, $defaultPatternPattern = null) 41 | { 42 | parent::__construct($defaultPatternPattern); 43 | 44 | $this->file = new \SplFileObject($file, 'r'); 45 | $i = 0; 46 | while (!$this->file->eof()) { 47 | $this->file->current(); 48 | $this->file->next(); 49 | $i++; 50 | } 51 | 52 | $this->lineCount = $i; 53 | $this->parser = $this->getDefaultParser(); 54 | } 55 | 56 | /** 57 | * @param LogParserInterface $parser 58 | * @return $this 59 | */ 60 | public function setParser(LogParserInterface $parser) 61 | { 62 | $this->parser = $parser; 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function offsetExists($offset) 71 | { 72 | return $offset < $this->lineCount; 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function offsetGet($offset) 79 | { 80 | $key = $this->file->key(); 81 | $this->file->seek($offset); 82 | $log = $this->current(); 83 | $this->file->seek($key); 84 | $this->file->current(); 85 | 86 | return $log; 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | */ 92 | public function offsetSet($offset, $value) 93 | { 94 | throw new \RuntimeException("LogReader is read-only."); 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | */ 100 | public function offsetUnset($offset) 101 | { 102 | throw new \RuntimeException("LogReader is read-only."); 103 | } 104 | 105 | /** 106 | * {@inheritdoc} 107 | */ 108 | public function rewind() 109 | { 110 | $this->file->rewind(); 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | */ 116 | public function next() 117 | { 118 | $this->file->next(); 119 | } 120 | 121 | /** 122 | * {@inheritdoc} 123 | */ 124 | public function current() 125 | { 126 | return $this->parser->parse($this->file->current()); 127 | } 128 | 129 | /** 130 | * {@inheritdoc} 131 | */ 132 | public function key() 133 | { 134 | return $this->file->key(); 135 | } 136 | 137 | /** 138 | * {@inheritdoc} 139 | */ 140 | public function valid() 141 | { 142 | return $this->file->valid(); 143 | } 144 | 145 | /** 146 | * {@inheritdoc} 147 | */ 148 | public function count() 149 | { 150 | return $this->lineCount; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tests/Dubture/Monolog/Reader/Test/LogReaderTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Dubture\Monolog\Reader\Test; 13 | 14 | use Dubture\Monolog\Reader\ReverseLogReader; 15 | use Dubture\Monolog\Reader\LogReader; 16 | 17 | /** 18 | * Class LogReaderTest 19 | * @package Dubture\Monolog\Reader\Test 20 | */ 21 | class LogReaderTest extends \PHPUnit_Framework_TestCase 22 | { 23 | public function testReader() 24 | { 25 | $file = __DIR__ . '/../../../../files/test.log'; 26 | $reader = new LogReader($file); 27 | 28 | $log = $reader[0]; 29 | 30 | $this->assertInstanceOf('\DateTime', $log['date']); 31 | $this->assertEquals('test', $log['logger']); 32 | $this->assertEquals('INFO', $log['level']); 33 | $this->assertEquals('foobar', $log['message']); 34 | $this->assertArrayHasKey('foo', $log['context']); 35 | 36 | $log = $reader[1]; 37 | 38 | $this->assertInstanceOf('\DateTime', $log['date']); 39 | $this->assertEquals('aha', $log['logger']); 40 | $this->assertEquals('DEBUG', $log['level']); 41 | $this->assertEquals('foobar', $log['message']); 42 | $this->assertArrayNotHasKey('foo', $log['context']); 43 | 44 | } 45 | 46 | public function testIterator() 47 | { 48 | // the test.log file contains 2 log lines 49 | $file = __DIR__ . '/../../../../files/test.log'; 50 | $reader = new LogReader($file); 51 | $lines = array(); 52 | $keys = array(); 53 | 54 | $this->assertTrue($reader->offsetExists(0)); 55 | $this->assertTrue($reader->offsetExists(1)); 56 | 57 | $this->assertFalse($reader->offsetExists(2)); 58 | $this->assertFalse($reader->offsetExists(3)); 59 | 60 | $this->assertEquals(2, count($reader)); 61 | 62 | foreach ($reader as $i => $log) { 63 | $test = $reader[0]; 64 | $lines[] = $log; 65 | $keys[] = $i; 66 | } 67 | 68 | $this->assertEquals(array(0, 1), $keys); 69 | $this->assertEquals('test', $lines[0]['logger']); 70 | $this->assertEquals('aha', $lines[1]['logger']); 71 | 72 | } 73 | 74 | /** 75 | * @expectedException RuntimeException 76 | */ 77 | public function testException() 78 | { 79 | $file = __DIR__ . '/../../../../files/test.log'; 80 | $reader = new LogReader($file); 81 | $reader[5] = 'foo'; 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/Dubture/Monolog/Reader/Test/ParserTest.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Dubture\Monolog\Reader\Test; 13 | 14 | use Dubture\Monolog\Parser\LineLogParser; 15 | 16 | /** 17 | * Class ParserTest 18 | * @package Dubture\Monolog\Reader\Test 19 | */ 20 | class ParserTest extends \PHPUnit_Framework_TestCase 21 | { 22 | public function testLineFormatter() 23 | { 24 | $parser = new LineLogParser(); 25 | $log = $parser->parse('[2013-03-16 14:19:51] test.INFO: foobar {"foo":"bar"} []'); 26 | 27 | $this->assertInstanceOf('\DateTime', $log['date']); 28 | $this->assertEquals('test', $log['logger']); 29 | $this->assertEquals('INFO', $log['level']); 30 | $this->assertEquals('foobar', $log['message']); 31 | $this->assertArrayHasKey('foo', $log['context']); 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 |