1 |
|
2 |
|
3 | /**
|
4 | * Sends statistics to the stats daemon over UDP
|
5 | *
|
6 | **/
|
7 |
|
8 | class StatsD {
|
9 |
|
10 | /**
|
11 | * Sets one or more timing values
|
12 | *
|
13 | * @param string|array $stats The metric(s) to set.
|
14 | * @param float $time The elapsed time (ms) to log
|
15 | **/
|
16 | public static function timing($stats, $time) {
|
17 | StatsD::updateStats($stats, $time, 1, 'ms');
|
18 | }
|
19 |
|
20 | /**
|
21 | * Sets one or more gauges to a value
|
22 | *
|
23 | * @param string|array $stats The metric(s) to set.
|
24 | * @param float $value The value for the stats.
|
25 | **/
|
26 | public static function gauge($stats, $value) {
|
27 | StatsD::updateStats($stats, $value, 1, 'g');
|
28 | }
|
29 |
|
30 | /**
|
31 | * A "Set" is a count of unique events.
|
32 | * This data type acts like a counter, but supports counting
|
33 | * of unique occurences of values between flushes. The backend
|
34 | * receives the number of unique events that happened since
|
35 | * the last flush.
|
36 | *
|
37 | * The reference use case involved tracking the number of active
|
38 | * and logged in users by sending the current userId of a user
|
39 | * with each request with a key of "uniques" (or similar).
|
40 | *
|
41 | * @param string|array $stats The metric(s) to set.
|
42 | * @param float $value The value for the stats.
|
43 | **/
|
44 | public static function set($stats, $value) {
|
45 | StatsD::updateStats($stats, $value, 1, 's');
|
46 | }
|
47 |
|
48 | /**
|
49 | * Increments one or more stats counters
|
50 | *
|
51 | * @param string|array $stats The metric(s) to increment.
|
52 | * @param float|1 $sampleRate the rate (0-1) for sampling.
|
53 | * @return boolean
|
54 | **/
|
55 | public static function increment($stats, $sampleRate=1) {
|
56 | StatsD::updateStats($stats, 1, $sampleRate, 'c');
|
57 | }
|
58 |
|
59 | /**
|
60 | * Decrements one or more stats counters.
|
61 | *
|
62 | * @param string|array $stats The metric(s) to decrement.
|
63 | * @param float|1 $sampleRate the rate (0-1) for sampling.
|
64 | * @return boolean
|
65 | **/
|
66 | public static function decrement($stats, $sampleRate=1) {
|
67 | StatsD::updateStats($stats, -1, $sampleRate, 'c');
|
68 | }
|
69 |
|
70 | /**
|
71 | * Updates one or more stats.
|
72 | *
|
73 | * @param string|array $stats The metric(s) to update. Should be either a string or array of metrics.
|
74 | * @param int|1 $delta The amount to increment/decrement each metric by.
|
75 | * @param float|1 $sampleRate the rate (0-1) for sampling.
|
76 | * @param string|c $metric The metric type ("c" for count, "ms" for timing, "g" for gauge, "s" for set)
|
77 | * @return boolean
|
78 | **/
|
79 | public static function updateStats($stats, $delta=1, $sampleRate=1, $metric='c') {
|
80 | if (!is_array($stats)) { $stats = array($stats); }
|
81 | $data = array();
|
82 | foreach($stats as $stat) {
|
83 | $data[$stat] = "$delta|$metric";
|
84 | }
|
85 |
|
86 | StatsD::send($data, $sampleRate);
|
87 | }
|
88 |
|
89 | /*
|
90 | * Squirt the metrics over UDP
|
91 | **/
|
92 | public static function send($data, $sampleRate=1) {
|
93 | $config = Config::getInstance();
|
94 | if (! $config->isEnabled("statsd")) { return; }
|
95 |
|
96 | // sampling
|
97 | $sampledData = array();
|
98 |
|
99 | if ($sampleRate < 1) {
|
100 | foreach ($data as $stat => $value) {
|
101 | if ((mt_rand() / mt_getrandmax()) <= $sampleRate) {
|
102 | $sampledData[$stat] = "$value|@$sampleRate";
|
103 | }
|
104 | }
|
105 | } else {
|
106 | $sampledData = $data;
|
107 | }
|
108 |
|
109 | if (empty($sampledData)) { return; }
|
110 |
|
111 | // Wrap this in a try/catch - failures in any of this should be silently ignored
|
112 | try {
|
113 | $host = $config->getConfig("statsd.host");
|
114 | $port = $config->getConfig("statsd.port");
|
115 | $fp = fsockopen("udp://$host", $port, $errno, $errstr);
|
116 | if (! $fp) { return; }
|
117 | foreach ($sampledData as $stat => $value) {
|
118 | fwrite($fp, "$stat:$value");
|
119 | }
|
120 | fclose($fp);
|
121 | } catch (Exception $e) {
|
122 | }
|
123 | }
|
124 | }
|
125 |
|
126 | class Config
|
127 | {
|
128 | private static $_instance;
|
129 | private $_data;
|
130 |
|
131 | private function __construct()
|
132 | {
|
133 | $this->_data = parse_ini_file('statsd.ini', true);
|
134 | }
|
135 |
|
136 | public static function getInstance()
|
137 | {
|
138 | if (!self::$_instance) self::$_instance = new self();
|
139 |
|
140 | return self::$_instance;
|
141 | }
|
142 |
|
143 | public function isEnabled($section)
|
144 | {
|
145 | return isset($this->_data[$section]);
|
146 | }
|
147 |
|
148 | public function getConfig($name)
|
149 | {
|
150 | $name_array = explode('.', $name, 2);
|
151 |
|
152 | if (count($name_array) < 2) return;
|
153 |
|
154 | list($section, $param) = $name_array;
|
155 |
|
156 | if (!isset($this->_data[$section][$param])) return;
|
157 |
|
158 | return $this->_data[$section][$param];
|
159 | }
|
160 | }
|
161 |
|
162 | /* Config file example (put it into "statsd.ini"):
|
163 |
|
164 | [statsd]
|
165 | host = yourhost
|
166 | port = 8125
|
167 |
|
168 | */
|