自分のドメインの管理を自分のDNSサーバで行っているため、バーストをくらってないかとか、管理外ドメインに対する問い合わせが来ているかいないか等をちょくちょく確認したくなります。(自分用にしか使っていないドメインのため、ほぼクエリは来ていないに等しいですが…)
flunetd+可視化ツール等が流行っていて、それ用に設定してみてもよいのですが、CLIでサーバにログインしない日は無いため、CLI上のツールで見れたらいいと思い、Perlの勉強がてらツールを作成してみました。
https://github.com/kuredev/tools
以下の情報を調べてみます。
出力結果イメージ
1日のクエリ数 2014-8-06: 60 queries/day 2014-8-07: 46 queries/day 2014-8-05: 40 queries/day 2014-8-08: 61 queries/day 2014-8-13: 19 queries/day 2014-8-12: 94 queries/day 2014-8-11: 43 queries/day 2014-8-10: 47 queries/day 2014-8-09: 45 queries/day 2014-7-31: 60 queries/day 2014-8-01: 91 queries/day 2014-8-03: 39 queries/day 2014-8-04: 97 queries/day 2014-7-30: 60 queries/day 2014-8-02: 50 queries/day ------1日の最高qps------ 2014-8-06(23:34:48): 1 qps 2014-8-05(09:40:47): 1 qps 2014-8-07(05:24:17): 1 qps 2014-8-08(22:06:05): 6 qps 2014-8-13(09:15:17): 2 qps 2014-8-12(11:11:32): 1 qps 2014-8-11(09:31:40): 2 qps 2014-8-10(16:48:41): 1 qps 2014-8-03(08:03:25): 1 qps 2014-8-01(11:17:53): 1 qps 2014-7-31(21:04:18): 1 qps 2014-8-09(09:40:50): 4 qps 2014-8-04(22:24:34): 1 qps 2014-8-02(01:04:39): 1 qps 2014-7-30(23:45:13): 1 qps -----1日の最多問い合わせドメイン数とクエリ数------ 2014-8-06(kuredev.info): 38 queris/day 2014-8-05(kuredev.info): 19 queris/day 2014-8-07(kuredev.info): 28 queris/day 2014-8-08(kuredev.info): 31 queris/day 2014-8-13(kuredev.info): 6 queris/day 2014-8-10(kuredev.info): 17 queris/day
前提
BINDのバージョン
# rpm -qa | grep bind bind-9.8.2-0.17.rc1.el6_4.4.x86_64
ログフォーマット
30-Jul-2014 12:43:51.913 queries: client 1.2.3.4#58334: query: hogehoge.com IN A + (1.2.3.4)
モジュールの作成
BINDログ解析用自作モジュール
package BindParse; use strict; use warnings; use Switch; sub new{ my $class = shift; my @self = @_; return bless \@self, $class; } sub parse{ my ($self, $file) = @_; open my $fh, '<', $file or die "canoot"; my $queries = []; while(my $line = <$fh>){ my @param = split /\s+/, $line; my @param_ip = split /#/, $param[4]; my $q_ip = $param_ip[0]; my $q_port = $param_ip[1]; my @time = split /\./, $param[1]; my %query = ( date => $param[0], time => $param[1], time_s => $time[0], q_ip => $q_ip, q_port => $q_port, q_domain => $param[6], q_type => $param[8]); push @$queries, \%query; } close $fh; return $queries; } sub convertDate{ my ($self, $str) = @_; my @s = split /-/, $str; my $result = $s[2].""; switch ($s[1]){ case 'Jan' { $result .= '-1-' } case 'Feb' { $result .= '-2-' } case "Mar" { $result .= '-3-' } case 'Apr' { $result .= '-4-' } case 'May' { $result .= '-5-' } case 'Jun' { $result .= '-6-' } case 'Jul' { $result .= '-7-' } case "Aug" { $result .= "-8-" } else {} } $result .= $s[0]; return $result; } 1;
parseメソッドは上述のログフォーマットを以下のようにPerlのデータ構造にパースします。
[ { 'q_domain' => 'hoge.info', 'q_type' => 'A', 'time' => '12:43:51.913', 'date' => '30-Jul-2014', 'q_port' => '58334:', 'q_ip' => '1.2.3.4', 'time_s' => '12:43:51' }, { 'q_domain' => 'hoge.info', 'q_type' => 'A', 'time' => '14:30:58.053', 'date' => '30-Jul-2014', 'q_port' => '56091:', 'q_ip' => '1.2.3.4', 'time_s' => '14:30:58' }, ]
また、convertDateメソッドは日付を人間が見やすい形に変換します。
解析スクリプト
取得する情報別にfor文を回しているので計算量が多いですが、まぁ取得したい情報は上記の4つ以外にも場合によって違うものが出てくると思いますので、特に気にせず書いています。
#!/usr/bin/perl use strict; use warnings; use BindParse; use Data::Dumper; my $file = shift; my $bind = BindParse->new(); my $result = $bind->parse("query.log"); #1日のクエリ数 #1日の最高qps #1日の最多問い合わせドメイン名とクエリ数 #1日の最多問い合わせIPアドレスとIPアドレス ############## #1日のクエリ数 print("1日のクエリ数\n"); my %dayps = (); foreach my $query (@{$result}){ $dayps{$query->{date}}++; } #出力 foreach my $date (keys %dayps){ print $bind->convertDate($date).": ".$dayps{$date}." queries/day\n"; } ############## #1日の最高qps my %qps_info = (); print("------1日の最高qps------\n"); foreach my $query (@{$result}){ $qps_info{$query->{date}}{$query->{time_s}}++; } my %max = (); foreach my $date (keys %qps_info){ my $dateHash_ref = $qps_info{$date}; my %dateHash = %{$dateHash_ref}; $max{$date}->{'num'} = 0; foreach my $s (keys %dateHash){ if($max{$date}->{'num'} < $dateHash{$s}){ $max{$date}->{'time_s'} = $s; $max{$date}->{'qps'} = $dateHash{$s}; } } } #出力 foreach my $date (keys %max){ print $bind->convertDate($date)."(".$max{$date}->{'time_s'}."): ".$max{$date}->{'qps'}." qps\n"; } ###################################### #Number of queries of domain print("-----1日の最多問い合わせドメイン数とクエリ数------\n"); %qps_info = (); foreach my $query (@{$result}){ $qps_info{$query->{date}}{$query->{q_domain}}++; } %max = (); my %domain_result = (); foreach my $date (keys %qps_info){ my $dateHash_ref = $qps_info{$date}; my %dateHash = %{$dateHash_ref}; $max{$date}->{'num'} = 0; foreach my $s (keys %dateHash){ if($max{$date}->{'num'} < $dateHash{$s}){ $max{$date}->{'domain'} = $s; $max{$date}->{'num'} = $dateHash{$s}; } } } #出力 foreach my $date (keys %max){ print $bind->convertDate($date)."(".$max{$date}->{'domain'}."): ".$max{$date}->{'num'}." queris/day\n"; } ################################### #IP Address Analysis #parse to Perl Data Structure print("-----ip info ----\n"); %qps_info = (); foreach my $query (@{$result}){ $qps_info{$query->{date}}{$query->{q_ip}}++; } #出力 print("---detail\n"); my $ip_ref = {}; my %ip_hash = (); foreach my $date (keys %qps_info){ print $bind->convertDate($date)."\n"; %ip_hash = %{$qps_info{$date}}; foreach my $ip (keys %ip_hash){ print " ".$ip.": ".$ip_hash{$ip}." queries\n"; } }
参考:
Apacheのログをパースしてみる - はこべブログ ♨ http://hakobe932.hatenablog.com/entry/20080316/1205659704
もっと自在にサーバを使い倒す 業務に役立つPerl (Software Design plus)
posted with amazlet at 14.08.16