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 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
=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 02f80a35 Lars Kruse
# "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 942bda31 Kenyon Ralph
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 17f78427 Lars Kruse
99 942bda31 Kenyon Ralph
	# 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 17f78427 Lars Kruse
107 942bda31 Kenyon Ralph
	# 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 17f78427 Lars Kruse
122 942bda31 Kenyon Ralph
	# count by status / error code
123
	$temp{$vpm}{"status"}{$status}++ if $status;
124
125
	if ($time) {
126
	  # microsec to millisec
127 17f78427 Lars Kruse
	  $time=sprintf("%d",$time/1000);
128 942bda31 Kenyon Ralph
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 17f78427 Lars Kruse
        # begin transaction
148 942bda31 Kenyon Ralph
        $share->lock(LOCK_EX);
149 17f78427 Lars Kruse
150 942bda31 Kenyon Ralph
        # get data (may be updated by other loggers too)
151 672fd1c3 stimpy23
        my %old=eval{%{thaw($share->fetch)}};              # using eval to suppress thaw error on empty string at the first run
152 942bda31 Kenyon Ralph
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 89b2f767 Lars Kruse
                foreach my $check (qw(requests bytes time max_bytes avg_bytes max_time avg_time)) {
169 942bda31 Kenyon Ralph
                        $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 17f78427 Lars Kruse
186 942bda31 Kenyon Ralph
        # parse/write every n seconds (plus processing time)
187
        sleep $nsec;
188
}