Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / apache / apache_vhosts / apache_logparser @ 942bda31

Historique | Voir | Annoter | Télécharger (6,19 ko)

1 942bda31 Kenyon Ralph
#!/usr/bin/perl
2
3
=head1 README
4
5
This is a logfile watcher for apache, it monitors a log dir for access logs and saves some stats to shared memory.
6
Munin can then fetch and reset the stats periodically.
7
8
Just start it once, it runs as a daemon and polls logs every n sec keeping track of changes to the logs.
9
Filelist is read on startup and on defined scan_intervals. File position is recorded and logs are checked for truncate/delete (for performance reasons).
10
11
Requires perl modules File::Tail::Multi Storable IPC::ShareLite Munin::Plugin (optional Data::Dumper)
12
13
You can use it in parallel to the pipelogger if that suits you better, the stats are merged in shared mem.
14
Both ways should show decent performance, the pipelogger works in RAM only, but writes no logs.
15
16
17
=head1 INSTALLATION
18
19
Install to /usr/share/munin and run it as root
20
21
configure the variables below:
22
23
$dir		path to your logfiles
24
$files		file-glob to find access logs
25
$site		regexp to find sitename from logfile name
26
$statefile	file to save last log position for tail
27
$nsec		tail and write to shared mem every n seconds
28
$debug		dump tallied data every n seconds, print every log line parsed
29
$scan_interval	rescan for new log files every n minutes
30
$type		log file type:
31
                common: CLF + vhost + time + (other fields)
32
                combined: combined + time + (other fields)
33
=cut
34
35
# config
36
my $dir = "/logs/apache_logs";
37
my $files = "*access_log";
38
my $site = "(.*)-access_log";
39
my $statefile = "/tmp/logstate";
40
`touch $statefile` unless (-f $statefile);
41
local $type="combined";
42
local $nsec=7;
43
local $debug=0;
44
45
my $scan_interval=5; # minutes
46
47
# perl modules
48
use File::Tail::Multi;
49
use Storable qw(freeze thaw);
50
use List::Util qw(min max);
51
use IPC::ShareLite ':lock';
52
require Data::Dumper if $debug;
53
use Munin::Plugin;
54
55
# shared mem
56
local $share = IPC::ShareLite->new(
57
	-key     => 'mapl',
58
	-create  => 1,
59
	-destroy => 1,
60
	-exclusive => 0,
61
	-mode => '0666'
62
) or die $!;
63
64
# drop stored data on reload
65
$share->store( freeze {} );
66
67
# tail log files
68
my $tail=File::Tail::Multi->new (
69
  Files=>["$dir/$files"],
70
  ScanForFiles=>$scan_interval,
71
  Debug=>0,
72
  LastRun_File => $statefile,
73
  RemoveDuplicate=>0,
74
  NumLines=>0,
75
  OutputPrefix=>"f"
76
);
77
78
# read to current position
79
$tail->read;
80
81
# register counting function
82
$tail->Function(\&count);
83
84
local $temp;
85
my ($file,$ip,$logname,$user,$rtime,$method,$request,$protocol,$status,$bytes,$referer,$useragent,$time);
86
sub count {
87
        foreach $_ (@{shift()})  {
88
        if ((()=/"/g)==2) {
89
          # common with filename prefix, optionally add time and vhost at the end
90
          ($file,$ip,$logname,$user,$rtime,$method,$request,$protocol,$status,$bytes,$time,$vhost)=/^(.*?)\s:\s(.*?)\s(.*?)\s(.*?)\s\[(.*?)\]\s"(.*)\s(.*?)\s(.*?)"\s(\d*)\s(\S*)\s?(\S*)\s?(\S*?)$/o;
91
        }
92
        elsif ((()=/"/g)==6) {
93
          # combined with filename prefix, optionally add time and vhost at the end
94
          ($file,$ip,$logname,$user,$rtime,$method,$request,$protocol,$status,$bytes,$referer,$useragent,$time,$vhost)=/^(.*?)\s:\s(.*?)\s(.*?)\s(.*?)\s\[(.*?)\]\s"(.*)\s(.*?)\s(.*?)"\s(\d*?)\s(.*?)\s"(.*?)"\s"(.*?)"\s?(\S*)\s?(\S*)$/o;
95
        };
96
97
	#find sitename
98
	$file=~s/$site/$1/;
99
	$file=$vhost if $vhost;
100
	
101
	# skip broken lines
102
	next unless $file;
103
104
	# sitename to munin fieldname
105
	my $vpm=clean_fieldname("$file");
106
	$temp{$vpm}{'label'}="$file";
107
	$temp{$vpm}{'label'}=~s/www\.//;
108
	
109
	# count all requests
110
	$temp{$vpm}{'requests'}++;
111
112
	if ($bytes) {
113
	 $bytes=~s/-/0/;
114
 	 # bytes transmitted
115
	 $temp{$vpm}{'bytes'}+=$bytes;
116
117
	 # max bytes
118
	 $temp{$vpm}{'max_bytes'}=max($temp{$vpm}{'max_bytes'},$bytes) || 0;
119
120
         # average bytes
121
         $temp{$vpm}{'avg_bytes'}=$temp{$vpm}{'bytes'}/$temp{$vpm}{'requests'} || 0;
122
	}
123
	
124
	# count by status / error code
125
	$temp{$vpm}{"status"}{$status}++ if $status;
126
127
	if ($time) {
128
	  # microsec to millisec
129
  	  $time=sprintf("%d",$time/1000); 
130
131
  	  # min/max execution time
132
  	  $temp{$vpm}{'max_time'}=max($temp{$vpm}{'max_time'},$time) || 0;
133
134
  	  # cumulative execution time
135
  	  $temp{$vpm}{'time'}+=$time;
136
137
          # average time
138
          $temp{$vpm}{'avg_time'}=$temp{$vpm}{'time'}/$temp{$vpm}{'requests'} || 0;
139
        }
140
141
        };
142
};
143
144
145
while (1) {
146
        # tail files, calls &count with linearray
147
        $tail->read;
148
149
        # begin transaction                
150
        $share->lock(LOCK_EX);
151
        
152
        # get data (may be updated by other loggers too)
153
        my %old=%{thaw $share->fetch};
154
155
        foreach my $vpm (keys %temp){
156
                # merge values
157
                $old{$vpm}{'label'}=$temp{$vpm}{'label'};
158
                $old{$vpm}{'bytes'}+=$temp{$vpm}{'bytes'} if $temp{$vpm}{'bytes'};
159
                $old{$vpm}{'requests'}+=$temp{$vpm}{'requests'} if $temp{$vpm}{'requests'};
160
                $old{$vpm}{'time'}+=$temp{$vpm}{'time'} if $temp{$vpm}{'time'};
161
                # avoid div by zero
162
                my $div=($old{$vpm}{'requests'} <1)?1:$old{$vpm}{'requests'};
163
                # recalc average on merged data for multiple datasources, use local average after purge/restart
164
                $old{$vpm}{'avg_time'}=($old{$vpm}{'avg_time'}>0)?sprintf("%d",($old{$vpm}{'time'}+$temp{$vpm}{'time'})/$div):sprintf("%d",$temp{$vpm}{'avg_time'});
165
                $old{$vpm}{'avg_bytes'}=($old{$vpm}{'avg_bytes'}>0)?sprintf("%d",($old{$vpm}{'bytes'}+$temp{$vpm}{'bytes'})/$div):sprintf("%d",$temp{$vpm}{'avg_bytes'});
166
                $old{$vpm}{'max_time'}=max($old{$vpm}{'max_time'},$temp{$vpm}{'max_time'}) || 0;
167
                $old{$vpm}{'max_bytes'}=max($old{$vpm}{'max_bytes'},$temp{$vpm}{'max_bytes'}) || 0;
168
169
                # reset local counters
170
                foreach my $check qw(requests bytes time max_bytes avg_bytes max_time avg_time) {
171
                        $temp{$vpm}{$check}=0;
172
                }
173
174
                # reset status counts
175
                foreach my $val (keys %{$temp{$vpm}{'status'}}) {
176
                        $old{$vpm}{'status'}{$val}+=$temp{$vpm}{'status'}{$val};
177
                        $temp{$vpm}{'status'}{$val}=0;
178
                }
179
180
        };
181
182
        # save to shm
183
        print Data::Dumper::Dumper(%old) if $debug;
184
        $share->store( freeze \%old );
185
        # end transaction
186
        $share->unlock;
187
  
188
        # parse/write every n seconds (plus processing time)
189
        sleep $nsec;
190
}