@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class MultimeterSampleController extends MultimeterController {
4
5 public function shouldAllowPublic() {
6 return true;
7 }
8
9 public function handleRequest(AphrontRequest $request) {
10 $viewer = $this->getViewer();
11 $group_map = $this->getColumnMap();
12
13 $group = explode('.', $request->getStr('group', ''));
14 $group = array_intersect($group, array_keys($group_map));
15 $group = array_fuse($group);
16
17 if (empty($group['type'])) {
18 $group['type'] = 'type';
19 }
20
21 $now = PhabricatorTime::getNow();
22 $ago = ($now - phutil_units('24 hours in seconds'));
23
24 $table = new MultimeterEvent();
25 $conn = $table->establishConnection('r');
26
27 $where = array();
28 $where[] = qsprintf(
29 $conn,
30 'epoch >= %d AND epoch <= %d',
31 $ago,
32 $now);
33
34 $with = array();
35 foreach ($group_map as $key => $column) {
36
37 // Don't let non-admins filter by viewers, this feels a little too
38 // invasive of privacy.
39 if ($key == 'viewer') {
40 if (!$viewer->getIsAdmin()) {
41 continue;
42 }
43 }
44
45 $with[$key] = $request->getStrList($key);
46 if ($with[$key]) {
47 $where[] = qsprintf(
48 $conn,
49 '%T IN (%Ls)',
50 $column,
51 $with[$key]);
52 }
53 }
54
55 $data = queryfx_all(
56 $conn,
57 'SELECT *,
58 count(*) AS N,
59 SUM(sampleRate * resourceCost) AS totalCost,
60 SUM(sampleRate * resourceCost) / SUM(sampleRate) AS averageCost
61 FROM %T
62 WHERE %LA
63 GROUP BY %LC
64 ORDER BY totalCost DESC, MAX(id) DESC
65 LIMIT 100',
66 $table->getTableName(),
67 $where,
68 array_select_keys($group_map, $group));
69
70 $this->loadDimensions($data);
71 $phids = array();
72 foreach ($data as $row) {
73 $viewer_name = $this->getViewerDimension($row['eventViewerID'])
74 ->getName();
75 $viewer_phid = $this->getEventViewerPHID($viewer_name);
76 if ($viewer_phid) {
77 $phids[] = $viewer_phid;
78 }
79 }
80 $handles = $viewer->loadHandles($phids);
81
82 $rows = array();
83 foreach ($data as $row) {
84
85 if ($row['N'] == 1) {
86 $events_col = $row['id'];
87 } else {
88 $events_col = $this->renderGroupingLink(
89 $group,
90 'id',
91 pht('%s Event(s)', new PhutilNumber($row['N'])));
92 }
93
94 if (isset($group['request'])) {
95 $request_col = $row['requestKey'];
96 if (!$with['request']) {
97 $request_col = $this->renderSelectionLink(
98 'request',
99 $row['requestKey'],
100 $request_col);
101 }
102 } else {
103 $request_col = $this->renderGroupingLink($group, 'request');
104 }
105
106 if (isset($group['viewer'])) {
107 if ($viewer->getIsAdmin()) {
108 $viewer_col = $this->getViewerDimension($row['eventViewerID'])
109 ->getName();
110
111 $viewer_phid = $this->getEventViewerPHID($viewer_col);
112 if ($viewer_phid) {
113 $viewer_col = $handles[$viewer_phid]->getName();
114 }
115
116 if (!$with['viewer']) {
117 $viewer_col = $this->renderSelectionLink(
118 'viewer',
119 $row['eventViewerID'],
120 $viewer_col);
121 }
122 } else {
123 $viewer_col = phutil_tag('em', array(), pht('(Masked)'));
124 }
125 } else {
126 $viewer_col = $this->renderGroupingLink($group, 'viewer');
127 }
128
129 if (isset($group['context'])) {
130 $context_col = $this->getContextDimension($row['eventContextID'])
131 ->getName();
132 if (!$with['context']) {
133 $context_col = $this->renderSelectionLink(
134 'context',
135 $row['eventContextID'],
136 $context_col);
137 }
138 } else {
139 $context_col = $this->renderGroupingLink($group, 'context');
140 }
141
142 if (isset($group['host'])) {
143 $host_col = $this->getHostDimension($row['eventHostID'])
144 ->getName();
145 if (!$with['host']) {
146 $host_col = $this->renderSelectionLink(
147 'host',
148 $row['eventHostID'],
149 $host_col);
150 }
151 } else {
152 $host_col = $this->renderGroupingLink($group, 'host');
153 }
154
155 if (isset($group['label'])) {
156 $label_col = $this->getLabelDimension($row['eventLabelID'])
157 ->getName();
158 if (!$with['label']) {
159 $label_col = $this->renderSelectionLink(
160 'label',
161 $row['eventLabelID'],
162 $label_col);
163 }
164 } else {
165 $label_col = $this->renderGroupingLink($group, 'label');
166 }
167
168 if ($with['type']) {
169 $type_col = MultimeterEvent::getEventTypeName($row['eventType']);
170 } else {
171 $type_col = $this->renderSelectionLink(
172 'type',
173 $row['eventType'],
174 MultimeterEvent::getEventTypeName($row['eventType']));
175 }
176
177 $rows[] = array(
178 $events_col,
179 $request_col,
180 $viewer_col,
181 $context_col,
182 $host_col,
183 $type_col,
184 $label_col,
185 MultimeterEvent::formatResourceCost(
186 $viewer,
187 $row['eventType'],
188 $row['averageCost']),
189 MultimeterEvent::formatResourceCost(
190 $viewer,
191 $row['eventType'],
192 $row['totalCost']),
193 ($row['N'] == 1)
194 ? $row['sampleRate']
195 : '-',
196 phabricator_datetime($row['epoch'], $viewer),
197 );
198 }
199
200 $table = id(new AphrontTableView($rows))
201 ->setHeaders(
202 array(
203 pht('ID'),
204 pht('Request'),
205 pht('Viewer'),
206 pht('Context'),
207 pht('Host'),
208 pht('Type'),
209 pht('Label'),
210 pht('Avg'),
211 pht('Cost'),
212 pht('Rate'),
213 pht('Epoch'),
214 ))
215 ->setColumnClasses(
216 array(
217 null,
218 null,
219 null,
220 null,
221 null,
222 null,
223 'wide',
224 'n',
225 'n',
226 'n',
227 null,
228 ));
229
230 $box = id(new PHUIObjectBoxView())
231 ->setHeaderText(pht('Samples'))
232 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
233 ->setTable($table);
234
235 $crumbs = $this->buildApplicationCrumbs();
236 $crumbs->addTextCrumb(
237 pht('Samples'),
238 $this->getGroupURI(array(), true));
239 $crumbs->setBorder(true);
240
241 $crumb_map = array(
242 'host' => pht('By Host'),
243 'context' => pht('By Context'),
244 'viewer' => pht('By Viewer'),
245 'request' => pht('By Request'),
246 'label' => pht('By Label'),
247 'id' => pht('By ID'),
248 );
249
250 $parts = array();
251 foreach ($group as $item) {
252 if ($item == 'type') {
253 continue;
254 }
255 $parts[$item] = $item;
256 $crumbs->addTextCrumb(
257 idx($crumb_map, $item, $item),
258 $this->getGroupURI($parts, true));
259 }
260
261 $header = id(new PHUIHeaderView())
262 ->setHeader(
263 pht(
264 'Samples (%s - %s)',
265 phabricator_datetime($ago, $viewer),
266 phabricator_datetime($now, $viewer)))
267 ->setHeaderIcon('fa-motorcycle');
268
269 $view = id(new PHUITwoColumnView())
270 ->setHeader($header)
271 ->setFooter($box);
272
273 return $this->newPage()
274 ->setTitle(pht('Samples'))
275 ->setCrumbs($crumbs)
276 ->appendChild($view);
277
278 }
279
280 private function renderGroupingLink(array $group, $key, $name = null) {
281 $group[] = $key;
282 $uri = $this->getGroupURI($group);
283
284 if ($name === null) {
285 $name = pht('(All)');
286 }
287
288 return phutil_tag(
289 'a',
290 array(
291 'href' => $uri,
292 'style' => 'font-weight: bold',
293 ),
294 $name);
295 }
296
297 private function getGroupURI(array $group, $wipe = false) {
298 unset($group['type']);
299 $uri = clone $this->getRequest()->getRequestURI();
300
301 $group = implode('.', $group);
302 if (!strlen($group)) {
303 $uri->removeQueryParam('group');
304 } else {
305 $uri->replaceQueryParam('group', $group);
306 }
307
308 if ($wipe) {
309 foreach ($this->getColumnMap() as $key => $column) {
310 $uri->removeQueryParam($key);
311 }
312 }
313
314 return $uri;
315 }
316
317 private function renderSelectionLink($key, $value, $link_text) {
318 $value = (array)$value;
319
320 $uri = clone $this->getRequest()->getRequestURI();
321 $uri->replaceQueryParam($key, implode(',', $value));
322
323 return phutil_tag(
324 'a',
325 array(
326 'href' => $uri,
327 ),
328 $link_text);
329 }
330
331 private function getColumnMap() {
332 return array(
333 'type' => 'eventType',
334 'host' => 'eventHostID',
335 'context' => 'eventContextID',
336 'viewer' => 'eventViewerID',
337 'request' => 'requestKey',
338 'label' => 'eventLabelID',
339 'id' => 'id',
340 );
341 }
342
343 private function getEventViewerPHID($viewer_name) {
344 if (!strncmp($viewer_name, 'user.', 5)) {
345 return substr($viewer_name, 5);
346 }
347 return null;
348 }
349
350}