├── .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 | [](https://travis-ci.org/tommy-muehle/error-log-parser)
4 | [](https://php.net/)
5 | [](https://raw.githubusercontent.com/tommy-muehle/error-log-parser/master/LICENSE)
6 | [](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 |
--------------------------------------------------------------------------------