Created
December 1, 2012 04:16
-
-
Save srgoogleguy/4180517 to your computer and use it in GitHub Desktop.
tailwatch -- Monitor your Apache httpd log with this PHP shell script in real time for more comprehensive information
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/php | |
<?php | |
const TW_VERSION = '0.3.2'; | |
/** | |
@name tailwatch | |
@version 0.3.2 BETA | |
@author Sherif Ramadan | |
@url http://sheriframadan.com | |
@description This is a PHP script for monitoring your apache httpd log from the command line. | |
The script requires libevent and ncurses see (http://pecl.php.net/ncurses http://pecl.php.net/libevent) | |
and PHP 5.3.0 or greater. The script must also be run from a CLI SAPI. | |
@usage You can save the script in your home directory and symlink it in /usr/bin or /usr/sbin then | |
run the script from `./tailwatch /path/to/access.log` or `tailwatch /path/to/access.log` or even | |
`php tailwatch /path/toaccess.log` | |
If you want to invoke the script without calling the interpreter directly make sure to modify the | |
shebang line at the very top of this script to the absolute path of your PHP CLI binary. | |
There is only one required argument and that's the absolute path to your httpd access log. | |
The log format is assumed 'NCSA extended/combined log format' by default. So you will need to adjust | |
the code accordingly if your format differs. | |
----------------------- | |
Command Line Arguments: | |
----------------------- | |
There are three optional arguments: | |
--hostname=[example.com] -- The hostname for the log file's VirtualHost | |
--max=[-1] -- The maximum number of requests to watch before exiting | |
--buffer=[1000] -- The maximum buffer size holding recent requests (how far back to scroll) | |
------------- | |
Control Keys: | |
------------- | |
Press 1 and 2 to switch between wide/long screen modes. (Information about the latest hits) | |
Press v to view the latest visitors information. (This is the order they appeared in log) | |
Press V to view the top visitors information. (This is in ascending order of number of hits) | |
Press P to view the top page views. (This is in ascending order of number of hits/visitors/time) | |
Press Q to quit at any time. | |
------------ | |
Scroll Keys: | |
------------ | |
PageUp/U/u to scroll up 10 lines at a time | |
PageDown/D/d to scroll down 10 lines at a time | |
+/UpArrow to scroll up 1 line at a time | |
-/DownArrow to scroll down 1 line at a time | |
Home/H/h to go to the top of the page | |
End/E/e to go to the bottom of the page | |
*/ | |
if (strtolower(PHP_SAPI) !== 'cli') { | |
echo "Please run from CLI only...\n"; | |
exit; | |
} | |
if ($argc < 2) { | |
print_help(); | |
echo "No file name supplied...\n"; | |
exit; | |
} | |
if ($argv[1] === '--help' || $argv[1] === '-h') { | |
print_help(); | |
exit; | |
} | |
if (!file_exists($argv[1])) { | |
echo "The file '{$argv[1]}' does not exist...\n"; | |
exit; | |
} | |
if (!is_file($argv[1])) { | |
echo "The file '{$argv[1]}' does not appear to be a regular file...\n"; | |
exit; | |
} | |
if (!is_readable($argv[1])) { | |
$user = trim(`whoami`); | |
echo "The file '{$argv[1]}' does not appear to be readable by the current user '$user'! | |
Make sure you have sufficient premisions to read this file and try again...\n"; | |
exit; | |
} | |
// Initialize default optional command line arguments | |
$hostname = 'example.com'; // default == 'example.com' | |
$max_requests = -1; // default == -1 | |
$buffersize = 1000; // default == 1000 | |
foreach ($argv as $arg) { | |
if (strpos($arg,'--hostname=') === 0) { | |
$param = explode('=', $arg, 2); | |
$hostname = $param[1]; | |
} | |
if (strpos($arg,'--max=') === 0) { | |
$param = explode('=', $arg, 2); | |
$max_requests = $param[1]; | |
} | |
if (strpos($arg,'--buffer=') === 0) { | |
$param = explode('=', $arg, 2); | |
$buffersize = $param[1]; | |
} | |
} | |
// Check for requirements... | |
if (!extension_loaded('ncurses')) { | |
die("The ncurses extension is required to run this script! Please make sure it's loaded...\n"); | |
} | |
if (!extension_loaded('libevent')) { | |
die("The libevent extension is required to run this script! Please make sure it's loaded...\n"); | |
} | |
// Initialize Global MODE variable | |
$mode = 1; // default is 1 -- wide screen view (optional 2 for tall screen view) | |
// Initialize descriptors | |
$descriptorspec = array( | |
0 => array("pipe", "r"), // stdin is a pipe that the child will read from | |
1 => array("pipe", "w"), // stdout is a pipe that the child will write to | |
2 => array("pipe", "w"), // stderr is a pipe that the child will write from | |
); | |
// Initialize current working directory and environment | |
$cwd = __DIR__; // Use script's own CWD | |
$env = null; | |
// Open a new process for tail watch | |
$fd = proc_open('tail -F ' . $argv[1], $descriptorspec, $pipes, $cwd, $env); | |
if ($fd === false) { | |
die("Error trying to start tail...\n"); | |
} | |
$status = proc_get_status($fd); | |
if (!$status['running']) { | |
die("Error: It doesn't look like the child process is active...\n"); | |
} | |
if (!stream_set_blocking($pipes[0], 0)) { | |
die("Could not set pipes to non-blocking mode...\n"); | |
} | |
if (!stream_set_blocking($pipes[1], 0)) { | |
die("Could not set pipes to non-blocking mode...\n"); | |
} | |
if (!stream_set_blocking(STDIN, 0)) { | |
die("Could not set STDIN to non-blocking mode...\n"); | |
} | |
// Initialize ncurses | |
ncurses_init(); | |
ncurses_savetty(); | |
ncurses_curs_set(0); | |
ncurses_start_color(); | |
// Enable keycodes | |
ncurses_keyok(NCURSES_KEY_UP , true); | |
ncurses_keyok(NCURSES_KEY_DOWN , true); | |
// Create new ncurses window | |
$fullscreen = ncurses_newwin (0, 0, 0, 0); | |
// Set global cursors | |
$cursor_pos = 0; // 0 means no offset (i.e. from the bottom of the stack) | |
$cursor_pos2 = 0; | |
$cursor_pos3 = 0; | |
$cursor_pos4 = 0; | |
// Register the shutdown function | |
register_shutdown_function('shutdown'); | |
// Create base and event | |
$base = event_base_new(); | |
$event = event_new(); | |
$input = event_new(); | |
// Set event flags | |
event_set($event, $pipes[1], | |
EV_READ | EV_PERSIST, | |
"print_line", | |
array($event, $base, $fd, $pipes, | |
$max_requests, | |
microtime(true), $fullscreen, $hostname, | |
) | |
); | |
event_set($input, STDIN, | |
EV_READ | EV_PERSIST, | |
"get_input", | |
array($input, $base, $fd, $pipes, | |
$max_requests, | |
microtime(true), $fullscreen, $hostname, $event, | |
) | |
); | |
// Set event base | |
event_base_set($event, $base); | |
event_base_set($input, $base); | |
// Enable event | |
event_add($event); | |
event_add($input); | |
// Start event loop | |
event_base_loop($base); | |
/* SHUTDOWN FUNCTIONS */ | |
function shutdown() { | |
// restore the tty and end ncurses upon shutdown. | |
ncurses_resetty(); | |
ncurses_end(); | |
} | |
/* HELP FUNCTIONS */ | |
function print_help() { | |
$VERSION = TW_VERSION; | |
echo <<<HELPSCREEN | |
tailwatch version $VERSION by Sherif Ramadan. http://sheriframadan.com | |
Tail Watch is a PHP script that allows you to monitor your webserver logs in real time. | |
It's basically a glorified tail -F that pipes in your webserver log and provides more | |
comprehensive information right from the terminal. | |
Usage: tailwatch <logfile> [--hostname=, --max=, --buffer=] | |
logfile, Required as the absolute path to your webserver log file | |
--hostname, If supplied use this as the hostname to resolve links | |
--max, If supplied will monitor up to this many requests and exit | |
--buffer, If supplied will limit the scroll back buffer size (default is 1000) | |
Inside the terminal you can press 1, 2, v, V, P to move between screens and Q to quit at any time. | |
Note that this scripts assumes your webserver log is a apache httpd NCSA extended/combined log format. | |
You'll need to adjust the code accordingly if not. | |
HELPSCREEN; | |
} | |
/* LOG PARSE FUNCTIONS */ | |
function parse_by_space($line, &$start) { | |
if (($end = strpos($line, " ", $start)) === false) { | |
$start = strlen($line) - 1; | |
return ''; | |
} | |
$pos = $end - $start; | |
$offset = $start; | |
$start = $end + 1; | |
return substr($line, $offset, $pos); | |
} | |
function parse_by_delim($line, &$start, array $delim) { | |
if (count($delim) < 2) { | |
trigger_error("We need two delimeters for parse_by_delim!", E_USER_ERROR); | |
} | |
if (($x = strpos($line, $delim[0], $start)) === false) { | |
$start = strlen($line) - 1; | |
return ''; | |
} | |
if (($y = strpos($line, $delim[1], $x+1)) === false) { | |
$start = strlen($line) - 1; | |
return ''; | |
} | |
while($line[$y-1] === "\\" || $line[$y] !== $delim[1]) { | |
if (++$y >= strlen($line)) | |
break; | |
} | |
$pos = $y - $x - 1; | |
$offset = $x + 1; | |
$start = $y + 1; | |
return substr($line, $offset, $pos); | |
} | |
/* HELPER FUNCTIONS */ | |
function format_bytes($bytes, $as_array = false) { | |
$sizes = array('B','KB','MB','GB','TB','PB','EB','ZB','YB'); | |
$log = floor(log((int)$bytes, 1024)); | |
if ($bytes != 0) | |
$fig = $bytes / pow(1024, $log); | |
else | |
$fig = 0; | |
if (!$as_array) return sprintf("%.2f %s", $fig, $sizes[$log]); | |
else return array(sprintf("%.2f", $fig), $sizes[$log]); | |
} | |
function average_requests(array $requests) { | |
return array_sum($requests) / count($requests); | |
} | |
function page_data_srt($a, $b) { | |
/* First sort by number of hits for that page */ | |
if ($a['visits'] > $b['visits']) { | |
return 1; | |
} elseif ($a['visits'] < $b['visits']) { | |
return -1; | |
} else { | |
/* Second sort by number of visitors for that page */ | |
if ($a['visitors'] > $b['visitors']) { | |
return 1; | |
} elseif ($a['visitors'] < $b['visitors']) { | |
return -1; | |
} else { | |
/* Third sort by last time the page was visited */ | |
if ($a['time'] > $b['time']) { | |
return 1; | |
} elseif ($a['time'] < $b['time']) { | |
return -1; | |
} else { | |
return 0; | |
} | |
} | |
} | |
} | |
/* MAIN LIBEVENT FUNCTION */ | |
function print_line($fd, $events, $arg, $default_block = false) | |
{ | |
static $request = 0; | |
static $data = array(); | |
static $data2 = array(); | |
static $ips = array(); | |
static $visitors = array(); | |
static $dates = array(); | |
static $_current = array(); | |
static $status = array(); | |
static $referer = array(); | |
static $uas = array(); | |
static $_error_keys = array(); | |
static $_page_keys = array(); | |
static $bytes = 0; | |
static $pageviews = 0; | |
static $last_refresh; | |
static $page_data = array(); | |
global $mode; | |
$refresh_rate = 0.5; | |
$iua_tok = "\xfe\xff\xfe\xff"; | |
$_if_page = false; | |
// Import the global cursor positions and buffer size | |
global $cursor_pos, $cursor_pos2, $cursor_pos3, $cursor_pos4, $buffersize, $buffersize2, $buffersize3; | |
if (empty($arg[7])) | |
$host = ''; | |
else | |
$host = $arg[7]; | |
if (empty($arg[4])) $arg[4] = 10; | |
$request++; | |
static $num_request = 0; | |
$t = microtime(true) - $arg[5]; | |
$days = floor($t / (60*60*24)); | |
$hours = floor($t / (60*60)) - ($days * 24); | |
$minutes = floor($t / 60) - (($hours * 60) + ($days * 24 * 60)); | |
$seconds = $t % 60; | |
$time = sprintf("%d Days %02d:%02d:%02d", $days, $hours, $minutes, $seconds); | |
$avg1 = $num_request / $t; | |
$avg2 = ($num_request / $t) * 60; | |
$avg3 = ($num_request / $t) * 60 * 60; | |
if ($request == $arg[4]) { | |
// exit loop after max reads | |
fclose($arg[3][0]); | |
fclose($arg[3][1]); | |
proc_terminate($arg[2]); | |
event_base_loopexit($arg[1]); | |
} | |
// Get the current window height/width | |
$h = null; | |
$w = null; | |
//ncurses_getmaxyx (STDSCR, $h, $w); | |
ncurses_wrefresh($arg[6]); | |
ncurses_getmaxyx ($arg[6], $h, $w); | |
/* | |
Headers | |
MODE 1 -- Wide Screen | |
Header Format | |
------------- | |
8 Columns - 1 Row | |
8 | 4 | 4 | 15 | 10 | 35 | 35 | 35 | |
Time | Verb | Code | IP Address | Data Sent | Reuqest URI | User Agent | Referer | |
hh:mm:ss | GET | 200 | 123.123.123.123 | 1023.99 KB | | | | |
MODE 2 -- Long Screen | |
6 Columns - 2 Rows | Row 2 | |
8 | 4 | 4 | 15 | 10 | 105 | 73 / 73 | |
Time | Verb | Code | IP Address | Data Sent | Reuqest URI | | |
| User Agent | Referer | |
hh:mm:ss | GET | 200 | 123.123.123.123 | 1023.99 KB | | |
| | | |
MODE 3 -- Frequent Visitors | |
3 Columns - 1 Row | |
16 | 16 | 115 | |
#Requests | IP Address | User Agent String | |
999,999,999,999 | 123.123.123.123 | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) ... | |
*/ | |
$header_line = str_pad("Time", 8, " ") . " | "; | |
$header_line .= str_pad("Verb", 4, " ") . " | "; | |
$header_line .= str_pad("Code", 4, " ") . " | "; | |
$header_line .= str_pad("IP Address", 15, " ") . " | "; | |
$header_line .= str_pad("Data Sent", 10, " ") . " | "; | |
$header_line .= str_pad("Request URI", 35, " ") . " | "; | |
$header_line .= str_pad("User Agent", 35, " ") . " | "; | |
$header_line .= str_pad("Referer String", 35, " "); | |
$header_line = str_pad($header_line,$w," "); | |
$header_line2 = str_pad("Time", 8, " ") . " | "; | |
$header_line2 .= str_pad("Verb", 4, " ") . " | "; | |
$header_line2 .= str_pad("Code", 4, " ") . " | "; | |
$header_line2 .= str_pad("IP Address", 15, " ") . " | "; | |
$header_line2 .= str_pad("Data Sent", 10, " ") . " | "; | |
$header_line2 .= str_pad("Request URI", 105, " "); | |
$header_line2 .= "\n"; | |
$header_line2 .= str_pad("", 8, " ") . " | "; | |
$header_line2 .= str_pad("User Agent", 77, " ") . " | "; | |
$header_line2 .= str_pad("Referer String", 77, " "); | |
$header_line2 = str_pad($header_line2,$w," "); | |
$header_line3 = str_pad("#Requests", 16, " ") . " | "; | |
$header_line3 .= str_pad("IP Address", 16, " ") . " | "; | |
$header_line3 .= str_pad("User Agent String", 115, " "); | |
$header_line3 = str_pad($header_line3,$w," "); | |
$header_line4 = str_pad("Page Views", 16, " ") . " | "; | |
$header_line4 .= str_pad("Visitors", 16, " ") . " | "; | |
$header_line4 .= str_pad("Last Visit", 20, " ") . " | "; | |
$header_line4 .= str_pad("Page URL", 115, " "); | |
$header_line4 = str_pad($header_line4,$w," "); | |
$line = false; | |
// print the line | |
if (is_resource($fd)) { | |
$data = array_slice($data, -($buffersize)); | |
$data2 = array_slice($data2, -($buffersize)); | |
if (!$default_block) | |
$line = fgets($fd); | |
if ($line !== false && (strpos($line,'tail: `') === false)) { | |
$num_request++; | |
} elseif ($line !== false && (strpos($line,'tail: `') === 0)) { | |
ncurses_move($h, 0); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair(0, NCURSES_COLOR_RED, NCURSES_COLOR_CYAN); | |
ncurses_color_set(0); | |
} | |
$proc_stat = 'WRNG!'; | |
$datetime = date("m/d/Y g:i:s a"); | |
$_30minavg = sprintf("%.3f", count($_current) / (30*60)); | |
$_15minavg = sprintf("%.3f",count( | |
array_filter( | |
$_current, | |
function ($v) { if ($v >= (time() - 15*60)) return true; } | |
) | |
) / (15*60) | |
); | |
$_5minavg = sprintf("%.3f",count( | |
array_filter( | |
$_current, | |
function ($v) { if ($v >= (time() - 5*60)) return true; } | |
) | |
) / (5*60) | |
); | |
$_1minavg = sprintf("%.3f",count( | |
array_filter( | |
$_current, | |
function ($v) { if ($v >= (time() - 60)) return true; } | |
) | |
) / 60 | |
); | |
$bottom = str_pad("Status: [$proc_stat] | Local Time: $datetime | Req/sec (1/5/15/30 min. avg.): " . | |
"$_1minavg , $_5minavg , $_15minavg , $_30minavg | Q = quit, v,V = visitors latest/top", | |
$w, " " | |
); | |
ncurses_addstr($bottom); | |
ncurses_refresh(0); | |
ncurses_wrefresh($arg[6]); | |
return; | |
} | |
if (strpos($line, " ") !== false && (strpos($line,'tail: `') === false)) { | |
/* | |
/// BEGIN PARSING Apache httpd log line /// | |
We assume NCSA extended/combined log format | |
"%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" | |
Adjust accordingly for your own format (%l Remote logname and %u Remote user fields are ignored) | |
*/ | |
if ($cursor_pos) | |
$cursor_pos++; | |
$start = 0; | |
// Parse IP Address | |
$ip = parse_by_space($line, $start); | |
// Parse Date String | |
$d = date("Y-m-d H:i:s", strtotime(parse_by_delim($line, $start, array('[',']')))); | |
$date_full = date("Y-m-d", strtotime($d)); | |
$date = date("H:i:s", strtotime($d)); | |
// Parse Full Rrequest String | |
$req_f = parse_by_delim($line, $start, array('"','"')); | |
// Request method (GET, POST, PUT, HEAD, etc...) | |
$req_method = substr($req_f,0,strpos($req_f, " ")); | |
// Request URI /some/uri/here | |
$req = substr($req_f,strpos($req_f, " ")+1); | |
$req = explode(" ", $req); // Split the request string from the protocol/version | |
$protocol = explode("/",$req[1]); | |
$protocol = $protocol[0]; // Store the protocol here and drop the version | |
$req = $req[0]; // Finalized request URI | |
// Parse HTTP Response Code | |
$start++; | |
$stat = parse_by_space($line, $start); | |
$status_code = (int)$stat; | |
// Parse HTTP Response Bytes | |
$bytes_sent = parse_by_space($line, $start); | |
$bytes += $bytes_sent; | |
// Parse HTTP REFERER Request Header | |
$ref = parse_by_delim($line, $start, array('"','"')); | |
// Parse User Agent String | |
$ua = parse_by_delim($line, $start, array('"','"')); | |
// Calculate unique visitors/visits by IP+UA string hash | |
if (isset($visitors[$ip . $iua_tok . $ua])) { | |
$visitors[$ip . $iua_tok . $ua]++; | |
} else { | |
$visitors[$ip . $iua_tok . $ua] = 1; | |
if ($cursor_pos2) | |
$cursor_pos2++; | |
} | |
// Calculate unique visitors/visits by IP only | |
if (isset($ips[$ip])) | |
$ips[$ip]++; | |
else | |
$ips[$ip] = 1; | |
// Update global buffer for visitors screen | |
$buffersize2 = count($visitors); | |
// Calculate requests by date | |
if (isset($dates[$date_full])) | |
$dates[$date_full]++; | |
else | |
$dates[$date_full] = 1; | |
// Calculate status codes by request | |
if (isset($status[$stat])) | |
$status[$stat]++; | |
else | |
$status[$stat] = 1; | |
// Calculate referers by request | |
if (isset($referer[$ref])) | |
$referer[$ref]++; | |
else | |
$referer[$ref] = 1; | |
// Calculate UAs by request | |
if (isset($uas[$ua])) | |
$uas[$ua]++; | |
else | |
$uas[$ua] = 1; | |
// current set | |
$_current[] = strtotime($d); | |
/* | |
/// END PARSING Apache httpd log line /// | |
*/ | |
$date .= " | "; | |
$req_method_ = $req_method; | |
$req_method = str_pad($req_method, 4, " ") . " | "; | |
$stat = str_pad($stat, 4, " ") . " | "; | |
$ip = str_pad($ip, 15, " ") . " | "; | |
$bytes_sent = format_bytes($bytes_sent, true); | |
$bytes_sent = str_pad($bytes_sent[0], 7, " ") . str_pad($bytes_sent[1], 3, " ", STR_PAD_LEFT) . " | "; | |
$req_URI = $req; | |
/* Mode 2 Stuff */ | |
$req2 = $req; | |
if (strlen($req2) > 105) | |
$req2 = substr($req2, 0, 103) . "..."; | |
$req2 = str_pad($req2, 105); | |
$ua2 = $ua; | |
if (strlen($ua2) > 77) | |
$ua2 = substr($ua2, 0, 73) . "..."; | |
$ua2 = str_pad($ua2, 77) . " | "; | |
$ref2 = $ref; | |
if (strlen($ref2) > 77) | |
$ref2 = substr($ref2, 0, 73) . "..."; | |
$ref2 = str_pad($ref2, $w - (strlen($ua2) + strlen($req2)), " "); | |
$line2 = $date . $req_method . $stat . $ip . $bytes_sent . $req2 . " \n | " . $ua2 . $ref2; | |
/* Mode 1 Stuff */ | |
if (strlen($req) > 35) | |
$req = substr($req, 0, 32) . "..."; | |
$req = str_pad($req, 35, " ") . " | "; | |
if (strlen($ua) > 35) | |
$ua = substr($ua, 0, 32) . "..."; | |
$ua = str_pad($ua, 35, " ") . " | "; | |
if (strlen($ref) > 35) | |
$ref = substr($ref, 0, 32) . "..."; | |
$ref = str_pad($ref, 35, " ") . " | "; | |
$line = $date . $req_method . $stat . $ip . $bytes_sent . $req . $ua . $ref; | |
// Parse URL to determine if it's a page view or a request... | |
$url_parsed = parse_url($protocol . "://" . $host . $req_URI); | |
$url_parsed_string = strtolower($protocol) . "://" . $host . $req_URI; | |
if (!empty($url_parsed['path'])) { | |
$ext = pathinfo($url_parsed['path'], PATHINFO_EXTENSION); | |
if ((!$ext || $ext == 'php' || $ext == 'html') && | |
(($status_code >= 200 && $status_code < 300) || $status_code == 302) && | |
(strtoupper(trim($req_method_)) !== "HEAD")) { | |
$pageviews++; | |
$_if_page = true; | |
// $page_data array for page view statistics (mode 5) | |
if (!isset($page_data[$url_parsed_string])) { | |
$page_data[$url_parsed_string] = array( | |
'time' => strtotime($d), | |
'visits' => 1, | |
'visitors' => array($ip . $iua_tok . $ua => 1), | |
); | |
if ($cursor_pos4) { | |
$cursor_pos4++; | |
} | |
} else { | |
$page_data[$url_parsed_string]['time'] = strtotime($d); | |
$page_data[$url_parsed_string]['visits']++; | |
if (!in_array($ip . $iua_tok . $ua, $page_data[$url_parsed_string]['visitors'], true)) { | |
$page_data[$url_parsed_string]['visitors'][$ip . $iua_tok . $ua] = 1; | |
} else { | |
$page_data[$url_parsed_string]['visitors'][$ip . $iua_tok . $ua]++; | |
} | |
} | |
uasort($page_data, 'page_data_srt'); | |
$buffersize3 = count($page_data); | |
} | |
} | |
// Append new line to $data buffer array. | |
$data[] = $line; | |
$data2[] = $line2; | |
if ($status_code >= 400) $_if_error = true; | |
else $_if_error = false; | |
$_error_keys[] = $_if_error; | |
$_page_keys[] = $_if_page; | |
} | |
} | |
$_current = array_filter($_current, function($v) { if ($v >= (time() - (30*60))) return true; }); | |
/* Refresh rate control */ | |
if (!empty($last_refresh) && ($last_refresh >= (microtime(true) - $refresh_rate)) && !$default_block) return; | |
$last_refresh = microtime(true); | |
$average = sprintf("Avg. %.3f req/sec - Avg. %.2f req/min - Avg. %.1f req/hr - Unique Visitors By IP: %d ". | |
"- Unique Visitors By IP+UA: %d - Unique User Agents: %d", $avg1, $avg2, $avg3, count($ips), | |
count($visitors), count($uas)); | |
$_200s = empty($status[200]) ? 0 : $status[200]; | |
$_301s = empty($status[301]) ? 0 : $status[301]; | |
$_302s = empty($status[302]) ? 0 : $status[302]; | |
$_403s = empty($status[403]) ? 0 : $status[403]; | |
$_404s = empty($status[404]) ? 0 : $status[404]; | |
$_500s = empty($status[500]) ? 0 : $status[500]; | |
// Padd the line with spaces to the right | |
$average = str_pad($average, $w, " "); | |
$h--; | |
$color = 0; | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_WHITE, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
ncurses_move(0,0); | |
ncurses_clrtoeol(); | |
ncurses_addstr("Host = $host, Total Requests = $num_request, Total PageViews = $pageviews"); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_YELLOW, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
ncurses_move(1,0); | |
ncurses_clrtoeol(); | |
ncurses_addstr("Total 200s = $_200s, Total 301s = $_301s, Total 302s = $_302s," . | |
"Total 403s = $_403s, Total 404s = $_404s, Total 500s = $_500s"); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_GREEN, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
ncurses_move(2,0); | |
ncurses_clrtoeol(); | |
if ($mode == 1) { | |
if ($h-6 < count($data)) | |
$_cur = "$cursor_pos/" . (count($data) - ($h-6)); | |
else | |
$_cur = "$cursor_pos/" . count($data); | |
} | |
elseif ($mode == 2) { | |
if ($h-7 < count($data2)) | |
$_cur = "$cursor_pos/" . (count($data2) - floor(($h-7)/2)); | |
else | |
$_cur = "$cursor_pos/" . count($data); | |
} | |
elseif ($mode == 3) { | |
if ($h-6 < count($visitors)) | |
$_cur = "$cursor_pos2/" . (count($visitors) - ($h-6)); | |
else | |
$_cur = "$cursor_pos2/" . count($visitors); | |
} | |
elseif ($mode == 4) { | |
if ($h-6 < count($visitors)) | |
$_cur = "$cursor_pos3/" . (count($visitors) - ($h-6)); | |
else | |
$_cur = "$cursor_pos3/" . count($visitors); | |
} | |
elseif ($mode == 5) { | |
if ($h-6 < count($page_data)) | |
$_cur = "$cursor_pos4/" . (count($page_data) - ($h-6)); | |
else | |
$_cur = "$cursor_pos4/" . count($page_data); | |
} | |
ncurses_addstr("Time running: $time -- Data sent: " . | |
format_bytes($bytes) . | |
" -- Mode: " . | |
($mode == 1 ? | |
'Wide Screen (Press 2 for Long Screen)' | |
: | |
($mode == 2 ? | |
'Long Screen (Press 1 for Wide Screen)' | |
: | |
($mode == 3 ? | |
'Latest Visitors (Press 1 or 2 to get back to Screen View)' | |
: | |
($mode == 4 ? | |
'Top Visitors (Press 1/2 to return to Screen View)' | |
: | |
'Top Page Views' | |
) | |
) | |
) | |
) . | |
" -- Scroll: $_cur" | |
); | |
ncurses_move(3,0); | |
ncurses_clrtoeol(); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_WHITE, NCURSES_COLOR_RED); | |
ncurses_color_set($color++); | |
} | |
ncurses_addstr($average); | |
ncurses_move(4,0); | |
ncurses_clrtoeol(); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_RED, NCURSES_COLOR_YELLOW); | |
ncurses_color_set($color++); | |
} | |
if ($mode == 1) { | |
ncurses_addstr($header_line); | |
} elseif ($mode == 2) { | |
foreach (explode("\n", $header_line2) as $x => $new_header) { | |
if ($x) { | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_MAGENTA, NCURSES_COLOR_YELLOW); | |
ncurses_color_set($color++); | |
} | |
} | |
ncurses_move(4+$x, 0); | |
ncurses_clrtoeol(); | |
ncurses_addstr(str_pad($new_header, $w, " ")); | |
} | |
} elseif ($mode == 3 || $mode == 4) { | |
ncurses_addstr($header_line3); | |
} elseif ($mode == 5) { | |
ncurses_addstr($header_line4); | |
} | |
/* Only show the last N lines from the cursor position according to screen height */ | |
if (($mode == 1 && !$cursor_pos) || ($mode == 1 && $default_block)) { | |
ncurses_move(5,0); | |
ncurses_clrtobot(); | |
$min = count($data) - ($h - 5); | |
if ($cursor_pos >= $min) $cursor_pos = $min; | |
if ($cursor_pos < 0) $cursor_pos = 0; | |
$pre_pos = (-($h - 5)) - ($cursor_pos); | |
$pre_set = $h - 5; | |
$filtered_errors = array_keys(array_filter(array_slice($_error_keys, $pre_pos, $pre_set))); | |
$filtered_pages = array_keys(array_filter(array_slice($_page_keys, $pre_pos, $pre_set))); | |
foreach (array_slice($data, $pre_pos, $pre_set) as $i => $l) { | |
ncurses_move(5 + $i, 0); | |
if (in_array($i, $filtered_errors, true)) { | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_RED, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
} | |
elseif (in_array($i, $filtered_pages, true)) { | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_WHITE, NCURSES_COLOR_BLUE); | |
ncurses_color_set($color++); | |
} | |
} | |
else { | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_BLUE, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
} | |
ncurses_addstr(substr(str_pad($l, $w, " "), 0, $w)); | |
} | |
} elseif(($mode == 2 && !$cursor_pos) || ($mode == 2 && $default_block)) { | |
ncurses_move(6,0); | |
ncurses_clrtobot(); | |
$min = count($data) - floor(($h - 6)/2); | |
if ($cursor_pos >= $min) $cursor_pos = $min; | |
if ($cursor_pos < 0) $cursor_pos = 0; | |
$pre_pos = -(floor(($h - 6)/2)) - ($cursor_pos); | |
$pre_set = floor(($h - 6)/2); | |
$filtered_errors = array_keys(array_filter(array_slice($_error_keys, $pre_pos, $pre_set))); | |
$filtered_pages = array_keys(array_filter(array_slice($_page_keys, $pre_pos, $pre_set))); | |
foreach (array_slice($data2, $pre_pos, $pre_set) as $i => $l) { | |
foreach (explode("\n", $l) as $ii => $ll) { | |
ncurses_move(6 + ($i*2) + $ii, 0); | |
if (in_array($i, $filtered_errors, true)) { | |
if (!$ii) { | |
ncurses_init_pair($color, NCURSES_COLOR_RED, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
else { | |
ncurses_init_pair($color, NCURSES_COLOR_MAGENTA, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
} | |
elseif (in_array($i, $filtered_pages, true)) { | |
if (ncurses_has_colors()) { | |
if (!$ii) { | |
ncurses_init_pair($color, NCURSES_COLOR_WHITE, NCURSES_COLOR_BLUE); | |
ncurses_color_set($color++); | |
} | |
else { | |
ncurses_init_pair($color, NCURSES_COLOR_MAGENTA, NCURSES_COLOR_WHITE); | |
ncurses_color_set($color++); | |
} | |
} | |
} | |
else { | |
if (ncurses_has_colors()) { | |
if (!$ii) { | |
ncurses_init_pair($color, NCURSES_COLOR_BLUE, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
else { | |
ncurses_init_pair($color, NCURSES_COLOR_MAGENTA, NCURSES_COLOR_BLACK); | |
ncurses_color_set($color++); | |
} | |
} | |
} | |
ncurses_addstr(substr(str_pad($ll, $w, " "), 0, $w)); | |
} | |
} | |
} elseif(($mode == 3 && !$cursor_pos2) || ($mode == 3 && $default_block)) { | |
ncurses_move(5,0); | |
ncurses_clrtobot(); | |
$min = count($visitors) - ($h - 5); | |
if ($cursor_pos2 >= $min) $cursor_pos2 = $min; | |
if ($cursor_pos2 < 0) $cursor_pos2 = 0; | |
$pre_pos = (-($h - 5)) - ($cursor_pos2); | |
$pre_set = $h - 5; | |
$i = 0; | |
foreach(array_slice($visitors, $pre_pos, $pre_set) as $vis_ => $count_) { | |
list($col_2, $col_3) = explode($iua_tok, $vis_); | |
$col_1 = str_pad(number_format($count_), 16, " ") . " | "; | |
$col_2 = str_pad($col_2, 16, " ") . " | "; | |
$col_3 = str_pad($col_3, 115, " "); | |
ncurses_move(5 + $i++, 0); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_YELLOW, NCURSES_COLOR_BLUE); | |
ncurses_color_set($color++); | |
} | |
ncurses_addstr(substr(str_pad($col_1 . $col_2 . $col_3, $w, " "), 0, $w)); | |
} | |
ncurses_move(5 + $i++, 0); | |
unset($i); | |
} elseif(($mode == 4 && !$cursor_pos3) || ($mode == 4 && $default_block)) { | |
ncurses_move(5,0); | |
ncurses_clrtobot(); | |
$visitors_ = $visitors; | |
asort($visitors_); | |
$min = count($visitors_) - ($h - 5); | |
if ($cursor_pos3 >= $min) $cursor_pos3 = $min; | |
if ($cursor_pos3 < 0) $cursor_pos3 = 0; | |
$pre_pos = (-($h - 5)) - ($cursor_pos3); | |
$pre_set = $h - 5; | |
$i = 0; | |
foreach(array_slice($visitors_, $pre_pos, $pre_set) as $vis_ => $count_) { | |
list($col_2, $col_3) = explode($iua_tok, $vis_); | |
$col_1 = str_pad(number_format($count_), 16, " ") . " | "; | |
$col_2 = str_pad($col_2, 16, " ") . " | "; | |
$col_3 = str_pad($col_3, 115, " "); | |
ncurses_move(5 + $i++, 0); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_YELLOW, NCURSES_COLOR_BLUE); | |
ncurses_color_set($color++); | |
} | |
ncurses_addstr(substr(str_pad($col_1 . $col_2 . $col_3, $w, " "), 0, $w)); | |
} | |
ncurses_move(5 + $i++, 0); | |
unset($visitors_, $i); | |
} elseif(($mode == 5 && !$cursor_pos4) || ($mode == 5 && $default_block)) { | |
ncurses_move(5,0); | |
ncurses_clrtobot(); | |
$min = count($page_data) - ($h - 5); | |
if ($cursor_pos4 >= $min) $cursor_pos4 = $min; | |
if ($cursor_pos4 < 0) $cursor_pos4 = 0; | |
$pre_pos = (-($h - 5)) - ($cursor_pos4); | |
$pre_set = $h - 5; | |
$i = 0; | |
foreach(array_slice($page_data, $pre_pos, $pre_set) as $__page => $__data) { | |
$col_1 = str_pad(number_format($__data['visits']), 16, " ") . " | "; // Hits | |
$col_2 = str_pad(number_format(count($__data['visitors'])), 16, " ") . " | "; // Visitors | |
$col_3 = str_pad(date('d-m-Y H:i:s',$__data['time']), 20, " ") . " | "; // Last Request | |
$col_4 = str_pad($__page, 115, " "); // Page | |
ncurses_move(5 + $i++, 0); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_YELLOW, NCURSES_COLOR_BLUE); | |
ncurses_color_set($color++); | |
} | |
ncurses_addstr(substr(str_pad($col_1 . $col_2 . $col_3 . $col_4, $w, " "), 0, $w)); | |
} | |
ncurses_move(5 + $i++, 0); | |
unset($i); | |
} | |
/* Create bottom status bar... */ | |
ncurses_move($h, 0); | |
if (ncurses_has_colors()) { | |
ncurses_init_pair($color, NCURSES_COLOR_RED, NCURSES_COLOR_CYAN); | |
ncurses_color_set($color++); | |
} | |
$proc_stat = proc_get_status($arg[2]); | |
$proc_stat = $proc_stat['running'] ? 'OK' : 'NO!'; | |
$datetime = date("m/d/Y g:i:s a"); | |
$_30minavg = sprintf("%.3f", count($_current) / (30*60)); | |
$_15minavg = sprintf("%.3f",count( | |
array_filter( | |
$_current, | |
function ($v) { if ($v >= (time() - 15*60)) return true; } | |
) | |
) / (15*60) | |
); | |
$_5minavg = sprintf("%.3f",count( | |
array_filter( | |
$_current, | |
function ($v) { if ($v >= (time() - 5*60)) return true; } | |
) | |
) / (5*60) | |
); | |
$_1minavg = sprintf("%.3f",count( | |
array_filter( | |
$_current, | |
function ($v) { if ($v >= (time() - 60)) return true; } | |
) | |
) / 60 | |
); | |
$bottom = str_pad("Status: [$proc_stat] | Local Time: $datetime | Req/sec (1/5/15/30 min. avg.): " . | |
"$_1minavg , $_5minavg , $_15minavg , $_30minavg | Q = quit, v,V = visitors latest/top", | |
$w, " " | |
); | |
ncurses_addstr($bottom); | |
ncurses_refresh(0); | |
ncurses_wrefresh($arg[6]); | |
} | |
function get_input($fd, $events, $arg) { | |
static $buf = false; | |
static $buffer = ''; | |
global $mode, $cursor_pos, $cursor_pos2, $cursor_pos3, $cursor_pos4, $buffersize, $buffersize2, $buffersize3; | |
if (($buf = fgets($fd, 8192)) !== false) { | |
$buffer .= $buf; | |
} | |
if ($buffer !== '') { | |
if ($buffer == 'q' || $buffer == 'Q') { | |
proc_terminate($arg[2]); | |
event_base_loopexit($arg[1], 0); | |
} | |
elseif ($buffer == '1') { | |
$mode = 1; | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == '2') { | |
$mode = 2; | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == 'v') { | |
$mode = 3; | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == 'V') { | |
$mode = 4; | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == 'p' || $buffer == 'P') { | |
$mode = 5; | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == '-' || $buffer == "\x1b\x5b\x41") { // Up Arrow | |
if ($mode == 1 || $mode == 2) | |
$cursor_pos = min($buffersize, ++$cursor_pos); | |
elseif ($mode == 3) | |
$cursor_pos2 = min($buffersize2, ++$cursor_pos2); | |
elseif ($mode == 4) | |
$cursor_pos3 = min($buffersize2, ++$cursor_pos3); | |
elseif ($mode == 5) | |
$cursor_pos4 = min($buffersize3, ++$cursor_pos4); | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == '+' || $buffer == '=' || $buffer == "\x1b\x5b\x42") { // Down Arrow | |
if ($mode == 1 || $mode == 2) | |
$cursor_pos = max(0, --$cursor_pos); | |
elseif ($mode == 3) | |
$cursor_pos2 = max(0, --$cursor_pos2); | |
elseif ($mode == 4) | |
$cursor_pos3 = max(0, --$cursor_pos3); | |
elseif ($mode == 5) | |
$cursor_pos4 = max(0, --$cursor_pos4); | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == "\x1b\x5b\x35\x7e" || $buffer == "u" || $buffer == "U") { // Page Up | |
if ($mode == 1 || $mode == 2) | |
$cursor_pos = min($buffersize, $cursor_pos += 10); | |
elseif ($mode == 3) | |
$cursor_pos2 = min($buffersize2, $cursor_pos2 += 10); | |
elseif ($mode == 4) | |
$cursor_pos3 = min($buffersize2, $cursor_pos3 += 10); | |
elseif ($mode == 5) | |
$cursor_pos4 = min($buffersize3, $cursor_pos4 += 10); | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == "\x1b\x5b\x36\x7e" || $buffer == "d" || $buffer == "D") { // Page Down | |
if ($mode == 1 || $mode == 2) | |
$cursor_pos = max(0, $cursor_pos -= 10); | |
elseif ($mode == 3) | |
$cursor_pos2 = max(0, $cursor_pos2 -= 10); | |
elseif ($mode == 4) | |
$cursor_pos3 = max(0, $cursor_pos3 -= 10); | |
elseif ($mode == 5) | |
$cursor_pos4 = max(0, $cursor_pos4 -= 10); | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == "\x1b\x4f\x48" || $buffer == "h" || $buffer == "H") { // Home | |
if ($mode == 1 || $mode == 2) | |
$cursor_pos = $buffersize; | |
elseif ($mode == 3) | |
$cursor_pos2 = $buffersize2; | |
elseif ($mode == 4) | |
$cursor_pos3 = $buffersize2; | |
elseif ($mode == 5) | |
$cursor_pos4 = $buffersize3; | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == "\x1b\x4f\x46" || $buffer == "e" || $buffer == "E") { // End | |
if ($mode == 1 || $mode == 2) | |
$cursor_pos = 0; | |
elseif ($mode == 3) | |
$cursor_pos2 = 0; | |
elseif ($mode == 4) | |
$cursor_pos3 = 0; | |
elseif ($mode == 5) | |
$cursor_pos4 = 0; | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
elseif ($buffer == 'r' || $buffer == 'R') { | |
ncurses_move(0,0); | |
ncurses_clrtobot(); | |
ncurses_refresh(0); | |
print_line($arg[3][1], $arg[8], $arg, true); | |
} | |
$buffer = ''; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment