Projet

Général

Profil

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

root / plugins / apache / apache_vhosts / apache_logparser @ 17f78427

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

1
#!/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
=cut
31

    
32
# config
33
my $dir = "/logs/apache_logs";
34
my $files = "*access_log";
35
my $site = "(.*)-access_log";
36
my $statefile = "/tmp/logstate";
37
`touch $statefile` unless (-f $statefile);
38
local $nsec=7;
39
local $debug=0;
40

    
41
my $scan_interval=5; # minutes
42

    
43
# perl modules
44
# "File:Tail:Multi" disappeared from CPAN (somewhen in 2016) - thus it needs
45
# to be imported carefully (for travis checks).
46
eval 'use File::Tail::Multi; 1;' or die 'Please install File::Tail::Multi';
47
use Storable qw(freeze thaw);
48
use List::Util qw(min max);
49
use IPC::ShareLite ':lock';
50
require Data::Dumper if $debug;
51
use Munin::Plugin;
52

    
53
# shared mem
54
local $share = IPC::ShareLite->new(
55
	-key     => 'mapl',
56
	-create  => 1,
57
	-destroy => 1,
58
	-exclusive => 0,
59
	-mode => '0666'
60
) or die $!;
61

    
62
# drop stored data on reload
63
$share->store( freeze {} );
64

    
65
# tail log files
66
my $tail=File::Tail::Multi->new (
67
  Files=>["$dir/$files"],
68
  ScanForFiles=>$scan_interval,
69
  Debug=>0,
70
  LastRun_File => $statefile,
71
  RemoveDuplicate=>0,
72
  NumLines=>0,
73
  OutputPrefix=>"f"
74
);
75

    
76
# read to current position
77
$tail->read;
78

    
79
# register counting function
80
$tail->Function(\&count);
81

    
82
local $temp;
83
my ($file,$ip,$logname,$user,$rtime,$method,$request,$protocol,$status,$bytes,$referer,$useragent,$time);
84
sub count {
85
        foreach $_ (@{shift()})  {
86
        if ((()=/"/g)==2) {
87
          # common with filename prefix, optionally add time and vhost at the end
88
          ($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;
89
        }
90
        elsif ((()=/"/g)==6) {
91
          # combined with filename prefix, optionally add time and vhost at the end
92
          ($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;
93
        };
94

    
95
	#find sitename
96
	$file=~s/$site/$1/;
97
	$file=$vhost if $vhost;
98

    
99
	# skip broken lines
100
	next unless $file;
101

    
102
	# sitename to munin fieldname
103
	my $vpm=clean_fieldname("$file");
104
	$temp{$vpm}{'label'}="$file";
105
	$temp{$vpm}{'label'}=~s/www\.//;
106

    
107
	# count all requests
108
	$temp{$vpm}{'requests'}++;
109

    
110
	if ($bytes) {
111
	 $bytes=~s/-/0/;
112
 	 # bytes transmitted
113
	 $temp{$vpm}{'bytes'}+=$bytes;
114

    
115
	 # max bytes
116
	 $temp{$vpm}{'max_bytes'}=max($temp{$vpm}{'max_bytes'},$bytes) || 0;
117

    
118
         # average bytes
119
         $temp{$vpm}{'avg_bytes'}=$temp{$vpm}{'bytes'}/$temp{$vpm}{'requests'} || 0;
120
	}
121

    
122
	# count by status / error code
123
	$temp{$vpm}{"status"}{$status}++ if $status;
124

    
125
	if ($time) {
126
	  # microsec to millisec
127
	  $time=sprintf("%d",$time/1000);
128

    
129
  	  # min/max execution time
130
  	  $temp{$vpm}{'max_time'}=max($temp{$vpm}{'max_time'},$time) || 0;
131

    
132
  	  # cumulative execution time
133
  	  $temp{$vpm}{'time'}+=$time;
134

    
135
          # average time
136
          $temp{$vpm}{'avg_time'}=$temp{$vpm}{'time'}/$temp{$vpm}{'requests'} || 0;
137
        }
138

    
139
        };
140
};
141

    
142

    
143
while (1) {
144
        # tail files, calls &count with linearray
145
        $tail->read;
146

    
147
        # begin transaction
148
        $share->lock(LOCK_EX);
149

    
150
        # get data (may be updated by other loggers too)
151
        my %old=eval{%{thaw($share->fetch)}};              # using eval to suppress thaw error on empty string at the first run
152

    
153
        foreach my $vpm (keys %temp){
154
                # merge values
155
                $old{$vpm}{'label'}=$temp{$vpm}{'label'};
156
                $old{$vpm}{'bytes'}+=$temp{$vpm}{'bytes'} if $temp{$vpm}{'bytes'};
157
                $old{$vpm}{'requests'}+=$temp{$vpm}{'requests'} if $temp{$vpm}{'requests'};
158
                $old{$vpm}{'time'}+=$temp{$vpm}{'time'} if $temp{$vpm}{'time'};
159
                # avoid div by zero
160
                my $div=($old{$vpm}{'requests'} <1)?1:$old{$vpm}{'requests'};
161
                # recalc average on merged data for multiple datasources, use local average after purge/restart
162
                $old{$vpm}{'avg_time'}=($old{$vpm}{'avg_time'}>0)?sprintf("%d",($old{$vpm}{'time'}+$temp{$vpm}{'time'})/$div):sprintf("%d",$temp{$vpm}{'avg_time'});
163
                $old{$vpm}{'avg_bytes'}=($old{$vpm}{'avg_bytes'}>0)?sprintf("%d",($old{$vpm}{'bytes'}+$temp{$vpm}{'bytes'})/$div):sprintf("%d",$temp{$vpm}{'avg_bytes'});
164
                $old{$vpm}{'max_time'}=max($old{$vpm}{'max_time'},$temp{$vpm}{'max_time'}) || 0;
165
                $old{$vpm}{'max_bytes'}=max($old{$vpm}{'max_bytes'},$temp{$vpm}{'max_bytes'}) || 0;
166

    
167
                # reset local counters
168
                foreach my $check (qw(requests bytes time max_bytes avg_bytes max_time avg_time)) {
169
                        $temp{$vpm}{$check}=0;
170
                }
171

    
172
                # reset status counts
173
                foreach my $val (keys %{$temp{$vpm}{'status'}}) {
174
                        $old{$vpm}{'status'}{$val}+=$temp{$vpm}{'status'}{$val};
175
                        $temp{$vpm}{'status'}{$val}=0;
176
                }
177

    
178
        };
179

    
180
        # save to shm
181
        print Data::Dumper::Dumper(%old) if $debug;
182
        $share->store( freeze \%old );
183
        # end transaction
184
        $share->unlock;
185

    
186
        # parse/write every n seconds (plus processing time)
187
        sleep $nsec;
188
}