PluginProbe
ElasticPress / 4.5.1
ElasticPress v4.5.1
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / includes / classes / Stats.php

Stats.php in ElasticPress 4.5.1, at includes/classes/Stats.php

311 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ElasticPress index health stats page handler
4 *
5 * @since 3.0
6 * @package elasticpress
7 */
8
9 namespace ElasticPress;
10
11 use ElasticPress\Utils as Utils;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 /**
18 * Class Stats
19 *
20 * @package ElasticPress
21 */
22 class Stats {
23 /**
24 * Index list with health data of current cluster
25 *
26 * @var array
27 * @since 3.0
28 */
29 protected $health = [];
30
31 /**
32 * Stats retrieved directly from the current cluster
33 *
34 * @var array
35 * @since 3.x
36 */
37 protected $stats = [];
38
39 /**
40 * Overall stats of the cluster
41 *
42 * @var array
43 * @since 3.2
44 */
45 protected $totals = [
46 'size' => 0,
47 'memory' => 0,
48 'docs' => 0,
49 ];
50
51 /**
52 * Later localized data.
53 *
54 * Used for chart building purposes
55 *
56 * @var array
57 * @since 3.2
58 */
59 protected $localized = [
60 'index_total' => 0,
61 'query_total' => 0,
62 'suggest_total' => 0,
63 'indices_data' => [],
64 ];
65
66 /**
67 * Cluster node data.
68 *
69 * Used to determine cluster health
70 *
71 * @var int
72 * @since 3.2
73 */
74 protected $nodes = 0;
75
76 /**
77 * Makes an api call to elasticsearch endpoint
78 *
79 * @param string $path Endpoint path to query
80 * @since 3.2
81 * @return array|mixed|object
82 */
83 protected function remote_request_helper( $path ) {
84 $request = Elasticsearch::factory()->remote_request( $path );
85
86 if ( is_wp_error( $request ) || empty( $request ) ) {
87 return false;
88 }
89
90 $body = wp_remote_retrieve_body( $request );
91
92 return json_decode( $body, true );
93 }
94
95 /**
96 * Makes api calls and organizes data depending on the specified context.
97 *
98 * @param boolean $force Force stats to be built even if cached
99 * @since 3.2
100 */
101 public function build_stats( $force = false ) {
102 static $stats_built = false;
103
104 if ( $stats_built && ! $force ) {
105 return;
106 }
107
108 $stats_built = true;
109
110 $this->stats = $this->remote_request_helper( '_stats?format=json' );
111
112 if ( empty( $this->stats ) || empty( $this->stats['_all'] ) || empty( $this->stats['_all']['total'] ) ) {
113 return;
114 }
115
116 $this->populate_indices_stats();
117
118 if ( Utils\is_epio() ) {
119 $node_stats = $this->remote_request_helper( '_nodes/stats/discovery?format=json' );
120 } else {
121 $node_stats = $this->remote_request_helper( '_nodes/stats?format=json' );
122 }
123
124 if ( ! empty( $node_stats ) ) {
125 $this->nodes = $node_stats['_nodes']['total'];
126 }
127 }
128
129 /**
130 * Populate the instantiated object with the correct indices, based on context
131 *
132 * @since 3.x
133 */
134 private function populate_indices_stats() {
135 $network_activated = defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK;
136 $blog_id = get_current_blog_id();
137 $site_indices = $this->get_indices_for_site( $blog_id );
138
139 $indices = $this->remote_request_helper( '_cat/indices?format=json' );
140
141 if ( empty( $indices ) ) {
142 return;
143 }
144
145 // If the plugin is network activated we only want the data from the indexable WP indexes, not any others.
146 if ( $network_activated ) {
147 $indexable_sites = Utils\get_sites();
148 foreach ( $indexable_sites as $site ) {
149 $indexables = $this->get_indices_for_site( $site['blog_id'] );
150 $site_indices = array_merge( $site_indices, $indexables );
151 }
152 }
153
154 // Filter the general list of indices to contain only the ones we care about.
155 $filtered_indices = array_filter(
156 $indices,
157 function ( $index ) use ( $site_indices ) {
158 return in_array( $index['index'], $site_indices, true );
159 }
160 );
161
162 /**
163 * Allow sites to select which indices will be displayed in the Index Health page
164 *
165 * @param {array} $filtered_indices Indices filtered to the site(s) being queried.
166 * @param {array} $indices All indices returned from Elasticsearch
167 *
168 * @return {array} List of indices to use
169 *
170 * @since 3.x
171 * @hook ep_index_health_stats_indices
172 */
173 $filtered_indices = apply_filters( 'ep_index_health_stats_indices', $filtered_indices, $indices );
174
175 foreach ( $filtered_indices as $index ) {
176 $this->populate_index_stats( $index['index'], $index['health'] );
177 }
178 }
179
180 /**
181 * Get all registered index names for a given site ID
182 *
183 * @param int $site_id the site id
184 *
185 * @return array
186 * @since 3.x
187 */
188 public function get_indices_for_site( $site_id ) {
189 $indexables = Indexables::factory()->get_all();
190 $indices = array();
191
192 foreach ( $indexables as $indexable ) {
193 $indices[] = $indexable->get_index_name( $site_id );
194 }
195
196 return $indices;
197 }
198
199 /**
200 * Populate index storage capacity and metrics
201 * Note: in the numbers below, those using the total key are counting values across all primary and replica shards
202 * while those using the primaries key are reading only from the primary shards
203 *
204 * @param string $index_name index name
205 * @param string $health index health status
206 *
207 * @since 3.x
208 */
209 private function populate_index_stats( $index_name, $health ) {
210
211 if ( empty( $this->stats['indices'][ $index_name ] ) ) {
212 return;
213 }
214
215 // Index-specific data
216 $this->health[ $index_name ]['name'] = $index_name;
217 $this->health[ $index_name ]['health'] = $health;
218
219 $this->localized['indices_data'][ $index_name ]['name'] = $index_name;
220 $this->localized['indices_data'][ $index_name ]['docs'] = $this->stats['indices'][ $index_name ]['primaries']['docs']['count'];
221
222 // General data counts
223 $this->localized['index_total'] += absint( $this->stats['indices'][ $index_name ]['primaries']['indexing']['index_total'] );
224 $this->localized['query_total'] += absint( $this->stats['indices'][ $index_name ]['total']['search']['query_total'] );
225 $this->localized['suggest_total'] += absint( $this->stats['indices'][ $index_name ]['total']['search']['suggest_total'] );
226
227 $this->totals['docs'] += absint( $this->stats['indices'][ $index_name ]['primaries']['docs']['count'] );
228 $this->totals['size'] += absint( $this->stats['indices'][ $index_name ]['total']['store']['size_in_bytes'] );
229 $this->totals['memory'] += absint( $this->stats['indices'][ $index_name ]['total']['segments']['memory_in_bytes'] );
230 }
231
232 /**
233 * Get index list and health data of an elasticsearch endpoint
234 *
235 * @return array
236 * @since 3.2
237 */
238 public function get_health() {
239 $this->build_stats();
240 return $this->health;
241 }
242
243 /**
244 * Get number of nodes in the current cluster
245 *
246 * @since 3.2
247 * @return int
248 */
249 public function get_nodes() {
250 $this->build_stats();
251 return $this->nodes;
252 }
253
254 /**
255 * Gets relevant total data of an elasticsearch endpoint
256 *
257 * @since 3.2
258 * @return array
259 */
260 public function get_totals() {
261 $this->build_stats();
262 return $this->totals;
263 }
264
265 /**
266 * Gets localized data
267 *
268 * @since 3.2
269 * @return mixed Data used in localization for chart creation.
270 */
271 public function get_localized() {
272 $this->build_stats();
273 return $this->localized;
274 }
275
276 /**
277 * Converts a number to a readable size format.
278 *
279 * @param int $size Desired number to convert
280 * @since 3.2
281 * @return string Size with appended unit
282 */
283 public function convert_to_readable_size( $size ) {
284 if ( empty( $size ) ) {
285 return 0;
286 }
287
288 $base = log( $size ) / log( 1024 );
289 $suffix = array( '', 'KB', 'MB', 'GB', 'TB' );
290 $f_base = floor( $base );
291
292 return round( pow( 1024, $base - floor( $base ) ), 1 ) . $suffix[ $f_base ];
293 }
294
295 /**
296 * Return singleton instance of class
297 *
298 * @return self
299 * @since 3.2
300 */
301 public static function factory() {
302 static $instance = false;
303
304 if ( ! $instance ) {
305 $instance = new self();
306 }
307
308 return $instance;
309 }
310 }
311