Skip to content

Instantly share code, notes, and snippets.

@srgoogleguy
Created December 1, 2012 04:16
Show Gist options
  • Save srgoogleguy/4180517 to your computer and use it in GitHub Desktop.
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
#!/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