diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 5c3fcfee6c..3a34b328f5 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -18,7 +18,7 @@ celerity_register_resource_map(array( ), 'aphront-dark-console-css' => array( - 'uri' => '/res/056b0c12/rsrc/css/aphront/dark-console.css', + 'uri' => '/res/0417eb95/rsrc/css/aphront/dark-console.css', 'type' => 'css', 'requires' => array( @@ -61,24 +61,6 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/aphront/headsup-action-list-view.css', ), - 'aphront-list-filter-view-css' => - array( - 'uri' => '/res/50a790ae/rsrc/css/aphront/list-filter-view.css', - 'type' => 'css', - 'requires' => - array( - ), - 'disk' => '/rsrc/css/aphront/list-filter-view.css', - ), - 'aphront-pager-view-css' => - array( - 'uri' => '/res/73ec8cd5/rsrc/css/aphront/pager-view.css', - 'type' => 'css', - 'requires' => - array( - ), - 'disk' => '/rsrc/css/aphront/pager-view.css', - ), 'aphront-panel-view-css' => array( 'uri' => '/res/8f9f3632/rsrc/css/aphront/panel-view.css', @@ -134,6 +116,24 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/aphront/typeahead.css', ), + 'aphront-pager-view-css' => + array( + 'uri' => '/res/73ec8cd5/rsrc/css/aphront/pager-view.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/aphront/pager-view.css', + ), + 'aphront-list-filter-view-css' => + array( + 'uri' => '/res/50a790ae/rsrc/css/aphront/list-filter-view.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/aphront/list-filter-view.css', + ), 'phabricator-standard-page-view' => array( 'uri' => '/res/0d41ea7c/rsrc/css/application/base/standard-page-view.css', @@ -297,15 +297,6 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/application/objectselector/object-selector.css', ), - 'owners-path-editor-css' => - array( - 'uri' => '/res/f40dc6b1/rsrc/css/application/owners/owners-path-editor.css', - 'type' => 'css', - 'requires' => - array( - ), - 'disk' => '/rsrc/css/application/owners/owners-path-editor.css', - ), 'phabricator-profile-css' => array( 'uri' => '/res/259ad37f/rsrc/css/application/people/profile.css', @@ -315,6 +306,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/application/people/profile.css', ), + 'owners-path-editor-css' => + array( + 'uri' => '/res/f40dc6b1/rsrc/css/application/owners/owners-path-editor.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/application/owners/owners-path-editor.css', + ), 'phabricator-ui-example-css' => array( 'uri' => '/res/365a10f1/rsrc/css/application/uiexample/example.css', @@ -360,6 +360,16 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/core/syntax.css', ), + 'multirow-row-manager' => + array( + 'uri' => '/res/330d076b/rsrc/js/application/core/MultirowRowManager.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-lib-dev', + ), + 'disk' => '/rsrc/js/application/core/MultirowRowManager.js', + ), 'javelin-behavior-dark-console' => array( 'uri' => '/res/020b0265/rsrc/js/application/core/behavior-dark-console.js', @@ -371,7 +381,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-phabricator-object-selector' => array( - 'uri' => '/res/4fe735af/rsrc/js/application/core/behavior-object-selector.js', + 'uri' => '/res/c0f12b29/rsrc/js/application/core/behavior-object-selector.js', 'type' => 'js', 'requires' => array( @@ -399,15 +409,15 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/core/behavior-workflow.js', ), - 'multirow-row-manager' => + 'javelin-behavior-error-log' => array( - 'uri' => '/res/330d076b/rsrc/js/application/core/MultirowRowManager.js', + 'uri' => '/res/c57a323f/rsrc/js/application/core/behavior-error-log.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), - 'disk' => '/rsrc/js/application/core/MultirowRowManager.js', + 'disk' => '/rsrc/js/application/core/behavior-error-log.js', ), 'javelin-behavior-differential-add-reviewers' => array( @@ -499,17 +509,6 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/diffusion/behavior-pull-lastmodified.js', ), - 'javelin-behavior-herald-rule-editor' => - array( - 'uri' => '/res/48108130/rsrc/js/application/herald/herald-rule-editor.js', - 'type' => 'js', - 'requires' => - array( - 0 => 'herald-rule-editor', - 1 => 'javelin-lib-dev', - ), - 'disk' => '/rsrc/js/application/herald/herald-rule-editor.js', - ), 'herald-rule-editor' => array( 'uri' => '/res/ec8e2110/rsrc/js/application/herald/HeraldRuleEditor.js', @@ -523,6 +522,17 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/herald/HeraldRuleEditor.js', ), + 'javelin-behavior-herald-rule-editor' => + array( + 'uri' => '/res/48108130/rsrc/js/application/herald/herald-rule-editor.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'herald-rule-editor', + 1 => 'javelin-lib-dev', + ), + 'disk' => '/rsrc/js/application/herald/herald-rule-editor.js', + ), 'path-typeahead' => array( 'uri' => '/res/42fb76c3/rsrc/js/application/herald/PathTypeahead.js', @@ -544,17 +554,6 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/maniphest/behavior-transaction-controls.js', ), - 'javelin-behavior-owners-path-editor' => - array( - 'uri' => '/res/7568aa22/rsrc/js/application/owners/owners-path-editor.js', - 'type' => 'js', - 'requires' => - array( - 0 => 'owners-path-editor', - 1 => 'javelin-lib-dev', - ), - 'disk' => '/rsrc/js/application/owners/owners-path-editor.js', - ), 'owners-path-editor' => array( 'uri' => '/res/b01c1ca9/rsrc/js/application/owners/OwnersPathEditor.js', @@ -568,6 +567,17 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/owners/OwnersPathEditor.js', ), + 'javelin-behavior-owners-path-editor' => + array( + 'uri' => '/res/7568aa22/rsrc/js/application/owners/owners-path-editor.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'owners-path-editor', + 1 => 'javelin-lib-dev', + ), + 'disk' => '/rsrc/js/application/owners/owners-path-editor.js', + ), 'javelin-magical-init' => array( 'uri' => '/res/76614f84/rsrc/js/javelin/init.dev.js', diff --git a/src/aphront/console/core/DarkConsoleCore.php b/src/aphront/console/core/DarkConsoleCore.php index afb9d09350..2b9582f28c 100644 --- a/src/aphront/console/core/DarkConsoleCore.php +++ b/src/aphront/console/core/DarkConsoleCore.php @@ -124,7 +124,7 @@ final class DarkConsoleCore { $panel_markup[] = javelin_render_tag( 'div', array( - 'class' => 'dark-console-panel', + 'class' => 'dark-console-panel dark-console-panel-'.$key, 'style' => $style, 'sigil' => 'dark-console-panel', ), diff --git a/src/aphront/console/plugin/errorlog/DarkConsoleErrorLogPlugin.php b/src/aphront/console/plugin/errorlog/DarkConsoleErrorLogPlugin.php index 0f140f582b..a3352c5023 100644 --- a/src/aphront/console/plugin/errorlog/DarkConsoleErrorLogPlugin.php +++ b/src/aphront/console/plugin/errorlog/DarkConsoleErrorLogPlugin.php @@ -46,39 +46,62 @@ class DarkConsoleErrorLogPlugin extends DarkConsolePlugin { $data = $this->getData(); $rows = array(); - foreach ($data as $row) { - switch ($row['event']) { - case 'error': - $file = $row['file']; - $line = $row['line']; - break; - case 'exception': - $file = $row['exception']->getFile(); - $line = $row['exception']->getLine(); - break; + $details = ''; + + foreach ($data as $index => $row) { + $file = $row['file']; + $line = $row['line']; + + $tag = phutil_render_tag( + 'a', + array( + 'onclick' => jsprintf('show_details(%d)', $index), + ), + phutil_escape_html($row['str'].' at ['.basename($file).':'.$line.']')); + $rows[] = array($tag); + + $details .= + '
'. + phutil_escape_html($row['details'])."\n". + 'Stack trace:'."\n"; + + foreach ($row['trace'] as $key => $entry) { + $line = ''; + if (isset($entry['class'])) { + $line .= $entry['class'].'::'; + } + $line .= idx($entry, 'function', ''); + $onclick = ''; + if (isset($entry['file'])) { + $line .= ' called at ['.$entry['file'].':'.$entry['line'].']'; + $onclick = jsprintf( + 'open_file(%s, %d)', $entry['file'], $entry['line']); + } + + $details .= phutil_render_tag( + 'a', + array( + 'onclick' => $onclick, + ), + phutil_escape_html($line)); + $details .= "\n"; } - - $rows[] = array( - basename($file).':'.$line, - $row['str'], - ); + $details .= '
'; } $table = new AphrontTableView($rows); - $table->setColumnClasses( - array( - null, - 'wide wrap', - )); - $table->setHeaders( - array( - 'File', - 'Error', - )); + $table->setClassName('error-log'); + $table->setHeaders(array('Error')); $table->setNoDataString('No errors.'); - return $table->render(); + return '
'. + '
'.$table->render().'
'. + '
'. + '
'.
+      $details.'
'. + '
'; } } diff --git a/src/aphront/console/plugin/errorlog/__init__.php b/src/aphront/console/plugin/errorlog/__init__.php index 6b52c92dea..cb560b1875 100644 --- a/src/aphront/console/plugin/errorlog/__init__.php +++ b/src/aphront/console/plugin/errorlog/__init__.php @@ -10,5 +10,9 @@ phutil_require_module('phabricator', 'aphront/console/plugin/base'); phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api'); phutil_require_module('phabricator', 'view/control/table'); +phutil_require_module('phutil', 'markup'); +phutil_require_module('phutil', 'utils'); +phutil_require_module('phutil', 'xsprintf/jsprintf'); + phutil_require_source('DarkConsoleErrorLogPlugin.php'); diff --git a/src/aphront/console/plugin/errorlog/api/DarkConsoleErrorLogPluginAPI.php b/src/aphront/console/plugin/errorlog/api/DarkConsoleErrorLogPluginAPI.php index a6eea09524..7a30048342 100644 --- a/src/aphront/console/plugin/errorlog/api/DarkConsoleErrorLogPluginAPI.php +++ b/src/aphront/console/plugin/errorlog/api/DarkConsoleErrorLogPluginAPI.php @@ -16,6 +16,7 @@ * limitations under the License. */ + class DarkConsoleErrorLogPluginAPI { private static $errors = array(); @@ -30,29 +31,50 @@ class DarkConsoleErrorLogPluginAPI { return self::$errors; } - public static function handleError($num, $str, $file, $line, $cxt) { - if (!self::$discardMode) { - self::$errors[] = array( - 'event' => 'error', - 'num' => $num, - 'str' => $str, - 'file' => $file, - 'line' => $line, - 'cxt' => $cxt, - 'trace' => debug_backtrace(), - ); + public static function handleErrors($event, $value, $metadata) { + if (self::$discardMode) { + return; } - error_log("{$file}:{$line} {$str}"); - } - public static function handleException($ex) { - if (!self::$discardMode) { - self::$errors[] = array( - 'event' => 'exception', - 'exception' => $ex, - ); + switch ($event) { + case PhutilErrorHandler::EXCEPTION: + // $value is of type Exception + self::$errors[] = array( + 'details' => $value->getMessage(), + 'event' => $event, + 'file' => $value->getFile(), + 'line' => $value->getLine(), + 'str' => $value->getMessage(), + 'trace' => $metadata['trace'], + ); + break; + case PhutilErrorHandler::ERROR: + // $value is a simple string + self::$errors[] = array( + 'details' => $value, + 'event' => $event, + 'file' => $metadata['file'], + 'line' => $metadata['line'], + 'str' => $value, + 'trace' => $metadata['trace'], + ); + break; + case PhutilErrorHandler::PHLOG: + // $value can be anything + self::$errors[] = array( + 'details' => PhutilReadableSerializer::printShallow($value, 3), + 'event' => $event, + 'file' => $metadata['file'], + 'line' => $metadata['line'], + 'str' => PhutilReadableSerializer::printShort($value), + 'trace' => $metadata['trace'], + ); + break; + default: + error_log('Unknown event : '.$event); + break; } - error_log($ex); } } + diff --git a/src/aphront/console/plugin/errorlog/api/__init__.php b/src/aphront/console/plugin/errorlog/api/__init__.php index f4f7b7ffb9..428c8500c1 100644 --- a/src/aphront/console/plugin/errorlog/api/__init__.php +++ b/src/aphront/console/plugin/errorlog/api/__init__.php @@ -6,5 +6,8 @@ +phutil_require_module('phutil', 'error'); +phutil_require_module('phutil', 'readableserializer'); + phutil_require_source('DarkConsoleErrorLogPluginAPI.php'); diff --git a/src/view/page/standard/PhabricatorStandardPageView.php b/src/view/page/standard/PhabricatorStandardPageView.php index b2c739b22a..2227d3b477 100644 --- a/src/view/page/standard/PhabricatorStandardPageView.php +++ b/src/view/page/standard/PhabricatorStandardPageView.php @@ -101,6 +101,9 @@ class PhabricatorStandardPageView extends AphrontPageView { array( 'uri' => '/~/', )); + + // Change this to initBehavior when there is some behavior to initialize + require_celerity_resource('javelin-behavior-error-log'); } $this->bodyContent = $this->renderChildren(); diff --git a/webroot/index.php b/webroot/index.php index fa175970f3..5b733544b1 100644 --- a/webroot/index.php +++ b/webroot/index.php @@ -71,6 +71,11 @@ $tz = PhabricatorEnv::getEnvConfig('phabricator.timezone'); if ($tz) { date_default_timezone_set($tz); } +phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api'); +phutil_require_module('phutil', 'error'); + +PhutilErrorHandler::setErrorListener( + array('DarkConsoleErrorLogPluginAPI', 'handleErrors')); foreach (PhabricatorEnv::getEnvConfig('load-libraries') as $library) { phutil_load_library($library); @@ -89,6 +94,7 @@ switch ($host) { break; } + $application->setHost($host); $application->setPath($path); $application->willBuildRequest(); @@ -126,6 +132,7 @@ foreach ($headers as $header) { header("{$header}: {$value}"); } + // TODO: This shouldn't be possible in a production-configured environment. if (isset($_REQUEST['__profile__']) && ($_REQUEST['__profile__'] == 'all')) { diff --git a/webroot/rsrc/css/aphront/dark-console.css b/webroot/rsrc/css/aphront/dark-console.css index c1e63cb14d..7fe3d766b5 100644 --- a/webroot/rsrc/css/aphront/dark-console.css +++ b/webroot/rsrc/css/aphront/dark-console.css @@ -70,3 +70,20 @@ a.dark-console-tab-selected { .dark-console .aphront-table-view tr.no-data td { color: #dddddd; } + +.dark-console-panel-ErrorLog { + max-height: 500px; + overflow: auto; +} + +.dark-console-panel-error-details { + display: none; +} + +.dark-console-panel-error-separator { + background-color: #e8e8e8; + border-bottom: 1px solid #b7b7b7; + border-top: 1px solid #b7b7b7; + height: 2px; +} + diff --git a/webroot/rsrc/js/application/core/behavior-error-log.js b/webroot/rsrc/js/application/core/behavior-error-log.js new file mode 100644 index 0000000000..5f2d64ed1c --- /dev/null +++ b/webroot/rsrc/js/application/core/behavior-error-log.js @@ -0,0 +1,22 @@ +/** + * @provides javelin-behavior-error-log + * @requires javelin-lib-dev + */ + +var current_details = null; + +function open_file(file, row) { + // Do some fun some here, e.g., open the diffusion page for the file + // or open the file in an editor +} + +function show_details(row) { + var node = JX.$('row-details-' + row); + + if (current_details !== null) { + JX.$('row-details-' + current_details).style.display = 'none'; + } + + node.style.display = 'block'; + current_details = row; +}