Your IP : 216.73.217.92


Current Path : /home/blindman/www/wp-content/mu-plugins/hosting/
Upload File :
Current File : /home/blindman/www/wp-content/mu-plugins/hosting/statsd.php

<?php
/*
Plugin Name: StatsD WordPress Client
Version: 0.2
Plugin URI: http://wordpress.org/extend/plugins/statsd/
Description: For no-latency application metric tracking and code profiling via <a href="http://codeascraft.com/2011/02/15/measure-anything-measure-everything/">Etsy's StatsD</a> + Graphite. Tracks logins, password resets, users, registrations, posting, commenting, attachments, XML-RPC, multiiste blog actions, load time, query count, http requests, and more! Requires <a href="https://github.com/etsy/statsd">StatsD</a> on the local or private network server. See API usage instructions: https://github.com/domnikl/statsd-php/blob/develop/README.md
Author: Aaron Edwards
Author URI: https://uglyrobot.com
Network: true

Copyright 2019 UglyRobot Web Development (http://uglyrobot.com)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License (Version 2 - GPLv2) as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/


//set default config if not set in wp-config
if ( ! defined( 'STATSD_IP' ) ) {
	define( 'STATSD_IP', '127.0.0.1' );
}
if ( ! defined( 'STATSD_PORT' ) ) {
	define( 'STATSD_PORT', 8125 );
}
if ( ! defined( 'STATSD_SAMPLE_RATE' ) ) {
	define( 'STATSD_SAMPLE_RATE', 1 );
} //allows you to override the sample rate for calls that run every page load. Example 0.5 would be roughly 50% of pageloads.

define( 'STATSD_NAMESPACE', '' ); // we don't need a namespace in our hosted environment.


//the plugin class to track default WP application stats
class WordPress_StatsD {

	private $statsd;

	public function __construct() {
		//create global
		global $statsd;
		$statsd_connection = new StatsD_Connect( STATSD_IP, STATSD_PORT );
		$statsd            = new StatsD( $statsd_connection, STATSD_NAMESPACE );
		$this->statsd      = &$statsd;

		//action hooks
		add_action( 'wp_login', array( &$this, 'login' ) );
		add_action( 'wp_logout', array( &$this, 'logout' ) );
		add_action( 'wp_login_failed', array( &$this, 'login_fail' ) );
		add_action( 'retrieve_password_key', array( &$this, 'password_reset_gen' ) );
		add_action( 'password_reset', array( &$this, 'password_reset_complete' ) );
		add_action( 'user_register', array( &$this, 'user_register' ) );

		add_action( 'publish_post', array( &$this, 'publish_post' ) );
		add_action( 'wp_trash_post', array( &$this, 'trash_post' ) );
		add_action( 'delete_post', array( &$this, 'delete_post' ) );

		add_action( 'wp_insert_comment', array( &$this, 'new_comment' ) );
		add_action( 'wp_set_comment_status', array( &$this, 'approve_comment' ), 10, 2 );
		add_action( 'trash_comment', array( &$this, 'trash_comment' ) );
		add_action( 'spam_comment', array( &$this, 'spam_comment' ) );
		add_action( 'unspam_comment', array( &$this, 'unspam_comment' ) );

		add_action( 'add_attachment', array( &$this, 'add_attachment' ) );
		add_action( 'edit_attachment', array( &$this, 'edit_attachment' ) );
		add_action( 'delete_attachment', array( &$this, 'delete_attachment' ) );

		//multisite only hooks
		if ( is_multisite() ) {
			add_action( 'wpmu_new_user', array( &$this, 'user_register' ) );
			add_action( 'wpmu_new_blog', array( &$this, 'new_blog' ) );
			add_action( 'make_spam_blog', array( &$this, 'spam_blog' ) );
			add_action( 'make_ham_blog', array( &$this, 'ham_blog' ) );
			add_action( 'make_spam_user', array( &$this, 'spam_user' ) );
			add_action( 'make_ham_user', array( &$this, 'ham_user' ) );
			add_action( 'archive_blog', array( &$this, 'archive_blog' ) );
			add_action( 'unarchive_blog', array( &$this, 'unarchive_blog' ) );
			add_action( 'make_delete_blog', array( &$this, 'delete_blog' ) );
			add_action( 'make_undelete_blog', array( &$this, 'undelete_blog' ) );
			add_action( 'init', array( &$this, 'blog_count' ) );
		}

		add_action( 'init', array( &$this, 'user_count' ) ); //multisite aware

		//http request timing
		add_filter( 'pre_http_request', array( &$this, 'pre_http' ), 10, 3 );
		add_action( 'http_api_debug', array( &$this, 'post_http' ), 10, 5 );

		//wpdb
		add_action( 'shutdown', array( &$this, 'num_queries' ) );

		add_action( 'shutdown', array( &$this, 'load_time' ) );

		add_action( 'xmlrpc_call', array( &$this, 'xmlrpc_call' ) );

		add_action( 'rest_api_init', array( &$this, 'rest_api_call' ) );

		//trac wp cron
		add_action( 'init', array( &$this, 'wp_cron' ) );

		//wp_mail
		add_filter( 'wp_mail', array( &$this, 'wp_mail' ) );
	}

	/* logins/registration */
	public function login( $username ) {
		$this->statsd->increment( "wordpress.logins.login" );
	}

	public function logout() {
		$this->statsd->increment( "wordpress.logins.logout" );
	}

	public function login_fail( $username ) {
		$this->statsd->increment( "wordpress.logins.fail" );
	}

	public function password_reset_gen( $username ) {
		$this->statsd->increment( "wordpress.logins.reset_start" );
	}

	public function password_reset_complete( $user ) {
		$this->statsd->increment( "wordpress.logins.reset_complete" );
	}

	public function user_register( $user_id ) {
		$this->statsd->increment( "wordpress.users.register" );
	}

	/* normal blog actions */
	public function publish_post( $id ) {
		$this->statsd->increment( "wordpress.posts.publish" );
	}

	public function trash_post( $id ) {
		$this->statsd->increment( "wordpress.posts.trash" );
	}

	public function delete_post( $id ) {
		$this->statsd->increment( "wordpress.posts.delete" );
	}

	public function new_comment( $id ) {
		$this->statsd->increment( "wordpress.comments.new" );
	}

	public function approve_comment( $id, $status ) {
		if ( 'approve' == $status ) {
			$this->statsd->increment( "wordpress.comments.approve" );
		}
	}

	public function spam_comment( $id ) {
		$this->statsd->increment( "wordpress.comments.spam" );
	}

	public function unspam_comment( $id ) {
		$this->statsd->increment( "wordpress.comments.unspam" );
	}

	public function trash_comment( $id ) {
		$this->statsd->increment( "wordpress.comments.trash" );
	}

	public function add_attachment( $id ) {
		$this->statsd->increment( "wordpress.attachments.add" );
	}

	public function edit_attachment( $id ) {
		$this->statsd->increment( "wordpress.attachments.edit" );
	}

	public function delete_attachment( $id ) {
		$this->statsd->increment( "wordpress.attachments.delete" );
	}

	/* multisite */
	public function new_blog( $id ) {
		$this->statsd->increment( "wordpress.blogs.new" );
	}

	public function spam_blog( $id ) {
		$this->statsd->increment( "wordpress.blogs.spam" );
	}

	public function ham_blog( $id ) {
		$this->statsd->increment( "wordpress.blogs.unspam" );
	}

	public function spam_user( $id ) {
		$this->statsd->increment( "wordpress.users.spam" );
	}

	public function ham_user( $id ) {
		$this->statsd->increment( "wordpress.users.unspam" );
	}

	public function delete_blog( $id ) {
		$this->statsd->increment( "wordpress.blogs.delete" );
	}

	public function undelete_blog( $id ) {
		$this->statsd->increment( "wordpress.blogs.undelete" );
	}

	public function archive_blog( $id ) {
		$this->statsd->increment( "wordpress.blogs.archive" );
	}

	public function unarchive_blog( $id ) {
		$this->statsd->increment( "wordpress.blogs.unarchive" );
	}

	public function blog_count() {
		//Only send this gauge on every hundredth request, it doesn't change often
		$sample = mt_rand() / mt_getrandmax();
		if ( $sample <= 0.01 ) {
			$this->statsd->gauge( "wordpress.blogs.count", get_blog_count() );
		}
	}

	public function user_count() {
		//Only send this gauge on every hundredth request, it doesn't change often
		$sample = mt_rand() / mt_getrandmax();
		if ( $sample <= 0.01 ) {
			if ( is_multisite() ) {
				$user_count = get_user_count();
			} else {
				//$user_count = count_users();
				//$user_count = $user_count['total_users'];
				global $wpdb;
				$user_count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users" ); //don't go by role, make it simple
			}
			$this->statsd->gauge( "wordpress.users.count", $user_count );
		}
	}

	public function pre_http( $false, $r, $url ) {
		$this->statsd->startTiming( "wordpress.http.requests" );

		return $false;
	}

	public function post_http( $response, $type, $class, $args, $url ) {

		$this->statsd->startBatch();
		$this->statsd->endTiming( "wordpress.http.requests" );
		$this->statsd->increment( "wordpress.http.counts" );
		$this->statsd->endBatch();
	}

	public function num_queries() {
		$this->statsd->count( "wordpress.wpdb.queries", get_num_queries(), STATSD_SAMPLE_RATE );
	}

	public function load_time() {
		global $timestart;
		// can't use timer_stop() because it's i18n formatted.
		$timeend   = microtime( true );
		$timetotal = $timeend - $timestart;
		$load_time = round( 1000 * $timetotal );
		$this->statsd->timing( "wordpress.load_time", $load_time, STATSD_SAMPLE_RATE );
	}

	public function xmlrpc_call( $type ) {
		$this->statsd->increment( "wordpress.xmlrpc" );
	}

	public function rest_api_call( $wp_rest_server ) {
		$this->statsd->increment( "wordpress.rest_api" );
	}

	public function wp_cron() {
		if ( defined( 'DOING_CRON' ) ) {
			$this->statsd->increment( "wordpress.cron" );
		}
	}

	public function wp_mail( $wp_mail ) {
		$this->statsd->increment( "wordpress.email" );

		return $wp_mail;
	}
}

new WordPress_StatsD;


/* Based off php statsd client by Dominik Liebler <liebler.dominik@googlemail.com>
 * https://github.com/domnikl/statsd-php
 */

/**
 * the statsd client
 *
 * @author Dominik Liebler <liebler.dominik@googlemail.com>
 */
class StatsD {
	/**
	 * Connection object that messages get send to
	 *
	 * @var Connection
	 */
	protected $_connection;

	/**
	 * holds all the timings that have not yet been completed
	 *
	 * @var array
	 */
	protected $_timings = array();

	/**
	 * holds all memory profiles like timings
	 *
	 * @var array
	 */
	protected $_memoryProfiles = array();

	/**
	 * global key namespace
	 *
	 * @var string
	 */
	protected $_namespace = '';

	/**
	 * stores the batch after batch processing was started
	 *
	 * @var array
	 */
	protected $_batch = array();

	/**
	 * batch mode?
	 *
	 * @var boolean
	 */
	protected $_isBatch = false;

	/**
	 * inits the Client object
	 *
	 * @param Connection $connection
	 * @param string     $namespace global key namespace
	 */
	public function __construct( $connection, $namespace = '' ) {
		$this->_connection = $connection;
		$this->_namespace  = (string) $namespace;
	}

	/**
	 * increments the key by 1
	 *
	 * @param string $key
	 * @param int    $sampleRate
	 *
	 * @return void
	 */
	public function increment( $key, $sampleRate = 1 ) {
		$this->count( $key, 1, $sampleRate );
	}

	/**
	 * decrements the key by 1
	 *
	 * @param string $key
	 * @param int    $sampleRate
	 *
	 * @return void
	 */
	public function decrement( $key, $sampleRate = 1 ) {
		$this->count( $key, - 1, $sampleRate );
	}

	/**
	 * sends a count to statsd
	 *
	 * @param string $key
	 * @param int    $value
	 * @param int    $sampleRate (optional) the default is 1
	 *
	 * @return void
	 */
	public function count( $key, $value, $sampleRate = 1 ) {
		$this->_send( $key, (int) $value, 'c', $sampleRate );
	}

	/**
	 * sends a timing to statsd (in ms)
	 *
	 * @param string $key
	 * @param int    $value      the timing in ms
	 * @param int    $sampleRate the sample rate, if < 1, statsd will send an average timing
	 *
	 * @return void
	 */
	public function timing( $key, $value, $sampleRate = 1 ) {
		$this->_send( $key, (int) $value, 'ms', $sampleRate );
	}

	/**
	 * starts the timing for a key
	 *
	 * @param string $key
	 *
	 * @return void
	 */
	public function startTiming( $key ) {
		$this->_timings[ $key ] = gettimeofday( true );
	}

	/**
	 * ends the timing for a key and sends it to statsd
	 *
	 * @param string $key
	 * @param int    $sampleRate (optional)
	 *
	 * @return void
	 */
	public function endTiming( $key, $sampleRate = 1 ) {
		$end = gettimeofday( true );

		if ( array_key_exists( $key, $this->_timings ) ) {
			$timing = ( $end - $this->_timings[ $key ] ) * 1000;
			$this->timing( $key, $timing, $sampleRate );
			unset( $this->_timings[ $key ] );
		}
	}

	/**
	 * start memory "profiling"
	 *
	 * @param string $key
	 *
	 * @return void
	 */
	public function startMemoryProfile( $key ) {
		$this->_memoryProfiles[ $key ] = memory_get_usage();
	}

	/**
	 * ends the memory profiling and sends the value to the server
	 *
	 * @param string $key
	 * @param int    $sampleRate
	 *
	 * @return void
	 */
	public function endMemoryProfile( $key, $sampleRate = 1 ) {
		$end = memory_get_usage();

		if ( array_key_exists( $key, $this->_memoryProfiles ) ) {
			$memory = ( $end - $this->_memoryProfiles[ $key ] );
			$this->memory( $key, $memory, $sampleRate );

			unset( $this->_memoryProfiles[ $key ] );
		}
	}

	/**
	 * report memory usage to statsd. if memory was not given report peak usage
	 *
	 * @param string $key
	 * @param int    $memory
	 * @param int    $sampleRate
	 *
	 * @return void
	 */
	public function memory( $key, $memory = null, $sampleRate = 1 ) {
		if ( null === $memory ) {
			$memory = memory_get_peak_usage();
		}

		$this->count( $key, (int) $memory, $sampleRate );
	}

	/**
	 * executes a Closure and records it's execution time and sends it to statsd
	 * returns the value the Closure returned
	 *
	 * @param string   $key
	 * @param \Closure $_block
	 * @param int      $sampleRate (optional) default = 1
	 *
	 * @return mixed
	 */
	public function time( $key, Closure $_block, $sampleRate = 1 ) {
		$this->startTiming( $key );
		$return = $_block();
		$this->endTiming( $key, $sampleRate );

		return $return;
	}

	/**
	 * sends a gauge, an arbitrary value to StatsD
	 *
	 * @param string $key
	 * @param int    $value
	 *
	 * @return void
	 */
	public function gauge( $key, $value ) {
		$this->_send( $key, (int) $value, 'g', 1 );
	}

	/**
	 * actually sends a message to to the daemon and returns the sent message
	 *
	 * @param string $key
	 * @param int    $value
	 * @param string $type
	 * @param int    $sampleRate
	 *
	 * @return void
	 */
	protected function _send( $key, $value, $type, $sampleRate ) {
		if ( 0 != strlen( $this->_namespace ) ) {
			$key = sprintf( '%s.%s', $this->_namespace, $key );
		}

		$message     = sprintf( "%s:%d|%s", $key, $value, $type );
		$sampledData = '';

		if ( $sampleRate < 1 ) {
			$sample = mt_rand() / mt_getrandmax();

			if ( $sample <= $sampleRate || $this->_connection->forceSampling() ) {
				$sampledData = sprintf( '%s|@%s', $message, $sampleRate );
			}
		} else {
			$sampledData = $message;
		}

		if ( empty( $sampledData ) ) {
			return false;
		} //skip sending empty data

		if ( ! $this->_isBatch ) {
			$this->_connection->send( $sampledData );
		} else {
			$this->_batch[] = $sampledData;
		}
	}

	/**
	 * changes the global key namespace
	 *
	 * @param string $namespace
	 *
	 * @return void
	 */
	public function setNamespace( $namespace ) {
		$this->_namespace = (string) $namespace;
	}

	/**
	 * gets the global key namespace
	 *
	 * @return string
	 */
	public function getNamespace() {
		return $this->_namespace;
	}

	/**
	 * is batch processing running?
	 *
	 * @return boolean
	 */
	public function isBatch() {
		return $this->_isBatch;
	}

	/**
	 * start batch-send-recording
	 *
	 * @return void
	 */
	public function startBatch() {
		$this->_isBatch = true;
	}

	/**
	 * ends batch-send-recording and sends the recorded messages to the connection
	 *
	 * @return void
	 */
	public function endBatch() {
		$this->_isBatch = false;
		$this->_connection->send( join( "\n", $this->_batch ) );
		$this->_batch = array();
	}

	/**
	 * stops batch-recording and resets the batch
	 *
	 * @return void
	 */
	public function cancelBatch() {
		$this->_isBatch = false;
		$this->_batch   = array();
	}
}

/**
 * encapsulates the connection to the statsd service
 *
 * @author Dominik Liebler <liebler.dominik@googlemail.com>
 */
class StatsD_Connect {
	/**
	 * host name
	 *
	 * @var string
	 */
	protected $_host;

	/**
	 * port number
	 *
	 * @var int
	 */
	protected $_port;

	/**
	 * the used socket resource
	 *
	 * @var resource
	 */
	protected $_socket;

	/**
	 * is sampling allowed?
	 *
	 * @var bool
	 */
	protected $_forceSampling = false;

	/**
	 * instantiates the Connection object and a real connection to statsd
	 *
	 * @param string $host
	 * @param int    $port
	 */
	public function __construct( $host = 'localhost', $port = 8125 ) {
		$this->_host   = (string) $host;
		$this->_port   = (int) $port;
		$this->_socket = fsockopen( sprintf( "udp://%s", $this->_host ), $this->_port );
		stream_set_blocking( $this->_socket, 0 ); //set nonblocking
	}

	/**
	 * sends a message to the UDP socket
	 *
	 * @param $message
	 *
	 * @return void
	 */
	public function send( $message ) {
		if ( 0 != strlen( $message ) && $this->_socket ) {
			try {
				@fwrite( $this->_socket, $message );
				if ( defined( 'STATSD_DEBUG' ) && STATSD_DEBUG ) {
					global $statsd;
					$statsd->msgs[] = $message;
				}
			} catch ( Exception $e ) {
				// ignore it: stats logging failure shouldn't stop the whole app
			}
		}
	}

	/**
	 * @return string
	 */
	public function getHost() {
		return $this->_host;
	}


	/**
	 * @return int
	 */
	public function getPort() {
		return $this->_port;
	}

	/**
	 * is sampling forced?
	 *
	 * @return boolean
	 */
	public function forceSampling() {
		return (bool) $this->_forceSampling;
	}
}