├── .gitignore ├── tests ├── Fixtures │ ├── apache_valid_error2.log │ ├── formless_error.log │ ├── apache_invalid_error.log │ ├── apache_valid_error.log │ └── nginx_valid_error.log └── ParserTest.php ├── infection.json.dist ├── src ├── Parser │ ├── ParserInterface.php │ ├── ApacheParser.php │ ├── FormlessParser.php │ ├── NginxParser.php │ └── AbstractParser.php ├── Exception │ ├── UnknownTypeException.php │ └── FormatException.php └── Parser.php ├── .travis.yml ├── phpunit.xml.dist ├── CONTRIBUTING.md ├── LICENSE ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /tests/Fixtures/apache_valid_error2.log: -------------------------------------------------------------------------------- 1 | [Tue Jun 21 14:56:32 2016] [warn] mod_fcgid: do nice things 2 | -------------------------------------------------------------------------------- /tests/Fixtures/formless_error.log: -------------------------------------------------------------------------------- 1 | 23263#0: *1 directory index of "/var/www/ssl/" is forbidden, client: 86.186.86.232, server: hotelpublisher.com, request: "GET / HTTP/1.1", host: "hotelpublisher.com" 2 | -------------------------------------------------------------------------------- /tests/Fixtures/apache_invalid_error.log: -------------------------------------------------------------------------------- 1 | Tue Dec 29 08:14:45 2015 warn 193.158.15.243 mod_fcgid: stderr: PHP Warning: Division by zero in /var/www/kd/app.de/src/Calc.php on line 346, referer: https://www.app.de 2 | -------------------------------------------------------------------------------- /infection.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "timeout": 10, 3 | "source": { 4 | "directories": [ 5 | "src" 6 | ] 7 | }, 8 | "logs": { 9 | "text": "infection-log.txt" 10 | } 11 | } -------------------------------------------------------------------------------- /tests/Fixtures/apache_valid_error.log: -------------------------------------------------------------------------------- 1 | [Tue Dec 29 08:14:45 2015] [warn] [client 193.158.15.243] mod_fcgid: stderr: PHP Warning: Division by zero in /var/www/kd/app.de/src/Calc.php on line 346, referer: https://www.app.de 2 | -------------------------------------------------------------------------------- /tests/Fixtures/nginx_valid_error.log: -------------------------------------------------------------------------------- 1 | 2011/06/10 13:30:10 [error] 23263#0: *1 directory index of "/var/www/ssl/" is forbidden, client: 86.186.86.232, server: hotelpublisher.com, request: "GET / HTTP/1.1", host: "hotelpublisher.com" 2 | -------------------------------------------------------------------------------- /src/Parser/ParserInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests/ 6 | 7 | 8 | 9 | 10 | 11 | ./src/ 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Workflow 4 | 5 | * Fork the project 6 | * Make your bug fix or feature addition 7 | * Add tests for it. This is important so we don't break it in a future version unintentionally 8 | * Send a pull request 9 | 10 | ## Attention 11 | 12 | Please make sure that you have set up your user name and email address for use with Git. 13 | Strings such as silly nick name look really stupid in the commit history of a project. 14 | 15 | Pull requests must be based on the current stable branch (master). 16 | -------------------------------------------------------------------------------- /src/Parser/ApacheParser.php: -------------------------------------------------------------------------------- 1 | '~^\[(.*?)\]~', 19 | 'type' => '/\[(warn|error|debug|info|crit|emerg|notice|alert)\]/', 20 | 'client' => '~\] \[client ([0-9\.]*)\]~', 21 | 'message' => '/\]+\s+(?!\[)(.*)/', 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Parser/FormlessParser.php: -------------------------------------------------------------------------------- 1 | type = 'info'; 22 | $object->message = $line; 23 | 24 | return $object; 25 | } 26 | 27 | /** 28 | * @return array 29 | */ 30 | public function getPatterns() 31 | { 32 | return []; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Parser/NginxParser.php: -------------------------------------------------------------------------------- 1 | '((\d{4}\/\d{2}\/\d{2}\s\d{2}:\d{2}:\d{2})\s)', 19 | 'type' => '(\s\[([^]]*)\])', 20 | 'message' => '(\[*:\s(.*)\s?,\sclient)', 21 | 'client' => '(\sclient:\s([^,]*),)', 22 | 'server' => '(\sserver:\s([^,]*),)', 23 | 'request' => '(\srequest:\s"([^"]*)",)', 24 | 'host' => '(\shost:\s"([^"]*)")', 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/FormatException.php: -------------------------------------------------------------------------------- 1 | getPatterns() as $key => $pattern) { 26 | $result = preg_match($pattern, $line, $matches); 27 | 28 | if (1 !== $result) { 29 | continue; 30 | } 31 | 32 | $object->$key = $matches[1]; 33 | } 34 | 35 | if (false === property_exists($object, 'date')) { 36 | throw new FormatException; 37 | } 38 | 39 | if (false === property_exists($object, 'type')) { 40 | throw new FormatException; 41 | } 42 | 43 | return $object; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Tommy Mühle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tm/error-log-parser", 3 | "type": "library", 4 | "description": "PHP error_log parser library.", 5 | "keywords": ["error_log", "error", "log", "apache", "parser", "nginx"], 6 | "homepage": "https://github.com/tommy-muehle/error-log-parser", 7 | "authors": [ 8 | { 9 | "name": "Tommy Muehle", 10 | "email": "tommy.muehle@gmail.com", 11 | "homepage": "https://tommy-muehle.de" 12 | } 13 | ], 14 | "support": { 15 | "email": "tommy.muehle@gmail.com", 16 | "issues": "https://github.com/tommy-muehle/error-log-parser/issues", 17 | "docs": "https://github.com/tommy-muehle/error-log-parser/blob/master/README.md", 18 | "source": "https://github.com/tommy-muehle/error-log-parser/tree/master" 19 | }, 20 | "license": "MIT", 21 | "require": { 22 | "php": ">=5.6.0" 23 | }, 24 | "require-dev": { 25 | "phpunit/phpunit": "^5.7" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "TM\\ErrorLogParser\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "TM\\ErrorLogParser\\Tests\\": "tests/" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Parser.php: -------------------------------------------------------------------------------- 1 | parser = new ApacheParser; 38 | } 39 | 40 | if (self::TYPE_NGINX === $type) { 41 | $this->parser = new NginxParser; 42 | } 43 | 44 | if (self::TYPE_FORMLESS === $type) { 45 | $this->parser = new FormlessParser(); 46 | } 47 | 48 | if (!$this->parser instanceof AbstractParser) { 49 | throw new UnknownTypeException; 50 | } 51 | } 52 | 53 | /** 54 | * @param string $line 55 | * 56 | * @return \stdClass 57 | * @throws FormatException 58 | */ 59 | public function parse($line) 60 | { 61 | return $this->parser->parse($line); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # error-log-parser 2 | 3 | [![Build Status](https://travis-ci.org/tommy-muehle/error-log-parser.svg?branch=master)](https://travis-ci.org/tommy-muehle/error-log-parser) 4 | [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%205.5-8892BF.svg?style=flat-square)](https://php.net/) 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/tommy-muehle/error-log-parser/master/LICENSE) 6 | [![GitHub issues](https://img.shields.io/github/issues/tommy-muehle/error-log-parser.svg)](https://github.com/tommy-muehle/error-log-parser/issues) 7 | 8 | Simple PHP library to parse Apache or Nginx error-log file entries for further usage. 9 | 10 | ## Install 11 | 12 | Using [Composer](https://getcomposer.org/) 13 | 14 | $ composer require tm/error-log-parser 15 | 16 | or manually add this to composer.json 17 | 18 | { 19 | "require": { 20 | "tm/error-log-parser": "~1.1" 21 | } 22 | } 23 | 24 | ## Usage 25 | 26 | Instantiate the class: 27 | 28 | use TM\ErrorLogParser\Parser; 29 | $parser = new Parser(Parser::TYPE_APACHE) // or TYPE_NGINX; 30 | 31 | And then parse the lines: 32 | 33 | function getLines($file) 34 | { 35 | $f = fopen($file, 'r'); 36 | if (!$f) throw new Exception(); 37 | while ($line = fgets($f)) { 38 | yield $line; 39 | } 40 | fclose($f); 41 | } 42 | 43 | foreach (getLines('/var/log/apache2/error.log') as $line) { 44 | $entry = $parser->parse($line); 45 | } 46 | 47 | Where ```$entry``` hold all parsed data. 48 | For Apache: 49 | 50 | stdClass Object ( 51 | [date] => "Tue Dec 29 08:14:45 2015" 52 | [type] => "warn" 53 | [client] => "193.158.15.243" 54 | [message] => "mod_fcgid: stderr: PHP Warning: Division by zero in /var/www/kd/app.de/src/Calc.php on line 346, referer: https://www.app.de" 55 | ) 56 | 57 | And for Nginx: 58 | 59 | stdClass Object ( 60 | [date] => "2011/06/10 13:30:10" 61 | [type] => "error" 62 | [message] => "*1 directory index of "/var/www/ssl/" is forbidden" 63 | [client] => "86.186.86.232" 64 | [server] => "hotelpublisher.com" 65 | [request] => "GET / HTTP/1.1" 66 | [host] => "hotelpublisher.com" 67 | ) 68 | 69 | Otherwise you can use the FormlessParser for formless log files: 70 | 71 | stdClass Object ( 72 | [type] => "info" 73 | [message] => "23263#0: *1 directory index of "/var/www/ssl/" is forbidden, client: 86.186.86.232, server: hotelpublisher.com, request: "GET / HTTP/1.1", host: "hotelpublisher.com"" 74 | ) 75 | ## Contributing 76 | 77 | Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) for information on how to contribute. 78 | -------------------------------------------------------------------------------- /tests/ParserTest.php: -------------------------------------------------------------------------------- 1 | parse(current($lines)); 27 | 28 | $this->assertEquals('warn', $object->type); 29 | $this->assertNotNull($object->message); 30 | } 31 | 32 | /** 33 | * @dataProvider getValidFormlessLogFiles 34 | * 35 | * @param string $logfile 36 | */ 37 | public function testCanParseAFormlessErrorLogLine($logfile) 38 | { 39 | $parser = new Parser(Parser::TYPE_FORMLESS); 40 | $lines = file($logfile); 41 | 42 | $object = $parser->parse(current($lines)); 43 | 44 | $this->assertEquals('info', $object->type); 45 | $this->assertNotNull($object->message); 46 | } 47 | 48 | /** 49 | * @dataProvider getInvalidApacheLogFiles 50 | * @expectedException \TM\ErrorLogParser\Exception\FormatException 51 | * @expectedExceptionMessageRegExp /The parser only supports the default Log-Format/ 52 | * 53 | * @param $logfile 54 | */ 55 | public function testSpecialLogFormatThrowsAnException($logfile) 56 | { 57 | $parser = new Parser(Parser::TYPE_APACHE); 58 | $lines = file($logfile); 59 | 60 | $parser->parse(current($lines)); 61 | } 62 | 63 | /** 64 | * @dataProvider getValidNginxLogFiles 65 | * 66 | * @param string $logfile 67 | */ 68 | public function testCanParseACorrectNginxErrorLogLine($logfile) 69 | { 70 | $parser = new Parser(Parser::TYPE_NGINX); 71 | $lines = file($logfile); 72 | 73 | $object = $parser->parse(current($lines)); 74 | 75 | $this->assertEquals('error', $object->type); 76 | $this->assertEquals('86.186.86.232', $object->client); 77 | $this->assertEquals('hotelpublisher.com', $object->server); 78 | } 79 | 80 | public function testThrowUnknownTypeExceptionOnInvalidParserType() 81 | { 82 | $this->setExpectedException(UnknownTypeException::class); 83 | 84 | new Parser('UnknownParserType'); 85 | } 86 | 87 | /** 88 | * @return void 89 | */ 90 | public function testCanGetPattern() 91 | { 92 | $this->assertTrue(is_array((new Parser\ApacheParser())->getPatterns())); 93 | $this->assertTrue(is_array((new Parser\NginxParser())->getPatterns())); 94 | $this->assertTrue(is_array((new Parser\FormlessParser())->getPatterns())); 95 | } 96 | 97 | /** 98 | * @return array 99 | */ 100 | public function getValidNginxLogFiles() 101 | { 102 | return [ 103 | [__DIR__ . '/Fixtures/nginx_valid_error.log'], 104 | ]; 105 | } 106 | 107 | /** 108 | * @return array 109 | */ 110 | public function getValidFormlessLogFiles() 111 | { 112 | return [ 113 | [__DIR__ . '/Fixtures/formless_error.log'], 114 | ]; 115 | } 116 | 117 | /** 118 | * @return array 119 | */ 120 | public function getInvalidApacheLogFiles() 121 | { 122 | return [ 123 | [__DIR__ . '/Fixtures/apache_invalid_error.log'], 124 | ]; 125 | } 126 | 127 | /** 128 | * @return array 129 | */ 130 | public function getValidApacheLogFiles() 131 | { 132 | return [ 133 | [__DIR__ . '/Fixtures/apache_valid_error.log'], 134 | [__DIR__ . '/Fixtures/apache_valid_error2.log'], 135 | ]; 136 | } 137 | } 138 | --------------------------------------------------------------------------------