@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
at upstream/main 350 lines 8.8 kB view raw
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}