Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php
index f5a34ae3d6..3e7abe6a9d 100644
--- a/src/aphront/configuration/AphrontApplicationConfiguration.php
+++ b/src/aphront/configuration/AphrontApplicationConfiguration.php
@@ -1,487 +1,500 @@
<?php
/**
* @task routing URI Routing
*/
abstract class AphrontApplicationConfiguration {
private $request;
private $host;
private $path;
private $console;
abstract public function getApplicationName();
abstract public function buildRequest();
abstract public function build404Controller();
abstract public function buildRedirectController($uri, $external);
final public function setRequest(AphrontRequest $request) {
$this->request = $request;
return $this;
}
final public function getRequest() {
return $this->request;
}
final public function getConsole() {
return $this->console;
}
final public function setConsole($console) {
$this->console = $console;
return $this;
}
final public function setHost($host) {
$this->host = $host;
return $this;
}
final public function getHost() {
return $this->host;
}
final public function setPath($path) {
$this->path = $path;
return $this;
}
final public function getPath() {
return $this->path;
}
public function willBuildRequest() {}
/**
* @phutil-external-symbol class PhabricatorStartup
*/
public static function runHTTPRequest(AphrontHTTPSink $sink) {
$multimeter = MultimeterControl::newInstance();
$multimeter->setEventContext('<http-init>');
$multimeter->setEventViewer('<none>');
PhabricatorEnv::initializeWebEnvironment();
$multimeter->setSampleRate(
PhabricatorEnv::getEnvConfig('debug.sample-rate'));
$debug_time_limit = PhabricatorEnv::getEnvConfig('debug.time-limit');
if ($debug_time_limit) {
PhabricatorStartup::setDebugTimeLimit($debug_time_limit);
}
// This is the earliest we can get away with this, we need env config first.
PhabricatorAccessLog::init();
$access_log = PhabricatorAccessLog::getLog();
PhabricatorStartup::setGlobal('log.access', $access_log);
$access_log->setData(
array(
'R' => AphrontRequest::getHTTPHeader('Referer', '-'),
'r' => idx($_SERVER, 'REMOTE_ADDR', '-'),
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'),
));
DarkConsoleXHProfPluginAPI::hookProfiler();
DarkConsoleErrorLogPluginAPI::registerErrorHandler();
$response = PhabricatorSetupCheck::willProcessRequest();
if ($response) {
PhabricatorStartup::endOutputCapture();
$sink->writeResponse($response);
return;
}
$host = AphrontRequest::getHTTPHeader('Host');
$path = $_REQUEST['__path__'];
switch ($host) {
default:
$config_key = 'aphront.default-application-configuration-class';
$application = PhabricatorEnv::newObjectFromConfig($config_key);
break;
}
$application->setHost($host);
$application->setPath($path);
$application->willBuildRequest();
$request = $application->buildRequest();
// Build the server URI implied by the request headers. If an administrator
// has not configured "phabricator.base-uri" yet, we'll use this to generate
// links.
$request_protocol = ($request->isHTTPS() ? 'https' : 'http');
$request_base_uri = "{$request_protocol}://{$host}/";
PhabricatorEnv::setRequestBaseURI($request_base_uri);
$access_log->setData(
array(
'U' => (string)$request->getRequestURI()->getPath(),
));
$write_guard = new AphrontWriteGuard(array($request, 'validateCSRF'));
$processing_exception = null;
try {
- $response = $application->processRequest($request, $access_log, $sink);
+ $response = $application->processRequest(
+ $request,
+ $access_log,
+ $sink,
+ $multimeter);
$response_code = $response->getHTTPResponseCode();
} catch (Exception $ex) {
$processing_exception = $ex;
$response_code = 500;
}
$write_guard->dispose();
$access_log->setData(
array(
'c' => $response_code,
'T' => PhabricatorStartup::getMicrosecondsSinceStart(),
));
+ $multimeter->newEvent(
+ MultimeterEvent::TYPE_REQUEST_TIME,
+ $multimeter->getEventContext(),
+ PhabricatorStartup::getMicrosecondsSinceStart());
+
$access_log->write();
$multimeter->saveEvents();
DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log);
// Add points to the rate limits for this request.
if (isset($_SERVER['REMOTE_ADDR'])) {
$user_ip = $_SERVER['REMOTE_ADDR'];
// The base score for a request allows users to make 30 requests per
// minute.
$score = (1000 / 30);
// If the user was logged in, let them make more requests.
if ($request->getUser() && $request->getUser()->getPHID()) {
$score = $score / 5;
}
PhabricatorStartup::addRateLimitScore($user_ip, $score);
}
if ($processing_exception) {
throw $processing_exception;
}
}
public function processRequest(
AphrontRequest $request,
PhutilDeferredLog $access_log,
- AphrontHTTPSink $sink) {
+ AphrontHTTPSink $sink,
+ MultimeterControl $multimeter) {
$this->setRequest($request);
list($controller, $uri_data) = $this->buildController();
+ $controller_class = get_class($controller);
$access_log->setData(
array(
- 'C' => get_class($controller),
+ 'C' => $controller_class,
));
+ $multimeter->setEventContext('web.'.$controller_class);
$request->setURIMap($uri_data);
$controller->setRequest($request);
// If execution throws an exception and then trying to render that
// exception throws another exception, we want to show the original
// exception, as it is likely the root cause of the rendering exception.
$original_exception = null;
try {
$response = $controller->willBeginExecution();
if ($request->getUser() && $request->getUser()->getPHID()) {
$access_log->setData(
array(
'u' => $request->getUser()->getUserName(),
'P' => $request->getUser()->getPHID(),
));
+ $multimeter->setEventViewer('user.'.$request->getUser()->getPHID());
}
if (!$response) {
$controller->willProcessRequest($uri_data);
$response = $controller->handleRequest($request);
}
} catch (Exception $ex) {
$original_exception = $ex;
$response = $this->handleException($ex);
}
try {
$response = $controller->didProcessRequest($response);
$response = $this->willSendResponse($response, $controller);
$response->setRequest($request);
$unexpected_output = PhabricatorStartup::endOutputCapture();
if ($unexpected_output) {
$unexpected_output = pht(
"Unexpected output:\n\n%s",
$unexpected_output);
phlog($unexpected_output);
if ($response instanceof AphrontWebpageResponse) {
echo phutil_tag(
'div',
array('style' =>
'background: #eeddff;'.
'white-space: pre-wrap;'.
'z-index: 200000;'.
'position: relative;'.
'padding: 8px;'.
'font-family: monospace',
),
$unexpected_output);
}
}
$sink->writeResponse($response);
} catch (Exception $ex) {
if ($original_exception) {
throw $original_exception;
}
throw $ex;
}
return $response;
}
/* -( URI Routing )-------------------------------------------------------- */
/**
* Using builtin and application routes, build the appropriate
* @{class:AphrontController} class for the request. To route a request, we
* first test if the HTTP_HOST is configured as a valid Phabricator URI. If
* it isn't, we do a special check to see if it's a custom domain for a blog
* in the Phame application and if that fails we error. Otherwise, we test
* against all application routes from installed
* @{class:PhabricatorApplication}s.
*
* If we match a route, we construct the controller it points at, build it,
* and return it.
*
* If we fail to match a route, but the current path is missing a trailing
* "/", we try routing the same path with a trailing "/" and do a redirect
* if that has a valid route. The idea is to canoncalize URIs for consistency,
* but avoid breaking noncanonical URIs that we can easily salvage.
*
* NOTE: We only redirect on GET. On POST, we'd drop parameters and most
* likely mutate the request implicitly, and a bad POST usually indicates a
* programming error rather than a sloppy typist.
*
* If the failing path already has a trailing "/", or we can't route the
* version with a "/", we call @{method:build404Controller}, which build a
* fallback @{class:AphrontController}.
*
* @return pair<AphrontController,dict> Controller and dictionary of request
* parameters.
* @task routing
*/
final public function buildController() {
$request = $this->getRequest();
// If we're configured to operate in cluster mode, reject requests which
// were not received on a cluster interface.
//
// For example, a host may have an internal address like "170.0.0.1", and
// also have a public address like "51.23.95.16". Assuming the cluster
// is configured on a range like "170.0.0.0/16", we want to reject the
// requests received on the public interface.
//
// Ideally, nodes in a cluster should only be listening on internal
// interfaces, but they may be configured in such a way that they also
// listen on external interfaces, since this is easy to forget about or
// get wrong. As a broad security measure, reject requests received on any
// interfaces which aren't on the whitelist.
$cluster_addresses = PhabricatorEnv::getEnvConfig('cluster.addresses');
if ($cluster_addresses) {
$server_addr = idx($_SERVER, 'SERVER_ADDR');
if (!$server_addr) {
if (php_sapi_name() == 'cli') {
// This is a command line script (probably something like a unit
// test) so it's fine that we don't have SERVER_ADDR defined.
} else {
throw new AphrontUsageException(
pht('No SERVER_ADDR'),
pht(
'Phabricator is configured to operate in cluster mode, but '.
'SERVER_ADDR is not defined in the request context. Your '.
'webserver configuration needs to forward SERVER_ADDR to '.
'PHP so Phabricator can reject requests received on '.
'external interfaces.'));
}
} else {
if (!PhabricatorEnv::isClusterAddress($server_addr)) {
throw new AphrontUsageException(
pht('External Interface'),
pht(
'Phabricator is configured in cluster mode and the address '.
'this request was received on ("%s") is not whitelisted as '.
'a cluster address.',
$server_addr));
}
}
}
if (PhabricatorEnv::getEnvConfig('security.require-https')) {
if (!$request->isHTTPS()) {
$https_uri = $request->getRequestURI();
$https_uri->setDomain($request->getHost());
$https_uri->setProtocol('https');
// In this scenario, we'll be redirecting to HTTPS using an absolute
// URI, so we need to permit an external redirect.
return $this->buildRedirectController($https_uri, true);
}
}
$path = $request->getPath();
$host = $request->getHost();
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
$prod_uri = PhabricatorEnv::getEnvConfig('phabricator.production-uri');
$file_uri = PhabricatorEnv::getEnvConfig(
'security.alternate-file-domain');
$allowed_uris = PhabricatorEnv::getEnvConfig('phabricator.allowed-uris');
$uris = array_merge(
array(
$base_uri,
$prod_uri,
),
$allowed_uris);
$cdn_routes = array(
'/res/',
'/file/data/',
'/file/xform/',
'/phame/r/',
);
$host_match = false;
foreach ($uris as $uri) {
if ($host === id(new PhutilURI($uri))->getDomain()) {
$host_match = true;
break;
}
}
if (!$host_match) {
if ($host === id(new PhutilURI($file_uri))->getDomain()) {
foreach ($cdn_routes as $route) {
if (strncmp($path, $route, strlen($route)) == 0) {
$host_match = true;
break;
}
}
}
}
// NOTE: If the base URI isn't defined yet, don't activate alternate
// domains.
if ($base_uri && !$host_match) {
try {
$blog = id(new PhameBlogQuery())
->setViewer(new PhabricatorUser())
->withDomain($host)
->executeOne();
} catch (PhabricatorPolicyException $ex) {
throw new Exception(
'This blog is not visible to logged out users, so it can not be '.
'visited from a custom domain.');
}
if (!$blog) {
if ($prod_uri && $prod_uri != $base_uri) {
$prod_str = ' or '.$prod_uri;
} else {
$prod_str = '';
}
throw new Exception(
'Specified domain '.$host.' is not configured for Phabricator '.
'requests. Please use '.$base_uri.$prod_str.' to visit this instance.'
);
}
// TODO: Make this more flexible and modular so any application can
// do crazy stuff here if it wants.
$path = '/phame/live/'.$blog->getID().'/'.$path;
}
list($controller, $uri_data) = $this->buildControllerForPath($path);
if (!$controller) {
if (!preg_match('@/$@', $path)) {
// If we failed to match anything but don't have a trailing slash, try
// to add a trailing slash and issue a redirect if that resolves.
list($controller, $uri_data) = $this->buildControllerForPath($path.'/');
// NOTE: For POST, just 404 instead of redirecting, since the redirect
// will be a GET without parameters.
if ($controller && !$request->isHTTPPost()) {
$slash_uri = $request->getRequestURI()->setPath($path.'/');
$external = strlen($request->getRequestURI()->getDomain());
return $this->buildRedirectController($slash_uri, $external);
}
}
return $this->build404Controller();
}
return array($controller, $uri_data);
}
/**
* Map a specific path to the corresponding controller. For a description
* of routing, see @{method:buildController}.
*
* @return pair<AphrontController,dict> Controller and dictionary of request
* parameters.
* @task routing
*/
final public function buildControllerForPath($path) {
$maps = array();
$applications = PhabricatorApplication::getAllInstalledApplications();
foreach ($applications as $application) {
$maps[] = array($application, $application->getRoutes());
}
$current_application = null;
$controller_class = null;
foreach ($maps as $map_info) {
list($application, $map) = $map_info;
$mapper = new AphrontURIMapper($map);
list($controller_class, $uri_data) = $mapper->mapPath($path);
if ($controller_class) {
if ($application) {
$current_application = $application;
}
break;
}
}
if (!$controller_class) {
return array(null, null);
}
$request = $this->getRequest();
$controller = newv($controller_class, array());
if ($current_application) {
$controller->setCurrentApplication($current_application);
}
return array($controller, $uri_data);
}
}
diff --git a/src/applications/multimeter/controller/MultimeterSampleController.php b/src/applications/multimeter/controller/MultimeterSampleController.php
index 1b60022eec..62a459f75b 100644
--- a/src/applications/multimeter/controller/MultimeterSampleController.php
+++ b/src/applications/multimeter/controller/MultimeterSampleController.php
@@ -1,284 +1,328 @@
<?php
final class MultimeterSampleController extends MultimeterController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$group_map = $this->getColumnMap();
$group = explode('.', $request->getStr('group'));
$group = array_intersect($group, array_keys($group_map));
$group = array_fuse($group);
if (empty($group['type'])) {
$group['type'] = 'type';
}
$now = PhabricatorTime::getNow();
$ago = ($now - phutil_units('24 hours in seconds'));
$table = new MultimeterEvent();
$conn = $table->establishConnection('r');
$where = array();
$where[] = qsprintf(
$conn,
'epoch >= %d AND epoch <= %d',
$ago,
$now);
$with = array();
foreach ($group_map as $key => $column) {
+
+ // Don't let non-admins filter by viewers, this feels a little too
+ // invasive of privacy.
+ if ($key == 'viewer') {
+ if (!$viewer->getIsAdmin()) {
+ continue;
+ }
+ }
+
$with[$key] = $request->getStrList($key);
if ($with[$key]) {
$where[] = qsprintf(
$conn,
'%T IN (%Ls)',
$column,
$with[$key]);
}
}
$where = '('.implode(') AND (', $where).')';
$data = queryfx_all(
$conn,
'SELECT *, count(*) N, SUM(sampleRate * resourceCost) as totalCost
FROM %T
WHERE %Q
GROUP BY %Q
ORDER BY totalCost DESC, MAX(id) DESC
LIMIT 100',
$table->getTableName(),
$where,
implode(', ', array_select_keys($group_map, $group)));
$this->loadDimensions($data);
+ $phids = array();
+ foreach ($data as $row) {
+ $viewer_name = $this->getViewerDimension($row['eventViewerID'])
+ ->getName();
+ $viewer_phid = $this->getEventViewerPHID($viewer_name);
+ if ($viewer_phid) {
+ $phids[] = $viewer_phid;
+ }
+ }
+ $handles = $viewer->loadHandles($phids);
$rows = array();
foreach ($data as $row) {
if (isset($group['request'])) {
$request_col = $row['requestKey'];
if (!$with['request']) {
$request_col = $this->renderSelectionLink(
'request',
$row['requestKey'],
$request_col);
}
} else {
$request_col = $this->renderGroupingLink($group, 'request');
}
if (isset($group['viewer'])) {
- $viewer_col = $this->getViewerDimension($row['eventViewerID'])
- ->getName();
- if (!$with['viewer']) {
- $viewer_col = $this->renderSelectionLink(
- 'viewer',
- $row['eventViewerID'],
- $viewer_col);
+ if ($viewer->getIsAdmin()) {
+ $viewer_col = $this->getViewerDimension($row['eventViewerID'])
+ ->getName();
+
+ $viewer_phid = $this->getEventViewerPHID($viewer_col);
+ if ($viewer_phid) {
+ $viewer_col = $handles[$viewer_phid]->getName();
+ }
+
+ if (!$with['viewer']) {
+ $viewer_col = $this->renderSelectionLink(
+ 'viewer',
+ $row['eventViewerID'],
+ $viewer_col);
+ }
+ } else {
+ $viewer_col = phutil_tag('em', array(), pht('(Masked)'));
}
} else {
$viewer_col = $this->renderGroupingLink($group, 'viewer');
}
if (isset($group['context'])) {
$context_col = $this->getContextDimension($row['eventContextID'])
->getName();
if (!$with['context']) {
$context_col = $this->renderSelectionLink(
'context',
$row['eventContextID'],
$context_col);
}
} else {
$context_col = $this->renderGroupingLink($group, 'context');
}
if (isset($group['host'])) {
$host_col = $this->getHostDimension($row['eventHostID'])
->getName();
if (!$with['host']) {
$host_col = $this->renderSelectionLink(
'host',
$row['eventHostID'],
$host_col);
}
} else {
$host_col = $this->renderGroupingLink($group, 'host');
}
if (isset($group['label'])) {
$label_col = $this->getLabelDimension($row['eventLabelID'])
->getName();
if (!$with['label']) {
$label_col = $this->renderSelectionLink(
'label',
$row['eventLabelID'],
$label_col);
}
} else {
$label_col = $this->renderGroupingLink($group, 'label');
}
if ($with['type']) {
$type_col = MultimeterEvent::getEventTypeName($row['eventType']);
} else {
$type_col = $this->renderSelectionLink(
'type',
$row['eventType'],
MultimeterEvent::getEventTypeName($row['eventType']));
}
$rows[] = array(
($row['N'] == 1)
? $row['id']
: pht('%s Events', new PhutilNumber($row['N'])),
$request_col,
$viewer_col,
$context_col,
$host_col,
$type_col,
$label_col,
+ MultimeterEvent::formatResourceCost(
+ $viewer,
+ $row['eventType'],
+ $row['totalCost'] / $row['N']),
MultimeterEvent::formatResourceCost(
$viewer,
$row['eventType'],
$row['totalCost']),
- $row['sampleRate'],
+ ($row['N'] == 1)
+ ? $row['sampleRate']
+ : '-',
phabricator_datetime($row['epoch'], $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('ID'),
pht('Request'),
pht('Viewer'),
pht('Context'),
pht('Host'),
pht('Type'),
pht('Label'),
+ pht('Avg'),
pht('Cost'),
pht('Rate'),
pht('Epoch'),
))
->setColumnClasses(
array(
'n',
null,
null,
null,
null,
null,
'wide',
'n',
'n',
+ 'n',
null,
));
$box = id(new PHUIObjectBoxView())
->setHeaderText(
pht(
'Samples (%s - %s)',
phabricator_datetime($ago, $viewer),
phabricator_datetime($now, $viewer)))
->appendChild($table);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Samples'),
$this->getGroupURI(array(), true));
$crumb_map = array(
'host' => pht('By Host'),
'context' => pht('By Context'),
'viewer' => pht('By Viewer'),
'request' => pht('By Request'),
'label' => pht('By Label'),
);
$parts = array();
foreach ($group as $item) {
if ($item == 'type') {
continue;
}
$parts[$item] = $item;
$crumbs->addTextCrumb(
idx($crumb_map, $item, $item),
$this->getGroupURI($parts, true));
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => pht('Samples'),
));
}
private function renderGroupingLink(array $group, $key) {
$group[] = $key;
$uri = $this->getGroupURI($group);
return phutil_tag(
'a',
array(
'href' => $uri,
'style' => 'font-weight: bold',
),
pht('(All)'));
}
private function getGroupURI(array $group, $wipe = false) {
unset($group['type']);
$uri = clone $this->getRequest()->getRequestURI();
$group = implode('.', $group);
if (!strlen($group)) {
$group = null;
}
$uri->setQueryParam('group', $group);
if ($wipe) {
foreach ($this->getColumnMap() as $key => $column) {
$uri->setQueryParam($key, null);
}
}
return $uri;
}
private function renderSelectionLink($key, $value, $link_text) {
$value = (array)$value;
$uri = clone $this->getRequest()->getRequestURI();
$uri->setQueryParam($key, implode(',', $value));
return phutil_tag(
'a',
array(
'href' => $uri,
),
$link_text);
}
private function getColumnMap() {
return array(
'type' => 'eventType',
'host' => 'eventHostID',
'context' => 'eventContextID',
'viewer' => 'eventViewerID',
'request' => 'requestKey',
'label' => 'eventLabelID',
);
}
+ private function getEventViewerPHID($viewer_name) {
+ if (!strncmp($viewer_name, 'user.', 5)) {
+ return substr($viewer_name, 5);
+ }
+ return null;
+ }
+
}
diff --git a/src/applications/multimeter/data/MultimeterControl.php b/src/applications/multimeter/data/MultimeterControl.php
index a7838ca859..9d948609a6 100644
--- a/src/applications/multimeter/data/MultimeterControl.php
+++ b/src/applications/multimeter/data/MultimeterControl.php
@@ -1,203 +1,207 @@
<?php
final class MultimeterControl {
private static $instance;
private $events = array();
private $sampleRate;
private $pauseDepth;
private $eventViewer;
private $eventContext;
private function __construct() {
// Private.
}
public static function newInstance() {
$instance = new MultimeterControl();
// NOTE: We don't set the sample rate yet. This allows the multimeter to
// be initialized and begin recording events, then make a decision about
// whether the page will be sampled or not later on (once we've loaded
// enough configuration).
self::$instance = $instance;
return self::getInstance();
}
public static function getInstance() {
return self::$instance;
}
public function isActive() {
return ($this->sampleRate !== 0) && ($this->pauseDepth == 0);
}
public function setSampleRate($rate) {
if ($rate && (mt_rand(1, $rate) == $rate)) {
$sample_rate = $rate;
} else {
$sample_rate = 0;
}
$this->sampleRate = $sample_rate;
return;
}
public function pauseMultimeter() {
$this->pauseDepth++;
return $this;
}
public function unpauseMultimeter() {
if (!$this->pauseDepth) {
throw new Exception(pht('Trying to unpause an active multimeter!'));
}
$this->pauseDepth--;
return $this;
}
public function newEvent($type, $label, $cost) {
if (!$this->isActive()) {
return null;
}
$event = id(new MultimeterEvent())
->setEventType($type)
->setEventLabel($label)
->setResourceCost($cost)
->setEpoch(PhabricatorTime::getNow());
$this->events[] = $event;
return $event;
}
public function saveEvents() {
if (!$this->isActive()) {
return;
}
$events = $this->events;
if (!$events) {
return;
}
if ($this->sampleRate === null) {
throw new Exception(pht('Call setSampleRate() before saving events!'));
}
// Don't sample any of this stuff.
$this->pauseMultimeter();
$use_scope = AphrontWriteGuard::isGuardActive();
if ($use_scope) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
} else {
AphrontWriteGuard::allowDangerousUnguardedWrites(true);
}
$caught = null;
try {
$this->writeEvents();
} catch (Exception $ex) {
$caught = $ex;
}
if ($use_scope) {
unset($unguarded);
} else {
AphrontWriteGuard::allowDangerousUnguardedWrites(false);
}
$this->unpauseMultimeter();
if ($caught) {
throw $caught;
}
}
private function writeEvents() {
$events = $this->events;
$random = Filesystem::readRandomBytes(32);
$request_key = PhabricatorHash::digestForIndex($random);
$host_id = $this->loadHostID(php_uname('n'));
$context_id = $this->loadEventContextID($this->eventContext);
$viewer_id = $this->loadEventViewerID($this->eventViewer);
$label_map = $this->loadEventLabelIDs(mpull($events, 'getEventLabel'));
foreach ($events as $event) {
$event
->setRequestKey($request_key)
->setSampleRate($this->sampleRate)
->setEventHostID($host_id)
->setEventContextID($context_id)
->setEventViewerID($viewer_id)
->setEventLabelID($label_map[$event->getEventLabel()])
->save();
}
}
public function setEventContext($event_context) {
$this->eventContext = $event_context;
return $this;
}
+ public function getEventContext() {
+ return $this->eventContext;
+ }
+
public function setEventViewer($viewer) {
$this->eventViewer = $viewer;
return $this;
}
private function loadHostID($host) {
$map = $this->loadDimensionMap(new MultimeterHost(), array($host));
return idx($map, $host);
}
private function loadEventViewerID($viewer) {
$map = $this->loadDimensionMap(new MultimeterViewer(), array($viewer));
return idx($map, $viewer);
}
private function loadEventContextID($context) {
$map = $this->loadDimensionMap(new MultimeterContext(), array($context));
return idx($map, $context);
}
private function loadEventLabelIDs(array $labels) {
return $this->loadDimensionMap(new MultimeterLabel(), $labels);
}
private function loadDimensionMap(MultimeterDimension $table, array $names) {
$hashes = array();
foreach ($names as $name) {
$hashes[] = PhabricatorHash::digestForIndex($name);
}
$objects = $table->loadAllWhere('nameHash IN (%Ls)', $hashes);
$map = mpull($objects, 'getID', 'getName');
$need = array();
foreach ($names as $name) {
if (isset($map[$name])) {
continue;
}
$need[] = $name;
}
foreach ($need as $name) {
$object = id(clone $table)
->setName($name)
->save();
$map[$name] = $object->getID();
}
return $map;
}
}
diff --git a/src/applications/multimeter/storage/MultimeterEvent.php b/src/applications/multimeter/storage/MultimeterEvent.php
index 1f7c27cbaa..23263a6e8d 100644
--- a/src/applications/multimeter/storage/MultimeterEvent.php
+++ b/src/applications/multimeter/storage/MultimeterEvent.php
@@ -1,71 +1,76 @@
<?php
final class MultimeterEvent extends MultimeterDAO {
const TYPE_STATIC_RESOURCE = 0;
+ const TYPE_REQUEST_TIME = 1;
protected $eventType;
protected $eventLabelID;
protected $resourceCost;
protected $sampleRate;
protected $eventContextID;
protected $eventHostID;
protected $eventViewerID;
protected $epoch;
protected $requestKey;
private $eventLabel;
public function setEventLabel($event_label) {
$this->eventLabel = $event_label;
return $this;
}
public function getEventLabel() {
return $this->eventLabel;
}
public static function getEventTypeName($type) {
switch ($type) {
case self::TYPE_STATIC_RESOURCE:
return pht('Static Resource');
+ case self::TYPE_REQUEST_TIME:
+ return pht('Web Request');
}
return pht('Unknown ("%s")', $type);
}
public static function formatResourceCost(
PhabricatorUser $viewer,
$type,
$cost) {
switch ($type) {
case self::TYPE_STATIC_RESOURCE:
return pht('%s Req', new PhutilNumber($cost));
+ case self::TYPE_REQUEST_TIME:
+ return pht('%s us', new PhutilNumber($cost));
}
return pht('%s Unit(s)', new PhutilNumber($cost));
}
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_COLUMN_SCHEMA => array(
'eventType' => 'uint32',
'resourceCost' => 'sint64',
'sampleRate' => 'uint32',
'requestKey' => 'bytes12',
),
self::CONFIG_KEY_SCHEMA => array(
'key_request' => array(
'columns' => array('requestKey'),
),
'key_type' => array(
'columns' => array('eventType', 'epoch'),
),
),
) + parent::getConfiguration();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Apr 29, 4:36 PM (23 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
108452
Default Alt Text
(32 KB)

Event Timeline