1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-19 05:12:41 +01:00

Add an "Event" plugin to DarkConsole for event inspection

Summary: Shows events which a page dispatched, plus all the registered
listeners.

Test Plan:
Pretty basic for now, but works OK:

https://secure.phabricator.com/file/view/PHID-FILE-49fcd23081ce55cf9369/

(I also made it dispatch some dummy events to verify they show up.)

Reviewers: aran

Reviewed By: aran

CC: aran

Differential Revision: 973
This commit is contained in:
epriestley 2011-09-30 12:54:17 -07:00
parent fc5cc180ab
commit 1b8562467c
12 changed files with 250 additions and 0 deletions

View file

@ -140,6 +140,8 @@ phutil_register_library_map(array(
'DarkConsoleCore' => 'aphront/console/core',
'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/errorlog',
'DarkConsoleErrorLogPluginAPI' => 'aphront/console/plugin/errorlog/api',
'DarkConsoleEventPlugin' => 'aphront/console/plugin/event',
'DarkConsoleEventPluginAPI' => 'aphront/console/plugin/event/api',
'DarkConsolePlugin' => 'aphront/console/plugin/base',
'DarkConsoleRequestPlugin' => 'aphront/console/plugin/request',
'DarkConsoleServicesPlugin' => 'aphront/console/plugin/services',
@ -851,6 +853,8 @@ phutil_register_library_map(array(
'DarkConsoleConfigPlugin' => 'DarkConsolePlugin',
'DarkConsoleController' => 'PhabricatorController',
'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
'DarkConsoleEventPlugin' => 'DarkConsolePlugin',
'DarkConsoleEventPluginAPI' => 'PhabricatorEventListener',
'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',

View file

@ -23,6 +23,7 @@ final class DarkConsoleCore {
const PLUGIN_ERRORLOG = 'ErrorLog';
const PLUGIN_SERVICES = 'Services';
const PLUGIN_EVENT = 'Event';
const PLUGIN_XHPROF = 'XHProf';
const PLUGIN_REQUEST = 'Request';
const PLUGIN_CONFIG = 'Config';
@ -32,6 +33,7 @@ final class DarkConsoleCore {
self::PLUGIN_ERRORLOG,
self::PLUGIN_REQUEST,
self::PLUGIN_SERVICES,
self::PLUGIN_EVENT,
self::PLUGIN_XHPROF,
self::PLUGIN_CONFIG,
);

View file

@ -0,0 +1,117 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group console
*/
class DarkConsoleEventPlugin extends DarkConsolePlugin {
public function getName() {
return 'Events';
}
public function getDescription() {
return 'Information about Phabricator events and event listeners.';
}
public function generateData() {
$listeners = PhabricatorEventEngine::getInstance()->getAllListeners();
foreach ($listeners as $key => $listener) {
$listeners[$key] = array(
'id' => $listener->getListenerID(),
'class' => get_class($listener),
);
}
$events = DarkConsoleEventPluginAPI::getEvents();
foreach ($events as $key => $event) {
$events[$key] = array(
'type' => $event->getType(),
'stopped' => $event->isStopped(),
);
}
return array(
'listeners' => $listeners,
'events' => $events,
);
}
public function render() {
$data = $this->getData();
$out = array();
$out[] =
'<div class="dark-console-panel-header">'.
'<h1>Registered Event Listeners</h1>'.
'</div>';
$rows = array();
foreach ($data['listeners'] as $listener) {
$rows[] = array(
phutil_escape_html($listener['id']),
phutil_escape_html($listener['class']),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Internal ID',
'Listener Class',
));
$table->setColumnClasses(
array(
'',
'wide',
));
$out[] = $table->render();
$out[] =
'<div class="dark-console-panel-header">'.
'<h1>Event Log</h1>'.
'</div>';
$rows = array();
foreach ($data['events'] as $event) {
$rows[] = array(
phutil_escape_html($event['type']),
$event['stopped'] ? 'STOPPED' : null,
);
}
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
'wide',
));
$table->setHeaders(
array(
'Event Type',
'Stopped',
));
$out[] = $table->render();
return implode("\n", $out);
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/console/plugin/base');
phutil_require_module('phabricator', 'aphront/console/plugin/event/api');
phutil_require_module('phabricator', 'infrastructure/events/engine');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phutil', 'markup');
phutil_require_source('DarkConsoleEventPlugin.php');

View file

@ -0,0 +1,47 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group console
*/
class DarkConsoleEventPluginAPI extends PhabricatorEventListener {
private static $events = array();
private static $discardMode = false;
public static function enableDiscardMode() {
self::$discardMode = true;
}
public static function getEvents() {
return self::$events;
}
public function register() {
$this->listen(PhabricatorEventType::TYPE_ALL);
}
public function handleEvent(PhabricatorEvent $event) {
if (self::$discardMode) {
return;
}
self::$events[] = $event;
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'infrastructure/events/constant/type');
phutil_require_module('phabricator', 'infrastructure/events/listener');
phutil_require_source('DarkConsoleEventPluginAPI.php');

View file

@ -25,6 +25,9 @@ abstract class PhabricatorDaemon extends PhutilDaemon {
// that daemons do not require unbounded amounts of memory.
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
// Also accumulates potentially unlimited amounts of data.
DarkConsoleEventPluginAPI::enableDiscardMode();
$phabricator = phutil_get_library_root('phabricator');
$root = dirname($phabricator);
require_once $root.'/scripts/__init_env__.php';

View file

@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api');
phutil_require_module('phabricator', 'aphront/console/plugin/event/api');
phutil_require_module('phabricator', 'aphront/writeguard');
phutil_require_module('phutil', 'daemon/base');

View file

@ -18,6 +18,7 @@
final class PhabricatorEventType extends PhabricatorEventConstants {
const TYPE_ALL = '*';
const TYPE_MANIPHEST_WILLEDITTASK = 'maniphest.willEditTask';
}

View file

@ -29,6 +29,9 @@ class PhabricatorEventEngine {
public static function initialize() {
self::$instance = new PhabricatorEventEngine();
// Register the DarkConosole event logger.
id(new DarkConsoleEventPluginAPI())->register();
// Instantiate and register custom event listeners so they can react to
// events.
$listeners = PhabricatorEnv::getEnvConfig('events.listeners');
@ -51,10 +54,30 @@ class PhabricatorEventEngine {
return $this;
}
/**
* Get all the objects currently listening to any event.
*/
public function getAllListeners() {
$listeners = array_mergev($this->listeners);
$listeners = mpull($listeners, null, 'getListenerID');
return $listeners;
}
public static function dispatchEvent(PhabricatorEvent $event) {
$instance = self::getInstance();
$listeners = idx($instance->listeners, $event->getType(), array());
$global_listeners = idx(
$instance->listeners,
PhabricatorEventType::TYPE_ALL,
array());
// Merge and deduplicate listeners (we want to send the event to each
// listener only once, even if it satisfies multiple criteria for the
// event).
$listeners = array_merge($listeners, $global_listeners);
$listeners = mpull($listeners, null, 'getListenerID');
foreach ($listeners as $listener) {
if ($event->isStopped()) {
// Do this first so if someone tries to dispatch a stopped event it

View file

@ -6,7 +6,9 @@
phutil_require_module('phabricator', 'aphront/console/plugin/event/api');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'infrastructure/events/constant/type');
phutil_require_module('phutil', 'utils');

View file

@ -18,6 +18,9 @@
abstract class PhabricatorEventListener {
private $listenerID;
private static $nextListenerID = 1;
final public function __construct() {
// <empty>
}
@ -30,4 +33,21 @@ abstract class PhabricatorEventListener {
$engine->addListener($this, $type);
}
/**
* Return a scalar ID unique to this listener. This is used to deduplicate
* listeners which match events on multiple rules, so they are invoked only
* once.
*
* @return int A scalar unique to this object instance.
*/
final public function getListenerID() {
if (!$this->listenerID) {
$this->listenerID = self::$nextListenerID;
self::$nextListenerID++;
}
return $this->listenerID;
}
}