Skip to content

Instantly share code, notes, and snippets.

@kuronekomichael
Created May 25, 2016 17:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kuronekomichael/2dc265adc0a73593d3f3714766880fd6 to your computer and use it in GitHub Desktop.
Save kuronekomichael/2dc265adc0a73593d3f3714766880fd6 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl -w
use strict;
use warnings;
use Time::Local 'timelocal';
$| = 1;
my $config = {
# ログの検索条件(ユーザIDを指定することになるはず)
criterion => undef,
# 探索するApacheログの格納パス
logs => '~/logs',
# ここで指定した秒数を超えてアクセスがない場合は、サービスから離れたとみなす
threshold_sec => 300, # デフォルト5分(300sec)
};
my $MONTH = {Jan => 0, Feb => 1, Mar => 2, Apr => 3, May => 4, Jun => 5, Jul => 6, Aug => 7, Sep => 8, Oct => 9, Nov => 10, Dec => 11};
die "usage: perl ./who_is_unsleeper.pl --target=<target-viewer-id>\nexample)117965337" unless ($ARGV[0]);
$config->{criterion} = $ARGV[0];
main();
sub main {
my $stat = {
criterion => $config->{criterion},
date_logs => {},
intervals => []
};
my @files = list_apache_accesslog_files($config->{logs});
# my $file = pop @files;
# check_logfile($stat, $file);
foreach my $file (@files) {
check_logfile($stat, $file);
}
output_report($stat);
}
sub check_logfile {
my $stat = shift or die;
my $file = shift or die;
print "Reading... '$file' \n";
if ($file =~ m/\.gz$/) {
check_gz_logfile($stat, $file);
} elsif ($file =~ m/\.log$/) {
check_txt_logfile($stat, $file);
} else {
die "Unknown logfile format: $file";
}
}
sub check_gz_logfile {
my $stat = shift or die;
my $file = shift or die;
open my $fh, "zcat $file 2>/dev/null |" or die "Cannot zcat '$file' for reading: $!";
while (<$fh>) {
next unless (m/$stat->{criterion}/);
# ログが残された日時を取得
die unless (m/(\d{2})\/([A-Z][a-z]{2})\/(\d{4}):(\d{2}):(\d{2}):(\d{2})\s\+\d{4}/);
my ($sec, $min, $hour, $mday, $month, $year) = (int($6), int($5), int($4), int($1), $MONTH->{$2}, int($3));
my $time = timelocal($sec, $min, $hour, $mday, $month, $year - 1900);
my $date = sprintf("%04d-%02d-%02d", $year, $month + 1, $mday);
# ログ日付の統計情報がなければ、この時点で作成する
unless (exists $stat->{'date_logs'}->{$date}) {
$stat->{'date_logs'}->{$date} = {
access_count => 0,
access_list => [],
lasttime => 0,
};
foreach (0..23) {
push @{$stat->{'date_logs'}->{$date}->{access_list}}, { access_count => 0, interval_sec => [] };
}
}
# 前日の日付を取得
my $prev_time = timelocal(0, 0, 0, $mday, $month, $year - 1900) - (24 * 60 * 60);
my ($prev_day, $prev_month, $prev_year) = (localtime($prev_time))[3..5];
my $lastdate = sprintf("%04d-%02d-%02d", $prev_year + 1900, $prev_month + 1, $prev_day);
# 当日のアクセスログがある場合→それと比較
# 当日のアクセスログがない場合→前日分をみて、あれば比較
# アクセス時間差を取る
my $lasttime = undef;
if ($stat->{'date_logs'}->{$date}->{'lasttime'} > 0) {
$lasttime = $stat->{'date_logs'}->{$date}->{'lasttime'};
} elsif (exists $stat->{'date_logs'}->{$lastdate} && $stat->{'date_logs'}->{$lastdate}->{'lasttime'} > 0) {
$lasttime = $stat->{'date_logs'}->{$lastdate}->{'lasttime'};
}
if ($lasttime) {
my $interval_sec = $time - $lasttime;
if ($interval_sec > $config->{threshold_sec}) {
# 休息をカウント
push @{$stat->{'date_logs'}->{$date}->{access_list}[$hour]->{interval_sec}}, $interval_sec;
push @{$stat->{intervals}}, {
rest_start => $lasttime,
rest_end => $time
};
}
}
#TODO: アクセス数をカウント
$stat->{'date_logs'}->{$date}->{access_count}++;
$stat->{'date_logs'}->{$date}->{access_list}[$hour]->{access_count}++;
if ($stat->{'date_logs'}->{$date}->{'lasttime'} < $time) {
$stat->{'date_logs'}->{$date}->{'lasttime'} = $time;
}
#print $_;
}
close $fh;
}
# 指定したディレクトリ配下のapacheログファイル名をリストアップして返す
sub list_apache_accesslog_files {
my $dir = shift or die;
my @files = ();
opendir DIR, $dir or die "Cannot open '$dir' accesslog dir: $!";
foreach (readdir(DIR)) {
next unless (m/apache\.(?:sp\.)?accesslog\.\d{8}\.log\.gz/);
push @files, $config->{logs}.'/'.$_;
}
closedir DIR;
return sort {
my $a_date = $1 if ($a =~ m/(\d{8})/);
my $b_date = $1 if ($b =~ m/(\d{8})/);
return 1 if ($a_date > $b_date);
return -1 if ($a_date < $b_date);
return 0;
} @files;
}
# Apacheログの日付形式をエポック秒に変換して返す
# 例) '09/May/2016:00:00:02 +0900' => 1462719602
sub str_to_epochtime {
die unless ($_[0] =~ m/(\d{2})\/([A-Z][a-z]{2})\/(\d{4}):(\d{2}):(\d{2}):(\d{2})\s\+\d{4}/);
return timelocal(int($6), int($5), int($4), int($1), $MONTH->{$2}, (int($3) - 1900));
}
sub epochtime_to_str {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime($_[0]);
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec);
}
#sub get_yesterday {
# my $date = shift or die;
# die unless ($date =~ m/^(\d{4})(\d{2})(\d{2})$/);
# my $time = timelocal(0, 0, 0, $3, $2 - 1, $1 - 1900) - (24 * 60 * 60);
# my ($day, $month, $year) = (localtime($time))[3..5];
# return sprintf("%04d%02d%02d", $year + 1900, $month + 1, $day);
#}
sub output_report {
my $stat = shift or die;
my $date_logs = $stat->{date_logs};
foreach my $date (sort keys %$date_logs) {
print "[$date]\n";
print "\tアクセス数: $date_logs->{$date}->{access_count}\n";
print "\t最終アクセス: ".epochtime_to_str($date_logs->{$date}->{lasttime})."\n";
foreach my $hour (0..23) {
print "\t\t".sprintf("%02d", $hour).": ".sprintf("%4d", $date_logs->{$date}->{access_list}[$hour]->{access_count});
#print ", 5分以上の休憩:".($#{$date_logs->{$date}->{access_list}[$hour]->{interval_sec}} + 1);
print "\n";
}
}
print "\n";
print "[休憩時間]\n";
foreach my $interval (@{$stat->{intervals}}) {
my $diff = ($interval->{rest_end} - $interval->{rest_start});
my $hour = int($diff / 60 / 60);
$diff -= ($hour * 60 * 60);
my $min = int($diff / 60);
my $sec = $diff - ($min * 60);
print sprintf("\t%d時間%02d分%02d秒", $hour, $min, $sec), "\t(", epochtime_to_str($interval->{rest_start})." 〜 ".epochtime_to_str($interval->{rest_end}), ")\n";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment