読者です 読者をやめる 読者になる 読者になる

Server Statusの情報収集スクリプト

Apache perl

以前にこんなエントリを書きました。


サーバー運用していると、定期的に情報収集したくなるものです。


ということで、Server Status 情報をCSVファイルにするスクリプトを作ってみました。
動くものができたので、公開してみる事にします。


Perlで書いています。モジュールを沢山使っている訳ではないので、
Perlが動く環境であれば、大体動くかと。。


利用はご自由にどうぞ。

#!/usr/bin/perl
# Author  : think-t
# Blog    : http://d.hatena.ne.jp/think-t

use warnings;
use strict;
use LWP::UserAgent;
use Fcntl;

my $hostname = "127.0.0.1";             # Name of server
my $port     = "80";                    # Port on server
my $address  = "server-status/?auto";   # Server Status Address
my $log_directory = "/var/log";         # Log Directory

my $url = "http://$hostname:$port/$address";

my ( $year, $month, $month_of_day, $hour, $minute ) = &get_current_time ( );
my $yyyyMMdd = sprintf ( "%04d%02d%02d", $year, $month, $month_of_day );
my $log_file = "$log_directory/$yyyyMMdd.server-status.log";

my %server_status = (
    'total_access' => '',
    'total_kbytes' => '',
    'uptime' => '',
    'req_per_sec' => '',
    'bytes_per_sec' => '',
    'bytes_per_req' => '',
    'busyworkers' => '',
    'idleworkers' => '',
    'scoreboard' => '',
);

my %scoreboard_count = (
    '_' => 0,        # Waiting for Connection
    'S' => 0,        # Starting up
    'R' => 0,        # Reading Request
    'W' => 0,        # Sending Reply
    'K' => 0,        # Keepalive(read)
    'D' => 0,        # DNS Lookup
    'C' => 0,        # Closing connection
    'L' => 0,        # Logging
    'G' => 0,        # Gracefully finishing
    'I' => 0,        # Idle cleanup of worker
    '.' => 0,        # Open slot with no current process
);

my $response = &get_server_status ( );

if ( $response -> is_success ){
    my $response_data = $response -> content;
    %server_status = &pick_out_server_status_value ( $response_data, \%server_status );

    %scoreboard_count = &count_scoreboard_count ( $server_status{'scoreboard'}, \%scoreboard_count );

    my $message = &collect_server_status_value ( \%server_status, \%scoreboard_count );

    &write_log ( $log_file, $message ); 
} else {
    my $date = sprintf ( "%04d/%02d/%02d", $year, $month, $month_of_day );
    my $time = sprintf ( "%02d:%02d", $hour, $minute );
    my $message = "$date,$time,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0";

    &write_log ( $log_file, $message );
}

sub get_current_time {
    my ( $second, $minute, $hour, $month_of_day, $month, $year ) = localtime ( );
    $year += 1900;
    $month++;

    return ( $year, $month, $month_of_day, $hour, $minute );
}

sub get_server_status {
    my $agent = LWP::UserAgent -> new;
    my $request = HTTP::Request -> new ( GET => $url );
    my $response = $agent -> request ( $request );
    
    return $response;
}

sub pick_out_server_status_value {
    my ( $response_data, $server_status ) = @_;

    if ( $response_data =~ m|^Total\ Accesses:\ (\S+)|m ) { $server_status{'total_access'} = $1 }; 
    if ( $response_data =~ m|^Total\ kBytes:\ (\S+)|m ) { $server_status{'total_kbytes'} = $1 }; 
    if ( $response_data =~ m|^Uptime:\ (\S+)|m ) { $server_status{'uptime'} = $1 };
    if ( $response_data =~ m|^ReqPerSec:\ (\S+)|m ) { $server_status{'req_per_sec'} = $1 };
    if ( $response_data =~ m|^BytesPerSec:\ (\S+)|m ) { $server_status{'bytes_per_sec'} = $1 };
    if ( $response_data =~ m|^BytesPerReq:\ (\S+)|m ) { $server_status{'bytes_per_req'} = $1 };
    if ( $response_data =~ m|^BusyWorkers:\ (\S+)|m ) { $server_status{'busyworkers'} = $1 };
    if ( $response_data =~ m|^IdleWorkers:\ (\S+)|m ) { $server_status{'idleworkers'} = $1 };
    if ( $response_data =~ m|^Scoreboard:\ (\S+)|m ) { $server_status{'scoreboard'} = $1 };

    return %server_status;
}

sub count_scoreboard_count {
    my ( $scoreboard, $scoreboard_count ) = @_;

    $scoreboard_count{'_'} = $scoreboard =~ tr/_/_/;
    $scoreboard_count{'S'} = $scoreboard =~ tr/S/S/;
    $scoreboard_count{'R'} = $scoreboard =~ tr/R/R/;
    $scoreboard_count{'W'} = $scoreboard =~ tr/W/W/;
    $scoreboard_count{'K'} = $scoreboard =~ tr/K/K/;
    $scoreboard_count{'D'} = $scoreboard =~ tr/D/D/;
    $scoreboard_count{'C'} = $scoreboard =~ tr/C/C/;
    $scoreboard_count{'L'} = $scoreboard =~ tr/L/L/;
    $scoreboard_count{'G'} = $scoreboard =~ tr/G/G/;
    $scoreboard_count{'I'} = $scoreboard =~ tr/I/I/;
    $scoreboard_count{'.'} = $scoreboard =~ tr/././;

    return %scoreboard_count;
}

sub collect_server_status_value {
    my ( $server_status, $scoreboard_count ) = @_;
    my ( $year, $month, $month_of_day, $hour, $minute ) = &get_current_time ( );

    my $date = sprintf ( "%04d/%02d/%02d", $year, $month, $month_of_day );
    my $time = sprintf ( "%02d:%02d", $hour, $minute );
    my $message = "$date,$time,$server_status{'total_access'},$server_status{'total_kbytes'},$server_status{'uptime'},"
                  . "$server_status{'req_per_sec'},$server_status{'bytes_per_sec'},$server_status{'bytes_per_req'},"
                  . "$server_status{'busyworkers'},$server_status{'idleworkers'},"
                  . "$scoreboard_count{'_'},$scoreboard_count{'S'},$scoreboard_count{'R'},$scoreboard_count{'W'},"
                  . "$scoreboard_count{'K'},$scoreboard_count{'D'},$scoreboard_count{'C'},$scoreboard_count{'L'},"
                  . "$scoreboard_count{'G'},$scoreboard_count{'I'},$scoreboard_count{'.'}";

    return $message;
}

sub write_log {
    my ( $log_file, $message ) = @_;

    my $header = "date,time,total_access,total_kbytes,uptime,"
                 . "req_per_sec,bytes_per_sec,bytes_per_req,busyservers,idleservers,"
                 . "waiting,starting_up,reading,sending,reading_keepalive,"
                 . "look_up_dns,closing,logging,finishing_gracefully,clean_up_idle,open_slot";

    if ( -f $log_file ){
        sysopen ( my $fh, $log_file, O_WRONLY|O_APPEND|O_CREAT ) or die $!;
        print $fh $message . "\n";
        close ( $fh );
    } else { 
        sysopen ( my $fh, $log_file, O_WRONLY|O_APPEND|O_CREAT ) or die $!;
        print $fh $header . "\n";
        print $fh $message . "\n";
        close ( $fh );
    }
}

exit 0;

設定など


使用する場合は、「mod_status」を有効にし、「ExtendedStatus On」として下さい。


また、必要に応じて、以下の部分を環境に合わせてカスタマイズして下さい。

my $hostname = "127.0.0.1";             # Name of server
my $port     = "80";                    # Port on server
my $address  = "server-status/?auto";   # Server Status Address
my $log_directory = "/var/log";         # Log Directory


※公開したものは、デフォルトで恐らく動くであろう状態にしています。

実行例


出力されるログはこんな感じです。ログファイルが無い場合はヘッダを入れるようにしました。
また、データが採取できない場合は0が入るようにしています。

# cat /var/log/20110110.server-status.log
date,time,total_access,total_kbytes,uptime,req_per_sec,bytes_per_sec,bytes_per_req,busyservers,idleservers,waiting,starting_up,reading,sending,reading_keepalive,look_up_dns,closing,logging,finishing_gracefully,clean_up_idle,open_slot
2011/01/10,01:01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2011/01/10,01:02,140223,6025,53,2645.72,116408,43.9985,28,0,0,2,0,1,0,0,27,0,0,0,226
2011/01/10,01:03,326803,14042,118,2769.52,121856,43.999,32,0,0,2,0,4,0,0,27,1,0,0,222
2011/01/10,01:04,462158,19859,165,2800.96,123246,44.0014,45,1,1,2,0,1,0,0,44,0,0,0,208
2011/01/10,01:05,616680,26499,219,2815.89,123904,44.0017,29,1,1,1,1,1,0,0,27,0,0,0,225
2011/01/10,01:06,800586,34401,283,2828.93,124476,44.001,50,0,0,2,1,1,0,0,48,0,0,0,204
2011/01/10,01:07,915240,39328,324,2824.81,124296,44.0014,31,4,4,0,0,1,0,0,30,0,0,0,221
2011/01/10,01:08,1000049,42971,384,2604.29,114589,44.0001,1,9,9,0,0,1,0,0,0,0,0,0,246
2011/01/10,01:09,1000050,42971,444,2252.36,99104.3,44.0001,1,9,9,0,0,1,0,0,0,0,0,0,246
2011/01/10,01:10,1000051,42972,504,1984.23,87308.2,44.0011,1,9,9,0,0,1,0,0,0,0,0,0,246
2011/01/10,01:11,1000052,42972,564,1773.14,78020.1,44.001,1,9,9,0,0,1,0,0,0,0,0,0,246
2011/01/10,01:12,1000053,42972,624,1602.65,70518.2,44.001,1,9,9,0,0,1,0,0,0,0,0,0,246


今回の確認用に、Apache同梱の「ab」を使いました。

# /usr/local/httpd-2.2.17.80/bin/ab -n 1000000 -c 30 http://127.0.0.1/index.html
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 100000 requests
Completed 200000 requests
Completed 300000 requests
Completed 400000 requests
Completed 500000 requests
Completed 600000 requests
Completed 700000 requests
Completed 800000 requests
Completed 900000 requests
Completed 1000000 requests
Finished 1000000 requests


Server Software:        Apache/2.2.17
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /index.html
Document Length:        44 bytes

Concurrency Level:      30
Time taken for tests:   350.418 seconds
Complete requests:      1000000
Failed requests:        0
Write errors:           0
Total transferred:      296000000 bytes
HTML transferred:       44000000 bytes
Requests per second:    2853.73 [#/sec] (mean)
Time per request:       10.513 [ms] (mean)
Time per request:       0.350 [ms] (mean, across all concurrent requests)
Transfer rate:          824.91 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.7      0      14
Processing:     2   10 143.8      9   38925
Waiting:        1    9 119.2      8   38925
Total:          2   10 143.8      9   38925

Percentage of the requests served within a certain time (ms)
  50%      9
  66%     10
  75%     10
  80%     10
  90%     10
  95%     11
  98%     12
  99%     12
 100%  38925 (longest request)


定期的に実行する場合は cron の設定を行います。
以下は5分毎に実行する例です。

 */5 * * * * /usr/local/scripts/get_server_status.pl


今日はこんなところで。