ApacheErrorLogParser   A
last analyzed

Complexity

Total Complexity 3

Size/Duplication

Total Lines 230
Duplicated Lines 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
wmc 3
eloc 76
c 5
b 1
f 0
dl 0
loc 230
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 99 1
A setFormat() 0 7 2
1
<?php declare(strict_types=1);
2
3
/**
4
 *  ___             _
5
 * | _ \__ _ _ _ __| |___  __ _
6
 * |  _/ _` | '_(_-< / _ \/ _` |
7
 * |_| \__,_|_| /__/_\___/\__, |
8
 *                        |___/
9
 * 
10
 * (c) Kristuff <[email protected]>
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 *
15
 * @version    0.7.2
16
 * @copyright  2017-2021 Kristuff
17
 */
18
19
namespace Kristuff\Parselog\Software;
20
21
use Kristuff\Parselog\Core\LogEntryFactoryInterface;
22
23
/**
24
 * ApacheErrorLogParser 
25
 * 
26
 * The directive ErrorLogFormat is documented in version 2.4, but not in 2.2
27
 * 
28
 * Changes between 2.2/2.4:
29
 * - time field includes milliseconds in apache 2.4
30
 * - client field includes port number in apache 2.4
31
 * - note sure about pid/tid in apache 2.2
32
 * 
33
 * Example (default in 2.4): 
34
 * ErrorLogFormat "[%t] [%l] [pid %P] %F: %E: [client %a] %M"
35
 * 
36
 * Example (similar to the 2.2.x format):
37
 * ErrorLogFormat "[%t] [%l] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
38
 * 
39
 * Example (default format for threaded MPMs):
40
 * ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
41
 * 
42
 * Note that depending on error, some field may be missing from output.
43
 * 
44
 * 2.2:                 [Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client denied by server configuration: /export/home/live/ap/htdocs/test
45
 * 2.4:                 [Thu Jun 27 11:55:44.569531 2013] [core:info] [pid 4101:tid 2992634688] [client 1.2.3.4:46652]
46
 * 2.4:                 [Thu Oct 01 14:01:53.127021 2020] [reqtimeout:info] [pid 21290] [client 1.2.3.4:63044] AH01382: Request header read timeout
47
 * 2.4:                 [Sun Sep 27 00:00:48.784450 2020] [mpm_prefork:notice] [pid 747] AH00163: Apache/2.4.38 (Debian) OpenSSL/1.1.1d configured -- resuming normal operations
48
 * 2.4 (with client):   [Thu Jun 27 11:55:44.569531 2013] [core:info] [pid 4101:tid 2992634688] [client 1.2.3.4:46652] AH00128: File does not exist: <path>
49
 * 2.4 (no client):     [Fri Sep 25 20:23:41.378709 2020] [mpm_prefork:notice] [pid 10578] AH00169: caught SIGTERM, shutting down
50
 * 2.4 (perfork):       [Mon Dec 23 07:49:01.981912 2013] [:error] [pid 3790] [client 1.2.3.4:46301] script '/var/www/timthumb.php' not found or unable to
51
 * 2.4 (with referer):  [Sat Oct 03 13:56:38.054651 2020] [authz_core:error] [pid 6257] [client 1.2.3.4:63032] AH01630: client denied by server configuration: /var/www/xxx.dommain.fr, referer: https://xxx.dommain.fr/
52
 * 2.4 (with 'F:'):     [Fri Jul 23 21:29:32.087762 2021] [php7:error] [pid 29504] sapi_apache2.c(356): [client 1.2.3.4:64950] script '/var/www/foo.php' not found or unable to stat
53
 * 2.4  ???             [Tue Oct 13 23:03:12.080268 2020] [proxy:error] [pid 29705] (20014)Internal error (specific information not available): [client 1.2.3.4:56450] AH01084: pass request body failed to [::1]:8080 (localhost)
54
 * 2.4  ???             [Thu Jul 22 08:19:23.627412 2021] [proxy_http:error] [pid 1723] (-102)Unknown error -102: [client 1.2.3.4:32840] AH01095: prefetch request body failed to 127.0.0.1:3000 (127.0.0.1) from 1.2.3.4 ()
55
 * @see https://httpd.apache.org/docs/2.2/mod/core.html#errorlog
56
 * @see https://httpd.apache.org/docs/2.4/mod/core.html#errorlogformat
57
 * @see https://github.com/fail2ban/fail2ban/issues/268
58
 */
59
class ApacheErrorLogParser extends SoftwareLogParser
60
{
61
    /**
62
     * Based on that example (similar to the 2.2.x format):
63
     * ErrorLogFormat "[%t] [%l] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
64
     * @see https://httpd.apache.org/docs/2.4/mod/core.html#errorlogformat
65
     */
66
    const FORMAT_APACHE_2_2_DEFAULT = '[%t] [%l] %E: [client %a] %M';
67
68
    /**
69
     * Based on that example (similar to the 2.2.x format):
70
     * ErrorLogFormat "[%t] [%l] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
71
     * @see https://httpd.apache.org/docs/2.4/mod/core.html#errorlogformat
72
     * 
73
     * 2_2_DEFAULT + referer
74
     */
75
    const FORMAT_APACHE_2_2_REFERER = '[%t] [%l] %E: [client %a] %M ,\ referer\ %{Referer}i';
76
77
    /**
78
     * Based on that example (similar to the 2.2.x format):
79
     * ErrorLogFormat "[%t] [%l] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
80
     * @see https://httpd.apache.org/docs/2.4/mod/core.html#errorlogformat
81
     * 
82
     * 2_2_DEFAULT + %F: 
83
     */
84
    const FORMAT_APACHE_2_2_EXTENDED = '[%t] [%l] %F: %E: [client %a] %M';
85
86
    /**
87
     * Based on that example (similar to the 2.2.x format):
88
     * ErrorLogFormat "[%t] [%l] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
89
     * @see https://httpd.apache.org/docs/2.4/mod/core.html#errorlogformat
90
     * 
91
     * 2_2_DEFAULT + %F: + referer
92
     */
93
    const FORMAT_APACHE_2_2_EXTENDED_REFERER = '[%t] [%l] %F: %E: [client %a] %M ,\ referer\ %{Referer}i';
94
95
    /**
96
     * Since apache 2.4, time includes milliseconds [%{u}t] and pid seems to be here by default.
97
     */
98
    const FORMAT_APACHE_2_4_DEFAULT = '[%{u}t] [%l] [pid %P] %E: [client %a] %M';
99
100
    /**
101
     * 2_4_DEFAULT + referer
102
     */
103
    const FORMAT_APACHE_2_4_REFEFER = '[%{u}t] [%l] [pid %P] %E: [client %a] %M ,\ referer\ %{Referer}i';
104
105
    /**
106
     * 2_4_DEFAULT + %F:
107
     */
108
    const FORMAT_APACHE_2_4_EXTENDED = '[%{u}t] [%l] [pid %P] %F: %E: [client %a] %M';
109
110
    /**
111
     * 2_4_DEFAULT + %F: + referer
112
     */
113
    const FORMAT_APACHE_2_4_EXTENDED_REFERER = '[%{u}t] [%l] [pid %P] %F: %E: [client %a] %M ,\ referer\ %{Referer}i';
114
    
115
    /**
116
     * Based on that example (default format for threaded MPMs)
117
     * ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
118
     * @see https://httpd.apache.org/docs/2.4/fr/mod/core.html#errorlog
119
     * 
120
     * 2_4_DEFAULT + %m
121
     * Note: No %F: (%7F: in example above, filtered to level 7 or more), no referer
122
     */
123
    const FORMAT_APACHE_2_4_MPM = '[%{u}t] [%-m:%l] [pid %P] %E: [client %a] %M';
124
125
    /**
126
     * Based on that example (default format for threaded MPMs)
127
     * ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
128
     * @see https://httpd.apache.org/docs/2.4/fr/mod/core.html#errorlog
129
     * 
130
     * 2_4_NPM + %F:
131
     */
132
    const FORMAT_APACHE_2_4_MPM_EXTENDED = '[%{u}t] [%-m:%l] [pid %P] %F: %E: [client %a] %M';
133
134
    /**
135
     * Based on that example (default format for threaded MPMs)
136
     * ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
137
     * @see https://httpd.apache.org/docs/2.4/fr/mod/core.html#errorlog
138
     * 
139
     * 2_4_NPM + %F: + referer
140
     */
141
    const FORMAT_APACHE_2_4_MPM_REFERER = '[%{u}t] [%-m:%l] [pid %P] %E: [client %a] %M ,\ referer\ %{Referer}i';
142
143
    /**
144
     * based on that example (default format for threaded MPMs)
145
     * ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
146
     * @see https://httpd.apache.org/docs/2.4/fr/mod/core.html#errorlog
147
     */
148
    const FORMAT_APACHE_2_4_MPM_EXTENDED_REFERER = '[%{u}t] [%-m:%l] [pid %P] %F: %E: [client %a] %M ,\ referer\ %{Referer}i';
149
150
    /**
151
     * 2_4_NPM + tid %T + %F:
152
     */
153
    const FORMAT_APACHE_2_4_MPM_TID = '[%{u}t] [%-m:%l] [pid %P:tid %T] %F: %E: [client %a] %M';
154
 
155
    /**
156
     * Based on that example (default format for threaded MPMs)
157
     * ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %a] %M% ,\ referer\ %{Referer}i"
158
     * @see https://httpd.apache.org/docs/2.4/fr/mod/core.html#errorlog
159
     * 
160
     * 2_4_NPM + tid %T + %F: + referer
161
     */
162
    const FORMAT_APACHE_2_4_MPM_TID_REFERER = '[%{u}t] [%-m:%l] [pid %P:tid %T] %F: %E: [client %a] %M ,\ referer\ %{Referer}i';
163
 
164
    /**
165
     * Constructor
166
     * 
167
     * @access public
168
     * @param string                    $format    
169
     * @param LogEntryFactoryInterface  $factory        
170
     * 
171
     * @return void
172
     */
173
    public function __construct(string $format = null, LogEntryFactoryInterface $factory = null)
174
    {
175
        $this->software           = 'Apache';
176
        $this->prettyName         = 'Apache Error';
177
        $this->defaultFormat      = self::FORMAT_APACHE_2_4_DEFAULT;
178
        
179
        // set 2.2 format by default. Will be changed to 2.4 format, ie:
180
        // $this->timeFormat        = 'D M d H:i:s.u Y';
181
        // , if the format contains %{u} instead of %t  
182
        $this->timeFormat        = 'D M d H:i:s Y';
183
184
        $this->addFormat('default',                         self::FORMAT_APACHE_2_4_DEFAULT);  
185
        $this->addFormat('apache2.2 default',               self::FORMAT_APACHE_2_2_DEFAULT);  
186
        $this->addFormat('apache2.2 extented',              self::FORMAT_APACHE_2_2_EXTENDED);  
187
        $this->addFormat('apache2.2 referer',               self::FORMAT_APACHE_2_2_REFERER);  
188
        $this->addFormat('apache2.2 extented referer',      self::FORMAT_APACHE_2_2_EXTENDED_REFERER);  
189
        $this->addFormat('apache2.4 default',               self::FORMAT_APACHE_2_4_DEFAULT);  
190
        $this->addFormat('apache2.4 extented',              self::FORMAT_APACHE_2_4_EXTENDED);  
191
        $this->addFormat('apache2.4 referer',               self::FORMAT_APACHE_2_4_REFEFER);  
192
        $this->addFormat('apache2.4 extented referer',      self::FORMAT_APACHE_2_4_EXTENDED_REFERER);  
193
        $this->addFormat('apache2.4 mpm',                   self::FORMAT_APACHE_2_4_MPM);  
194
        $this->addFormat('apache2.4 mpm extented',          self::FORMAT_APACHE_2_4_MPM_EXTENDED);  
195
        $this->addFormat('apache2.4 mpm referer',           self::FORMAT_APACHE_2_4_MPM_REFERER);  
196
        $this->addFormat('apache2.4 mpm extented referer ', self::FORMAT_APACHE_2_4_MPM_EXTENDED_REFERER);  
197
        $this->addFormat('apache2.4 mpm tid',               self::FORMAT_APACHE_2_4_MPM_TID);  
198
        $this->addFormat('apache2.4 mpm tid referer',       self::FORMAT_APACHE_2_4_MPM_TID_REFERER);  
199
200
        $this->addPath("/var/log/");
201
        $this->addPath("/var/log/apache/");
202
        $this->addPath("/var/log/apache2/");
203
        $this->addPath("/var/log/httpd/");
204
        $this->addPath("/usr/local/var/log/apache/");
205
        $this->addPath("/usr/local/var/log/apache2/");
206
        $this->addPath("/usr/local/var/log/httpd/");
207
        $this->addPath("/opt/local/apache/logs/");
208
        $this->addPath("/opt/local/apache2/logs/");
209
        $this->addPath("/opt/local/httpd/logs/");
210
        $this->addPath("C:/wamp/logs/");
211
212
        $this->addFile('error.log');
213
        $this->addFile('error_log');
214
        $this->addFile("apache_error.log"); 
215
216
        // ***************
217
        // define patterns
218
        // ***************
219
220
        $this->addNamedPattern('%%' , 'percent', '\%');
221
                
222
        // %t 	        The current time
223
        // %{u}t 	The current time including micro-seconds
224
        $datePattern = '\[(?P<time>(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{2} \d{2}:\d{2}:\d{2}(?:\.\d{6}|) \d{4})\]';
225
        $this->addPattern('\[%{u}t\]', $datePattern);
226
        $this->addPattern('\[%t\]', $datePattern);
227
        $this->addPattern('%t', $datePattern);
228
        
229
        // %a 	Client IP address and port of the request (port is not registered by parser). 
230
        //      That column may be missing depending of error
231
        $clientIpPattern = '( \[client (?P<remoteIp>' . self::PATTERN_IP_ALL . ')(:[\d]+|)?\])?';
232
        $this->addPattern(' \[client %a\]' , $clientIpPattern);
233
        $this->addPattern(' \[%a\]' ,  $clientIpPattern);
234
        $this->addPattern(' %a' , $clientIpPattern);
235
236
        // %A 	Local IP-address and port
237
        //      That column may be missing depending of error
238
        $this->addNamedPattern('%A',  'localIP', self::PATTERN_IP_ALL, false);
239
        
240
        // %m 	Name of the module logging the message
241
        // %l 	Loglevel of the message
242
        $this->addPattern('\[%m:%l\]',  '\[(?<module>.+?)?:(?P<level>[\w]+)\]');
243
        $this->addPattern('\[%-m:%l\]', '\[(?<module>.+?)?:(?P<level>[\w]+)\]');
244
        $this->addPattern('\[%l\]',     '\[(?P<level>[\w:]+)\]');
245
        $this->addPattern('%l',         '\[(?P<level>[\w:]+)\]');
246
247
        // %P 	Process ID of current process (since apache 2.4?)
248
        // %T 	Thread ID of current thread
249
        $this->addPattern('\[pid %P:tid %T\]', '\[pid (?P<pid>\d+):tid (?P<tid>\d+)\]'); 
250
        $this->addPattern('%P %T',             '\[pid (?P<pid>\d+):tid (?P<tid>\d+)\]');
251
        $this->addPattern('\[pid %P\]', '\[pid (?P<pid>\d+)\]');
252
        $this->addPattern('\[%P\]',     '\[pid (?P<pid>\d+)\]');   
253
        $this->addPattern('%P',         '\[pid (?P<pid>\d+)\]');
254
        
255
        // %E 	APR/OS error status code and string
256
        //      That column may be missing depending of error
257
        $this->addPattern(' %E:', '( (?P<errorCode>\([\-\d]+\)[^\[]+):)?');              
258
259
        // %F 	Source file name and line number of the log call
260
        //$this->addPattern(' %F:', '( (?P<fileName>[^\*\s\|><\?]*[\/][^\*\s\|><\?]*):)?');              
261
        $this->addPattern(' %F:', '( (?P<fileName>[^ ]+\([\d]+\)):)?');              
262
263
        // %M 	The actual log message
264
        $this->addNamedPattern('%M',  'message', '.+?');
265
266
        // referer (may be empty)
267
        $this->addPattern(' , referer %{Referer}i',   '(, referer: (?P<referer>[^ ]+))?');
268
        $this->addPattern(', referer %{Referer}i',    '(, referer: (?P<referer>[^ ]+))?');
269
270
        // now let default constructor
271
        parent::__construct($format, $factory);
272
    } 
273
274
    /**
275
     * Sets the log format  
276
     * 
277
     * @access public
278
     * @param string    $format
279
     * 
280
     * @return void
281
     */
282
    public function setFormat(string $format): void
283
    {
284
        parent::setFormat($format);
285
286
        // set the correct time format
287
        if (strpos($this->logFormat, '%{u}t') !== false){
288
            $this->timeFormat = 'D M d H:i:s.u Y';
289
        }
290
    }
291
}