Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
76.00% |
38 / 50 |
|
40.00% |
2 / 5 |
CRAP | |
0.00% |
0 / 1 |
SamplingStatsdClient | |
77.55% |
38 / 49 |
|
40.00% |
2 / 5 |
32.07 | |
0.00% |
0 / 1 |
setSamplingRates | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
appendSampleRate | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
6 | |||
send | |
68.00% |
17 / 25 |
|
0.00% |
0 / 1 |
13.28 | |||
sampleData | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
6.04 | |||
throwException | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * Copyright 2015 |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | */ |
22 | |
23 | namespace Wikimedia\Stats; |
24 | |
25 | use Exception; |
26 | use InvalidArgumentException; |
27 | use Liuggio\StatsdClient\Entity\StatsdData; |
28 | use Liuggio\StatsdClient\Entity\StatsdDataInterface; |
29 | use Liuggio\StatsdClient\StatsdClient; |
30 | use LogicException; |
31 | use Wikimedia\RequestTimeout\TimeoutException; |
32 | |
33 | /** |
34 | * A statsd client that applies the sampling rate to the data items before sending them. |
35 | * |
36 | * @deprecated since 1.43 No longer used after $wgSamplingStatsdClient was removed. |
37 | * @since 1.26 |
38 | */ |
39 | class SamplingStatsdClient extends StatsdClient { |
40 | /** @var array */ |
41 | protected $samplingRates = []; |
42 | |
43 | /** |
44 | * Sampling rates as an associative array of patterns and rates. |
45 | * Patterns are Unix shell patterns (e.g. 'MediaWiki.api.*'). |
46 | * Rates are sampling probabilities (e.g. 0.1 means 1 in 10 events are sampled). |
47 | * @param array $samplingRates |
48 | * @since 1.28 |
49 | */ |
50 | public function setSamplingRates( array $samplingRates ) { |
51 | $this->samplingRates = $samplingRates; |
52 | } |
53 | |
54 | /** |
55 | * Sets sampling rate for all items in $data. |
56 | * The sample rate specified in a StatsdData entity overrides the sample rate specified here. |
57 | * |
58 | * @inheritDoc |
59 | */ |
60 | public function appendSampleRate( $data, $sampleRate = 1 ) { |
61 | $samplingRates = $this->samplingRates; |
62 | if ( !$samplingRates && $sampleRate !== 1 ) { |
63 | $samplingRates = [ '*' => $sampleRate ]; |
64 | } |
65 | if ( $samplingRates ) { |
66 | array_walk( $data, static function ( $item ) use ( $samplingRates ) { |
67 | /** @var StatsdData $item */ |
68 | foreach ( $samplingRates as $pattern => $rate ) { |
69 | if ( fnmatch( $pattern, $item->getKey(), FNM_NOESCAPE ) ) { |
70 | $item->setSampleRate( $item->getSampleRate() * $rate ); |
71 | break; |
72 | } |
73 | } |
74 | } ); |
75 | } |
76 | |
77 | return $data; |
78 | } |
79 | |
80 | /** |
81 | * Send the metrics over UDP |
82 | * Sample the metrics according to their sample rate and send the remaining ones. |
83 | * |
84 | * @param StatsdDataInterface|StatsdDataInterface[] $data message(s) to sent |
85 | * strings are not allowed here as sampleData requires a StatsdDataInterface |
86 | * @param int $sampleRate |
87 | * |
88 | * @return int the data sent in bytes |
89 | */ |
90 | public function send( $data, $sampleRate = 1 ) { |
91 | if ( !is_array( $data ) ) { |
92 | $data = [ $data ]; |
93 | } |
94 | if ( !$data ) { |
95 | return 0; |
96 | } |
97 | foreach ( $data as $item ) { |
98 | if ( !( $item instanceof StatsdDataInterface ) ) { |
99 | throw new InvalidArgumentException( |
100 | 'SamplingStatsdClient does not accept stringified messages' ); |
101 | } |
102 | } |
103 | |
104 | // add sampling |
105 | $data = $this->appendSampleRate( $data, $sampleRate ); |
106 | $data = $this->sampleData( $data ); |
107 | |
108 | $data = array_map( 'strval', $data ); |
109 | |
110 | // reduce number of packets |
111 | if ( $this->getReducePacket() ) { |
112 | $data = $this->reduceCount( $data ); |
113 | } |
114 | |
115 | // failures in any of this should be silently ignored if .. |
116 | $written = 0; |
117 | try { |
118 | $fp = $this->getSender()->open(); |
119 | if ( !$fp ) { |
120 | return 0; |
121 | } |
122 | foreach ( $data as $message ) { |
123 | $written += $this->getSender()->write( $fp, $message ); |
124 | } |
125 | $this->getSender()->close( $fp ); |
126 | } catch ( TimeoutException $e ) { |
127 | throw $e; |
128 | } catch ( Exception $e ) { |
129 | $this->throwException( $e ); |
130 | } |
131 | |
132 | return $written; |
133 | } |
134 | |
135 | /** |
136 | * Throw away some of the data according to the sample rate. |
137 | * @param StatsdDataInterface[] $data |
138 | * @return StatsdDataInterface[] |
139 | */ |
140 | protected function sampleData( $data ) { |
141 | $newData = []; |
142 | $mt_rand_max = mt_getrandmax(); |
143 | foreach ( $data as $item ) { |
144 | $samplingRate = $item->getSampleRate(); |
145 | if ( $samplingRate <= 0.0 || $samplingRate > 1.0 ) { |
146 | throw new LogicException( 'Sampling rate shall be within ]0, 1]' ); |
147 | } |
148 | if ( |
149 | $samplingRate === 1 || |
150 | ( mt_rand() / $mt_rand_max <= $samplingRate ) |
151 | ) { |
152 | $newData[] = $item; |
153 | } |
154 | } |
155 | return $newData; |
156 | } |
157 | |
158 | /** |
159 | * Ideally this would override parent::throwException(), but that is private, |
160 | * not protected. |
161 | */ |
162 | private function throwException( Exception $exception ) { |
163 | if ( !$this->getFailSilently() ) { |
164 | throw $exception; |
165 | } |
166 | } |
167 | } |
168 | |
169 | /** @deprecated class alias since 1.43 */ |
170 | class_alias( SamplingStatsdClient::class, 'SamplingStatsdClient' ); |