1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-18 21:02:41 +01:00

Improve web tools for viewing daemons

Summary:
- Provides an "all daemons" view to look at more than the first 15 daemons.
  - Provides a "combined log" view with a large page size, to quickly look at
the log across all the daemons, making it easier to find issues when you have a
bunch of the same daemon and only one is having issues.
  - When viewing the web console on the same host as a daemon, show whether it's
running or not.

Test Plan:
Clicked the various daemon log interfaces.

Reviewed By: aran
Reviewers: jungejason, tuomaspelkonen, aran
CC: aran
Differential Revision: 215
This commit is contained in:
epriestley 2011-05-02 17:05:22 -07:00
parent 8370f93048
commit 6229cdadd8
16 changed files with 428 additions and 77 deletions

View file

@ -271,12 +271,16 @@ phutil_register_library_map(array(
'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog',
'PhabricatorController' => 'applications/base/controller/base',
'PhabricatorDaemon' => 'infrastructure/daemon/base',
'PhabricatorDaemonCombinedLogController' => 'applications/daemon/controller/combined',
'PhabricatorDaemonConsoleController' => 'applications/daemon/controller/console',
'PhabricatorDaemonControl' => 'infrastructure/daemon/control',
'PhabricatorDaemonController' => 'applications/daemon/controller/base',
'PhabricatorDaemonDAO' => 'infrastructure/daemon/storage/base',
'PhabricatorDaemonLog' => 'infrastructure/daemon/storage/log',
'PhabricatorDaemonLogEvent' => 'infrastructure/daemon/storage/event',
'PhabricatorDaemonLogEventsView' => 'applications/daemon/view/daemonlogevents',
'PhabricatorDaemonLogListController' => 'applications/daemon/controller/loglist',
'PhabricatorDaemonLogListView' => 'applications/daemon/view/daemonloglist',
'PhabricatorDaemonLogViewController' => 'applications/daemon/controller/logview',
'PhabricatorDaemonReference' => 'infrastructure/daemon/control/reference',
'PhabricatorDaemonTimelineConsoleController' => 'applications/daemon/controller/timeline',
@ -675,11 +679,15 @@ phutil_register_library_map(array(
'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO',
'PhabricatorController' => 'AphrontController',
'PhabricatorDaemon' => 'PhutilDaemon',
'PhabricatorDaemonCombinedLogController' => 'PhabricatorDaemonController',
'PhabricatorDaemonConsoleController' => 'PhabricatorDaemonController',
'PhabricatorDaemonController' => 'PhabricatorController',
'PhabricatorDaemonDAO' => 'PhabricatorLiskDAO',
'PhabricatorDaemonLog' => 'PhabricatorDaemonDAO',
'PhabricatorDaemonLogEvent' => 'PhabricatorDaemonDAO',
'PhabricatorDaemonLogEventsView' => 'AphrontView',
'PhabricatorDaemonLogListController' => 'PhabricatorDaemonController',
'PhabricatorDaemonLogListView' => 'AphrontView',
'PhabricatorDaemonLogViewController' => 'PhabricatorDaemonController',
'PhabricatorDaemonTimelineConsoleController' => 'PhabricatorDaemonController',
'PhabricatorDaemonTimelineEventController' => 'PhabricatorDaemonController',

View file

@ -231,6 +231,8 @@ class AphrontDefaultApplicationConfiguration
'/daemon/' => array(
'task/(?P<id>\d+)/$' => 'PhabricatorWorkerTaskDetailController',
'log/' => array(
'$' => 'PhabricatorDaemonLogListController',
'combined/$' => 'PhabricatorDaemonCombinedLogController',
'(?P<id>\d+)/$' => 'PhabricatorDaemonLogViewController',
),
'timeline/$' => 'PhabricatorDaemonTimelineConsoleController',

View file

@ -0,0 +1,54 @@
<?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.
*/
class PhabricatorDaemonCombinedLogController
extends PhabricatorDaemonController {
public function processRequest() {
$request = $this->getRequest();
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('page'));
$pager->setPageSize(1000);
$events = id(new PhabricatorDaemonLogEvent())->loadAllWhere(
'1 = 1 ORDER BY id DESC LIMIT %d, %d',
$pager->getOffset(),
$pager->getPageSize() + 1);
$events = $pager->sliceResults($events);
$pager->setURI($request->getRequestURI(), 'page');
$event_view = new PhabricatorDaemonLogEventsView();
$event_view->setEvents($events);
$event_view->setCombinedLog(true);
$log_panel = new AphrontPanelView();
$log_panel->setHeader('Combined Daemon Logs');
$log_panel->appendChild($event_view);
$log_panel->appendChild($pager);
return $this->buildStandardPageResponse(
$log_panel,
array(
'title' => 'Combined Daemon Log',
));
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/daemon/controller/base');
phutil_require_module('phabricator', 'applications/daemon/view/daemonlogevents');
phutil_require_module('phabricator', 'infrastructure/daemon/storage/event');
phutil_require_module('phabricator', 'view/control/pager');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorDaemonCombinedLogController.php');

View file

@ -22,48 +22,26 @@ class PhabricatorDaemonConsoleController extends PhabricatorDaemonController {
$logs = id(new PhabricatorDaemonLog())->loadAllWhere(
'1 = 1 ORDER BY id DESC LIMIT 15');
$rows = array();
foreach ($logs as $log) {
$epoch = $log->getDateCreated();
$rows[] = array(
phutil_escape_html($log->getDaemon()),
phutil_escape_html($log->getHost()),
$log->getPID(),
date('M j, Y', $epoch),
date('g:i A', $epoch),
phutil_render_tag(
'a',
array(
'href' => '/daemon/log/'.$log->getID().'/',
'class' => 'button small grey',
),
'View Log'),
);
}
$daemon_table = new AphrontTableView($rows);
$daemon_table->setHeaders(
array(
'Daemon',
'Host',
'PID',
'Date',
'Time',
'View',
));
$daemon_table->setColumnClasses(
array(
'wide wrap',
'',
'',
'',
'right',
'action',
));
$daemon_table = new PhabricatorDaemonLogListView();
$daemon_table->setDaemonLogs($logs);
$daemon_panel = new AphrontPanelView();
$daemon_panel->setHeader('Recently Launched Daemons');
$daemon_panel->setHeader(
'Recently Launched Daemons'.
' &middot; '.
phutil_render_tag(
'a',
array(
'href' => '/daemon/log/',
),
'View All Daemons').
' &middot; '.
phutil_render_tag(
'a',
array(
'href' => '/daemon/log/combined/',
),
'View Combined Log'));
$daemon_panel->appendChild($daemon_table);
$tasks = id(new PhabricatorWorkerTask())->loadAllWhere(

View file

@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'applications/daemon/controller/base');
phutil_require_module('phabricator', 'applications/daemon/view/daemonloglist');
phutil_require_module('phabricator', 'infrastructure/daemon/storage/log');
phutil_require_module('phabricator', 'infrastructure/daemon/timeline/storage/cursor');
phutil_require_module('phabricator', 'infrastructure/daemon/workers/storage/task');

View file

@ -0,0 +1,50 @@
<?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.
*/
class PhabricatorDaemonLogListController extends PhabricatorDaemonController {
public function processRequest() {
$request = $this->getRequest();
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('page'));
$logs = id(new PhabricatorDaemonLog())->loadAllWhere(
'1 = 1 ORDER BY id DESC LIMIT %d, %d',
$pager->getOffset(),
$pager->getPageSize() + 1);
$logs = $pager->sliceResults($logs);
$pager->setURI($request->getRequestURI(), 'page');
$daemon_table = new PhabricatorDaemonLogListView();
$daemon_table->setDaemonLogs($logs);
$daemon_panel = new AphrontPanelView();
$daemon_panel->setHeader('Launched Daemons');
$daemon_panel->appendChild($daemon_table);
$daemon_panel->appendChild($pager);
return $this->buildStandardPageResponse(
$daemon_panel,
array(
'title' => 'All Daemons',
));
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/daemon/controller/base');
phutil_require_module('phabricator', 'applications/daemon/view/daemonloglist');
phutil_require_module('phabricator', 'infrastructure/daemon/storage/log');
phutil_require_module('phabricator', 'view/control/pager');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorDaemonLogListController.php');

View file

@ -34,7 +34,7 @@ class PhabricatorDaemonLogViewController extends PhabricatorDaemonController {
}
$events = id(new PhabricatorDaemonLogEvent())->loadAllWhere(
'logID = %d ORDER BY id DESC LIMIT 200',
'logID = %d ORDER BY id DESC LIMIT 1000',
$log->getID());
$content = array();
@ -72,44 +72,19 @@ class PhabricatorDaemonLogViewController extends PhabricatorDaemonController {
$content[] = $panel;
$rows = array();
foreach ($events as $event) {
$rows[] = array(
phutil_escape_html($event->getLogType()),
date('M j, Y', $event->getEpoch()),
date('g:i:s A', $event->getEpoch()),
str_replace("\n", '<br />', phutil_escape_html($event->getMessage())),
);
}
$log_table = new AphrontTableView($rows);
$log_table->setHeaders(
array(
'Type',
'Date',
'Time',
'Message',
));
$log_table->setColumnClasses(
array(
'',
'',
'right',
'wide wrap',
));
$event_view = new PhabricatorDaemonLogEventsView();
$event_view->setEvents($events);
$log_panel = new AphrontPanelView();
$log_panel->setHeader('Daemon Logs');
$log_panel->appendChild($log_table);
$log_panel->appendChild($event_view);
$content[] = $log_panel;
return $this->buildStandardPageResponse(
$content,
array(
'title' => 'Log',
'title' => 'Daemon Log',
));
}

View file

@ -8,15 +8,14 @@
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'applications/daemon/controller/base');
phutil_require_module('phabricator', 'applications/daemon/view/daemonlogevents');
phutil_require_module('phabricator', 'infrastructure/daemon/storage/event');
phutil_require_module('phabricator', 'infrastructure/daemon/storage/log');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');

View file

@ -0,0 +1,83 @@
<?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.
*/
final class PhabricatorDaemonLogEventsView extends AphrontView {
private $events;
private $combinedLog;
public function setEvents(array $events) {
$this->events = $events;
}
public function setCombinedLog($is_combined) {
$this->combinedLog = $is_combined;
}
public function render() {
$rows = array();
foreach ($this->events as $event) {
$row = array(
phutil_escape_html($event->getLogType()),
date('M j, Y', $event->getEpoch()),
date('g:i:s A', $event->getEpoch()),
str_replace("\n", '<br />', phutil_escape_html($event->getMessage())),
);
if ($this->combinedLog) {
array_unshift(
$row,
phutil_render_tag(
'a',
array(
'href' => '/daemon/log/'.$event->getLogID().'/',
),
phutil_escape_html('Daemon '.$event->getLogID())));
}
$rows[] = $row;
}
$classes = array(
'',
'',
'right',
'wide wrap',
);
$headers = array(
'Type',
'Date',
'Time',
'Message',
);
if ($this->combinedLog) {
array_unshift($classes, 'pri');
array_unshift($headers, 'Daemon');
}
$log_table = new AphrontTableView($rows);
$log_table->setHeaders($headers);
$log_table->setColumnClasses($classes);
return $log_table->render();
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phutil', 'markup');
phutil_require_source('PhabricatorDaemonLogEventsView.php');

View file

@ -0,0 +1,113 @@
<?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.
*/
final class PhabricatorDaemonLogListView extends AphrontView {
private $daemonLogs;
public function setDaemonLogs(array $daemon_logs) {
$this->daemonLogs = $daemon_logs;
}
public function render() {
$rows = array();
foreach ($this->daemonLogs as $log) {
$epoch = $log->getDateCreated();
if ($log->getHost() == php_uname('n')) {
// This will probably fail since apache can't signal the process, but
// we can check the error code to figure out if the process exists.
$is_running = posix_kill($log->getPID(), 0);
if (posix_get_last_error() == 1) {
// "Operation Not Permitted", indicates that the PID exists. If it
// doesn't, we'll get an error 3 ("No such process") instead.
$is_running = true;
}
if ($is_running) {
$running = phutil_render_tag(
'span',
array(
'style' => 'color: #00cc00',
'title' => 'Running',
),
'&bull;');
} else {
$running = phutil_render_tag(
'span',
array(
'style' => 'color: #cc0000',
'title' => 'Not running',
),
'&bull;');
}
} else {
$running = phutil_render_tag(
'span',
array(
'style' => 'color: #888888',
'title' => 'Not on this host',
),
'?');
}
$rows[] = array(
$running,
phutil_escape_html($log->getDaemon()),
phutil_escape_html($log->getHost()),
$log->getPID(),
date('M j, Y', $epoch),
date('g:i A', $epoch),
phutil_render_tag(
'a',
array(
'href' => '/daemon/log/'.$log->getID().'/',
'class' => 'button small grey',
),
'View Log'),
);
}
$daemon_table = new AphrontTableView($rows);
$daemon_table->setHeaders(
array(
'',
'Daemon',
'Host',
'PID',
'Date',
'Time',
'View',
));
$daemon_table->setColumnClasses(
array(
'',
'wide wrap',
'',
'',
'',
'right',
'action',
));
return $daemon_table->render();
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'view/base');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phutil', 'markup');
phutil_require_source('PhabricatorDaemonLogListView.php');

View file

@ -30,11 +30,7 @@ class PhabricatorFileListController extends PhabricatorFileController {
$pager->getOffset(),
$pager->getPageSize() + 1);
if (count($files) > $pager->getPageSize()) {
$files = array_slice($files, 0, $pager->getPageSize(), true);
$pager->setHasMorePages(true);
}
$files = $pager->sliceResults($files);
$pager->setURI($request->getRequestURI(), 'page');
$rows = array();

View file

@ -80,6 +80,32 @@ final class AphrontPagerView extends AphrontView {
return $this->count !== null;
}
/**
* A common paging strategy is to select one extra record and use that to
* indicate that there's an additional page (this doesn't give you a
* complete page count but is often faster than counting the total number
* of items). This method will take a result array, slice it down to the
* page size if necessary, and call setHasMorePages() if there are more than
* one page of results.
*
* $results = queryfx_all(
* $conn,
* 'SELECT ... LIMIT %d, %d',
* $pager->getOffset(),
* $pager->getPageSize() + 1);
* $results = $pager->sliceResults($results);
*
* @param list Result array.
* @return list One page of results.
*/
public function sliceResults(array $results) {
if (count($results) > $this->getPageSize()) {
$results = array_slice($results, 0, $this->getPageSize(), true);
$this->setHasMorePages(true);
}
return $results;
}
public function render() {
if (!$this->uri) {
throw new Exception(