Changeset 3227
- Timestamp:
- 08/27/10 01:46:46 (21 months ago)
- File:
-
- 1 edited
-
trunk/engine/ispcp-apache-logger (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/engine/ispcp-apache-logger
r3201 r3227 2 2 3 3 # ispCP ω (OMEGA) a Virtual Hosting Control Panel 4 # Copyright (C) 2001-2006 by moleSoftware GmbH - http://www.molesoftware.com5 4 # Copyright (C) 2006-2010 by isp Control Panel - http://ispcp.net 6 5 # 7 6 # Version: $Id$ 7 # Author: Laurent Declercq <laurent.declercq@ispcp.net> 8 8 # 9 9 # The contents of this file are subject to the Mozilla Public License … … 17 17 # under the License. 18 18 # 19 # The Original Code is "VHCS - Virtual Hosting Control System". 20 # 21 # The Initial Developer of the Original Code is moleSoftware GmbH. 22 # Portions created by Initial Developer are Copyright (C) 2001-2006 23 # by moleSoftware GmbH. All Rights Reserved. 24 # Portions created by the ispCP Team are Copyright (C) 2006-2010 by 19 # The Original Code is "ispCP ω (OMEGA) a Virtual Hosting Control Panel". 20 # 21 # The Initial Developer of the Original Code is ispCP Team. 22 # Portions created by Initial Developer are Copyright (C) 2006-2010 by 25 23 # isp Control Panel. All Rights Reserved. 26 24 # … … 30 28 # 31 29 32 use strict; 33 use warnings; 34 use sigtrap qw(handler exitall HUP USR1 TERM INT PIPE); 35 use Getopt::Std; 36 37 use FindBin; 38 use lib "$FindBin::Bin/"; 39 require 'ispcp_common_code.pl'; 40 41 our %OPTS; 42 getopts( 'e', \%OPTS ); 43 44 my $MAXFILES = "33"; 45 if ( $main::cfg{'APACHE_MAX_OPEN_LOG'} ) { 46 $MAXFILES = $main::cfg{'APACHE_MAX_OPEN_LOG'}; 47 } 48 49 my $MAXLOGFILESIZE = 10485760; # 10Mb 50 if ( $main::cfg{'APACHE_MAX_LOG_FILE_SIZE'} ) { 51 $MAXLOGFILESIZE = $main::cfg{'APACHE_MAX_LOG_FILE_SIZE'}; 52 } 53 54 my %timestamps = (); 55 my %combined_logs = (); 56 my %traff_logs = (); 57 my %access_logs = (); 58 my %error_logs = (); 59 60 sub checkFileExists; 61 sub DefaultLogs(); 62 sub ErrorLogs(); 63 sub checkFileSize; 64 65 ## Creating log files if possible ## 66 67 if ( !-d $main::cfg{'LOG_DIR'} ) { 68 print STDERR "[ispcp-apache-logger] target directory ".$main::cfg{'LOG_DIR'}." does not exist - logging on standard streams.\n"; 30 ################################################################################ 31 ## Script description: ## 32 ## ispcp-apache-logger - ispCP apache logfiles handling ## 33 ## ## 34 ## I. CustomLog handler: ## 35 ## ## 36 ## When called without arguments, this script receives log lines through a ## 37 ## pipe and outputs their information into three separate files, depending on ## 38 ## the origin virtual host: ## 39 ## ## 40 ## - /var/log/apache2/domain.tld-combined.log ## 41 ## - /var/log/apache2/domain.tld-traf log ## 42 ## - /var/log/apache2/users/domain.tld-access.log ## 43 ## ## 44 ## The program assumes that the first field of each log line is the virtual ## 45 ## host identity (See the %v logging directive from Apache mod_log_config). ## 46 ## ## 47 ## The log format is currently recognized as follows: ## 48 ## ## 49 ## "%v %... %I %O" ## 50 ## ## 51 ## Where %... represents any other logging directives of your choice. ## 52 ## Additionally, %I and %O correspond to apache's mod_logio directives for ## 53 ## Input and Output bytes, used to generate the content of the traffic log ## 54 ## files. ## 55 ## ## 56 ## II. ErrorLog handler: ## 57 ## ## 58 ## When called with the 'e' argument, this script takes error log lines ## 59 ## pipe and outputs their information into a separate file depending on the ## 60 ## originating virtual host : ## 61 ## ## 62 ## - /var/log/apache2/users/domain.tld-error.log ## 63 ## ## 64 ## Due to the fixed log format of error log lines, the script uses a ## 65 ## workaround to retrieve the virtual host identity, by looking for any ## 66 ## virtual host information inside the line itself. If the script is unable ## 67 ## to identify the virtual host with this method, errors are written to ## 68 ## the fallback log file, named "default-error.log". ## 69 ## ## 70 ## See http://httpd.apache.org/docs/current/logs.html#piped for more info. ## 71 ################################################################################ 72 73 # Only for dev 74 #use strict; 75 #no strict 'refs'; 76 #use warnings; 77 78 use IO::Handle; 79 use POSIX qw/strftime/; 80 use FileCache maxopen => 55; 81 use Getopt::Long; 82 use sigtrap 'handler' => \&signalHandler, 'normal-signals'; 83 84 my %opt = ( 85 loggingType => 'access', # Default logging type 86 logDir => '/var/log/ispcp' # Script event log directory 87 ); 88 89 Getopt::Long::Configure(qw(no_ignore_case)); 90 GetOptions(\%opt, 'loggingType|t=s', 'logDir|l=s'); 91 92 %main::cfg = (); 93 94 ################################################################################ 95 # Subroutines # 96 ################################################################################ 97 98 99 ################################################################################ 100 # 101 # Load configuration from ispCP configuration file 102 # 103 # @return int 0 on success, negative int otherwise 104 # 105 sub getConf { 106 107 my $file; 108 109 if(-e '/usr/local/etc/ispcp/ispcp.conf'){ 110 $file = '/usr/local/etc/ispcp/ispcp.conf'; 111 } elsif(-e '/etc/ispcp/ispcp.conf') { 112 $file = '/etc/ispcp/ispcp.conf'; 113 } else { 114 return -1; 115 } 116 117 return -1 if ! open F, '<', $file; 118 119 %main::cfg = join('', <F>) =~ /^\s*(\w+)\s*=\s*(.*)$/gm; 120 close F; 121 122 %main::cfg ? 0 : 1; 123 } 124 125 ################################################################################ 126 # Create/update all custom log files 127 # 128 # This subroutine is the handler that is responsible to grab and parse all error 129 # log lines from apache and create the ispCP errors log files from them. 130 # 131 # @return void 132 # 133 sub ErrorLogHandler { 134 135 eventLog( 136 'notice', 'ispCP Apache Logger started ErrorLog Handler -- ' . 137 'resuming normal operations' 138 ); 139 140 while (<STDIN>) { 141 my $vhost = 'default'; 142 143 # Trying to retrieve the virtual host identity 144 $vhost = $1 if / 145 (?:$main::cfg{'APACHE_WWW_DIR'}|$main::cfg{'PHP_STARTER_DIR'}) 146 \/([\w\d.-]+) 147 /x; 148 149 # Error from ispCP Frontend ? 150 $vhost = $main::cfg{'BASE_SERVER_VHOST'} if ($vhost eq 'master'); 151 152 # Write the error log line in the default|domain.tld-error.log 153 my $fh; 154 155 if($vhost eq 'default') { 156 $fh = cacheout '>>', "$main::cfg{'APACHE_LOG_DIR'}/$vhost-error.log"; 157 } else { 158 $fh = cacheout '>>', 159 "$main::cfg{'APACHE_USERS_LOG_DIR'}/$vhost-error.log"; 160 } 161 162 $fh->autoflush(1); 163 print $fh $_; 164 } 165 166 # Occurs when apache breaks the communication for the reason exposed here: 167 # https://issues.apache.org/bugzilla/show_bug.cgi?id=49800 168 signalHandler('eol'); 169 } 170 171 ################################################################################ 172 # Create/update all custom log files 173 # 174 # This subroutine is the handler that is responsible to grab and parse all 175 # custom log lines from apache and create the ispCP custom logfiles from them. 176 # 177 # @return void 178 # 179 sub CustomLoghandler { 180 181 eventLog( 182 'notice', 'ispCP Apache Logger started CustomLog Handler -- ' . 183 'resuming normal operations' 184 ); 185 186 while (<STDIN>) { 187 my ($vhost, $inBytes, $outBytes) = /^(\S+|\s) .* (\d+) (\d+)$/; 188 189 # Normalize the virtual host name to all lowercase. If it's blank, the 190 # request was handled by the default server, so supply a default name. 191 # This shouldn't happen, but caution rocks. 192 $vhost = lc ($vhost) || 'default'; 193 194 # If the vhost contains a '/' or '\', it is illegal so just use the 195 # default log to avoid any security issues due if it is interpreted as a 196 # directory separator. 197 $vhost = 'default' if $vhost =~ m@[/\\]@; 198 199 # Back the log line to the NCSA extended/combined log format by stripped 200 # the %v %I and %O variables. 201 # Note: That allows to not define a custom log format in Awstats 202 # configuration template. 203 s/^\S*\s+(.*)\s\d+\s\d+$/$1/; 204 205 # Write the log line in the domain.tld-combined.log file 206 my $fh = cacheout '>>', 207 "$main::cfg{'APACHE_LOG_DIR'}/$vhost-combined.log"; 208 $fh->autoflush(1); 209 print $fh $_; 210 211 # Write the traffic value in the default|domain.tld-traf.log file 212 $fh = cacheout '>>', "$main::cfg{'APACHE_LOG_DIR'}/$vhost-traf.log"; 213 $fh->autoflush(1); 214 print $fh $inBytes + $outBytes, "\n"; 215 216 # Write the log line in the default|domain.tld-access.log file 217 $fh = cacheout '>>', 218 "$main::cfg{'APACHE_USERS_LOG_DIR'}/$vhost-access.log"; 219 $fh->autoflush(1); 220 print $fh $_; 221 } 222 } 223 224 ################################################################################ 225 # 226 # Convenience subroutines to log events from this script 227 # 228 sub eventLog { 229 230 ($priority, $message) = @_; 231 232 my $date = strftime "%a %b %e %H:%M:%S %Y", localtime; 233 234 if(-d $opt{'logDir'}) { 235 if($priority ne 'debug' && $priority ne 'info' && $priority ne 'notice') { 236 *STDERR = cacheout '>>', 237 "$opt{'logDir'}/ispcp-apache-logger.stderr"; 238 STDERR->autoflush(1); 239 240 print STDERR "[$date] [$priority] $message\n"; 241 } elsif($priority eq 'debug' && !$main::cfg{'DEBUG'}) { 242 return 0; 243 } else { 244 *STDOUT = cacheout '>>', 245 "$opt{'logDir'}/ispcp-apache-logger.stdout"; 246 STDOUT->autoflush(1); 247 248 print STDOUT "[$date] [$priority] $message\n"; 249 } 250 } else { # Logging on STDERR 251 STDERR->autoflush(1); 252 253 print STDERR "[$date] [$priority] $message\n"; 254 } 255 256 0; 257 } 258 259 ################################################################################ 260 # 261 # This subroutine is the handler that is responsible to handle some signals that 262 # can be received from Apache. This handler ensure that all filehandles are 263 # correctly closed before shutting down. 264 # 265 # This method is also called by the errorLog handler in condition exposed here: 266 # https://issues.apache.org/bugzilla/show_bug.cgi?id=49800 267 # 268 sub signalHandler() { 269 270 my $signal = shift; 271 my $loggingType = ($opt{'loggingType'} eq 'access') ? 'Access' : 'Error'; 272 273 eventLog('debug', "Ending $loggingType log processing..."); 274 275 if($signal eq 'eol') { 276 eventLog('notice', "No more lines received, shutting down (pid $$)"); 277 } else { 278 eventLog('notice', "Caught SIG$signal, shutting down (pid $$)"); 279 } 280 281 # Close all filehandles 282 cacheout_close(); 283 284 exit; 285 } 286 287 ################################################################################ 288 # Main program # 289 ################################################################################ 290 291 eventLog('notice', "Starting ispCP Apache logger (pid $$)"); 292 293 # Get ispCP configuration file 294 if(getConf() != 0) { 295 eventLog( 296 'err', 297 "Unable to load ispCP configuration from the ispcp.conf file!" 298 ); 299 300 } elsif (!-d $main::cfg{'APACHE_LOG_DIR'}) { 301 eventLog( 302 'err', 303 "Apache $main::cfg{'APACHE_LOG_DIR'} directory does not exist!" 304 ); 305 306 } elsif (!-d $main::cfg{'APACHE_USERS_LOG_DIR'}) { 307 eventLog( 308 'err', 309 "Apache $main::cfg{'APACHE_USERS_LOG_DIR'} directory does not exist!" 310 ); 311 312 } elsif ($opt{'loggingType'} eq 'access') { 313 # Stating Apache access log processing 314 CustomLoghandler(); 69 315 } else { 70 checkFileSize("$main::cfg{'LOG_DIR'}/ispcp-apache-logger.stderr"); 71 checkFileSize("$main::cfg{'LOG_DIR'}/ispcp-apache-logger.stdout"); 72 73 my $res = open (STDERR, ">>", "$main::cfg{'LOG_DIR'}/ispcp-apache-logger.stderr"); 74 if (!defined($res)) { 75 print STDERR "[ispcp-apache-logger] Can't redirect STDERR\n"; 76 } else { 77 STDERR->autoflush(1); 78 } 79 $res = open (STDOUT, ">>", "$main::cfg{'LOG_DIR'}/ispcp-apache-logger.stdout"); 80 if (!defined($res)) { 81 print STDERR "[ispcp-apache-logger] Can't redirect STDOUT\n"; 82 } else { 83 STDOUT->autoflush(1); 84 } 85 } 86 87 if ( !-d $main::cfg{'APACHE_LOG_DIR'}) { 88 print STDERR "[ispcp-apache-logger] target directory ".$main::cfg{'APACHE_LOG_DIR'}." does not exist!!!\n"; 89 } elsif ( !-d $main::cfg{'APACHE_USERS_LOG_DIR'}) { 90 print STDERR "[ispcp-apache-logger] target directory ".$main::cfg{'APACHE_USERS_LOG_DIR'}." does not exist!!!.\n"; 91 } elsif ( $OPTS{'e'} ) { 92 ErrorLogs(); 93 } else { 94 DefaultLogs(); 95 } 96 97 while ( my $log_line = <STDIN> ){} 98 99 sub ErrorLogs(){ 100 if (defined($main::engine_debug)){ 101 print STDOUT "[ispcp-apache-logger] Starting error log proccessing...\n"; 102 } 103 while ( my $log_line = <STDIN> ) { 104 my $vhost = 'default'; 105 106 if($log_line =~ m/($main::cfg{'APACHE_WWW_DIR'})/){ 107 ($vhost) = $log_line =~ m/$main::cfg{'APACHE_WWW_DIR'}\/([a-zA-Z0-9_\-\.]*)/; 108 } else { 109 if($log_line =~ m/($main::cfg{'PHP_STARTER_DIR'})/){ 110 ($vhost) = $log_line =~ m/$main::cfg{'PHP_STARTER_DIR'}\/([a-zA-Z0-9_\-\.]*)/; 111 } 112 } 113 114 if ( $vhost eq 'master' ){ 115 $vhost='default'; 116 } 117 118 my $force_open=0; 119 if ( !$error_logs{$vhost} ){ 120 $force_open=1; 121 if ( (keys(%timestamps)+1) > $MAXFILES ) { 122 my ( $key, $value ) = sort { $timestamps{$a} <=> $timestamps{$b} } ( keys(%timestamps) ); 123 close $error_logs{$key}; 124 delete $error_logs{$key}; 125 delete $timestamps{$key}; 126 } 127 } 128 129 checkFileExists($vhost, \%error_logs, $main::cfg{'APACHE_USERS_LOG_DIR'},"-error.log",$force_open); 130 print { $error_logs{$vhost} } $log_line; 131 } 132 } 133 134 sub DefaultLogs(){ 135 if (defined($main::engine_debug)){ 136 print STDOUT "[ispcp-apache-logger] Starting default log proccessing...\n"; 137 } 138 139 while (my $log_line = <STDIN>) { 140 my ($vhost, $size, $in, $out) = 141 $log_line =~ m/^(\S+) (\d+|-) (\d+|-) (\d+|-)$/s; 142 143 if(!defined($vhost) || !defined($size)){ 144 print STDERR "[ispcp-apache-logger] Trouble line:\n\t$log_line\n"; 145 } 146 147 $vhost = lc($vhost) || "default"; 148 if ( $vhost =~ m#[/\\]# ) { $vhost = "default" } 149 $vhost =~ /(.*)/o; 150 $vhost = $1; 151 $vhost = 'default' unless $vhost; 152 153 my $force_open = 0; 154 if (!$combined_logs{$vhost} || !$traff_logs{$vhost} || !$access_logs{$vhost}){ 155 $force_open = 1; 156 if ((keys(%timestamps)+1) > $MAXFILES) { 157 my ($key, $value) = sort { $timestamps{$a} <=> $timestamps{$b} } (keys(%timestamps)); 158 if(-e "$main::cfg{'APACHE_LOG_DIR'}/$key-combined.log" && defined($combined_logs{$key})){ 159 close $combined_logs{$key}; 160 } 161 if(-e "$main::cfg{'APACHE_LOG_DIR'}/$key-traf.log" && defined($traff_logs{$key})){ 162 close $traff_logs{$key}; 163 } 164 if(-e "$main::cfg{'APACHE_USERS_LOG_DIR'}/$key-access.log" && defined($access_logs{$key})){ 165 close $access_logs{$key}; 166 } 167 delete $combined_logs{$key}; 168 delete $traff_logs{$key}; 169 delete $access_logs{$key}; 170 delete $timestamps{$key}; 171 } 172 } 173 174 checkFileExists($vhost, \%combined_logs, $main::cfg{'APACHE_LOG_DIR'},"-combined.log",$force_open); 175 checkFileExists($vhost, \%traff_logs, $main::cfg{'APACHE_LOG_DIR'},"-traf.log",$force_open); 176 checkFileExists($vhost, \%access_logs, $main::cfg{'APACHE_USERS_LOG_DIR'},"-access.log",$force_open); 177 178 print { $combined_logs{$vhost} } $log_line; 179 print { $access_logs{$vhost} } $log_line; 180 if ($size ne '-' && $size != 0){ 181 print { $traff_logs{$vhost} } "$size\n"; 182 } 183 } 184 } 185 186 exit; 187 188 sub checkFileSize(){ 189 my ($file)=@_; 190 if( -e $file && ((my $filesize = -s $file) > $MAXLOGFILESIZE)) { 191 unlink($file); 192 } 193 } 194 195 sub checkFileExists(){ 196 my ($local_vhost, $hash, $path, $postpend,$force)=@_; 197 if (!(-e "$path/$local_vhost$postpend") || $force eq '1'){ 198 my $res = open ($hash->{$local_vhost}, ">>", "$path/$local_vhost$postpend"); 199 if (!defined($res)) { 200 print STDERR "[ispcp-apache-logger] Can't open $path/$local_vhost$postpend\n"; 201 } else { 202 $hash->{$local_vhost}->autoflush(1); 203 $timestamps{$local_vhost}=time(); 204 } 205 } 206 } 207 208 sub exitall { 209 if ( $OPTS{'e'} ) { 210 foreach my $key ( keys %error_logs ) { 211 close $key; 212 } 213 if (defined($main::engine_debug)){ 214 print STDOUT "[ispcp-apache-logger] Ending error log proccessing...\n"; 215 } 216 } else { 217 foreach my $key ( keys %combined_logs ) { 218 close $key; 219 } 220 %combined_logs = (); 221 foreach my $key ( keys %traff_logs ) { 222 close $key; 223 } 224 %traff_logs = (); 225 foreach my $key ( keys %access_logs ) { 226 close $key; 227 } 228 %access_logs = (); 229 if (defined($main::engine_debug)){ 230 print STDOUT "[ispcp-apache-logger] Ending default log proccessing...\n"; 231 } 232 } 233 } 316 # Stating Apache error log processing 317 ErrorLogHandler(); 318 } 319 320 # This is the rescue part of this script that is run when an error occurs. 321 # This allows to not kill the current process and prevents Apache to trying to 322 # re-spawn the process again and again. Instead, the script will acts as 323 # /dev/null. It's really better to act like this to avoid too much processor 324 # resources consumption. But the side effect of this is that the administrator 325 # will not be able to see the problem unless if he reads the log file. 326 327 eventLog( 328 'notice', "Acts as /dev/null to avoid too many re-spawn attempts by " . 329 "Apache (pid $$)" 330 ); 331 332 while(<STDIN>) {} 333 334 __END__
Note: See TracChangeset
for help on using the changeset viewer.
