diff --git a/predict.py b/predict.py index 1e0d6f5..91da21d 100755 --- a/predict.py +++ b/predict.py @@ -14,8 +14,11 @@ import traceback import calendar import optparse import subprocess +import statsd import simplejson as json +statsd.init_statsd({'STATSD_BUCKET_PREFIX': 'habhub.predictor'}) + # We use Pydap from http://pydap.org/. import pydap.exceptions, pydap.client, pydap.lib pydap.lib.CACHE = "/tmp/pydap-cache/" @@ -65,11 +68,14 @@ def update_progress(**kwargs): global log log.error('Could not update progress file') +@statsd.StatsdTimer.wrap('time') def main(): """ The main program routine. """ + statsd.increment('run') + # Set up our command line options parser = optparse.OptionParser() parser.add_option('-d', '--cd', dest='directory', @@ -142,6 +148,7 @@ def main(): # Check we got a UUID in the arguments if len(args) != 1: log.error('Exactly one positional argument should be supplied (uuid).') + statsd.increment('error') sys.exit(1) if options.directory: @@ -162,6 +169,7 @@ def main(): if process.find(uuid) > 0: pid = int(line.split()[0]) if pid != os.getpid(): + statsd.increment('duplicate') log.error('A process is already running for this UUID, quitting.') sys.exit(1) @@ -180,25 +188,30 @@ def main(): run_time=str(int(timelib.time()))) except IOError: log.error('Error opening progress.json file') + statsd.increment('error') sys.exit(1) # Check the predictor binary exists if not os.path.exists(pred_binary): log.error('Predictor binary does not exist.') + statsd.increment('error') sys.exit(1) # Check the latitude is in the right range. if (options.lat < -90) | (options.lat > 90): log.error('Latitude %s is outside of the range (-90,90).') + statsd.increment('error') sys.exit(1) # Check the delta sizes are valid. if (options.latdelta <= 0.5) | (options.londelta <= 0.5): log.error('Latitiude and longitude deltas must be at least 0.5 degrees.') + statsd.increment('error') sys.exit(1) if options.londelta > 180: log.error('Longitude window sizes greater than 180 degrees are meaningless.') + statsd.increment('error') sys.exit(1) # We need to wrap the longitude into the right range. @@ -224,6 +237,8 @@ def main(): dataset = dataset_for_time(time_to_find, options.hd) except: log.error('Could not locate a dataset for the requested time.') + statsd.increment('no_dataset') + statsd.increment('error') sys.exit(1) dataset_times = map(timestamp_to_datetime, dataset.time) @@ -261,6 +276,7 @@ def main(): subprocess.call([pred_binary, '-i/var/www/cusf-standalone-predictor/gfs/', '-v', '-o'+uuid_path+'flight_path.csv', uuid_path+'scenario.ini'] + alarm_flags) update_progress(pred_running=False, pred_complete=True) + statsd.increment('success') def purge_cache(): """ @@ -573,8 +589,10 @@ if __name__ == '__main__': log.debug("Exit: " + repr(e)) if e.code != 0 and progress_f: update_progress(error="Unknown error exit") + statsd.increment("unknown_error_exit") raise except Exception as e: + statsd.increment("uncaught_exception") log.exception("Uncaught exception") (exc_type, exc_value, discard_tb) = sys.exc_info() exc_tb = traceback.format_exception_only(exc_type, exc_value) diff --git a/predict/ajax.php b/predict/ajax.php index bd2b1a1..78f3f4c 100644 --- a/predict/ajax.php +++ b/predict/ajax.php @@ -2,6 +2,8 @@ require_once("includes/functions.inc.php"); require_once("includes/config.inc.php"); +$stats = new StatsD(); + $action = $_GET['action']; $software_available = array("gfs", "gfs_hd"); @@ -19,6 +21,7 @@ case "getCSV": } $returned = json_encode($data); echo $returned; + $stats->counting('habhub.predictor.php.get_csv'); break; case "JSONexists": @@ -58,6 +61,7 @@ case "getModelByUUID": $pred_model = array(); if ( !file_exists(PREDS_PATH . $uuid . "/" . SCENARIO_FILE ) ) { $pred_model['valid'] = false; + $stats->counting('habhub.predictor.php.couldnt_get_by_uuid'); } else { // populate the array, JSON encode it and return $pred_model = parse_ini_file(PREDS_PATH . $uuid . "/" . SCENARIO_FILE); @@ -67,6 +71,7 @@ case "getModelByUUID": $pred_model['valid'] = false; } $pred_model['uuid'] = $uuid; + $stats->counting('habhub.predictor.php.got_by_uuid'); } echo json_encode($pred_model); break; @@ -83,6 +88,7 @@ case "submitForm": $json_return['error'] = "Server couldn't make a model from the form data"; echo json_encode($json_return); + $stats->counter('habhub.predictor.php.form_error'); break; } @@ -91,6 +97,7 @@ case "submitForm": if ( !$verify_dump['valid'] ) { $json_return['error'] = $verify_dump['msg']; echo json_encode($json_return); + $stats->counter('habhub.predictor.php.invalid_model') break; } @@ -98,6 +105,7 @@ case "submitForm": if ( !$pred_model['uuid'] = makesha1hash($pred_model) ) { $json_return['error'] = "Couldn't make the SHA1 hash"; echo json_encode($json_return); + $stats->counter('habhub.predictor.php.unhashable'); break; } @@ -106,10 +114,12 @@ case "submitForm": $json_return['valid'] = "true"; $json_return['uuid'] = $pred_model['uuid']; $json_return['timestamp'] = $pred_model['timestamp']; + $stats->counting('habhub.predictor.php.prediction_run'); } else { $json_return['error'] = "The form submit function was called without any data"; + $stats->counting('habhub.predictor.php.no_form_data'); } echo json_encode($json_return); diff --git a/predict/includes/functions.inc.php b/predict/includes/functions.inc.php index 1073af0..9a7b409 100644 --- a/predict/includes/functions.inc.php +++ b/predict/includes/functions.inc.php @@ -6,6 +6,7 @@ */ require_once("config.inc.php"); +require_once("statsd.php"); // Given a POST array, create a scenario model function createModel($post_array) { diff --git a/predict/includes/statsd.php b/predict/includes/statsd.php new file mode 100644 index 0000000..ac65594 --- /dev/null +++ b/predict/includes/statsd.php @@ -0,0 +1,47 @@ + + * https://raw.github.com/seejohnrun/php-statsd/master/libraries/statsd.php + */ +class StatsD { + + private $host, $port; + + // Instantiate a new client + public function __construct($host = 'localhost', $port = 8125) { + $this->host = $host; + $this->port = $port; + } + + // Record timing + public function timing($key, $time, $rate = 1) { + $this->send("$key:$time|ms", $rate); + } + + // Time something + public function time_this($key, $callback, $rate = 1) { + $begin = microtime(true); + $callback(); + $time = floor((microtime(true) - $begin) * 1000); + // And record + $this->timing($key, $time, $rate); + } + + // Record counting + public function counting($key, $amount = 1, $rate = 1) { + $this->send("$key:$amount|c", $rate); + } + + // Send + private function send($value, $rate) { + $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr); + // Will show warning if not opened, and return false + if ($fp) { + fwrite($fp, "$value|@$rate"); + fclose($fp); + } + } + +} diff --git a/predict/index.php b/predict/index.php index 5ff5bc8..50967af 100644 --- a/predict/index.php +++ b/predict/index.php @@ -14,6 +14,10 @@ require_once("includes/config.inc.php"); require_once("includes/functions.inc.php"); + +$stats = new StatsD(); +$stats->counting('hits'); + // Get the time for pre-populating the form $time = time() + 3600; ?>