Skip to content

Commit

Permalink
[JENKINS-20935] Provide separate monitoring pages for each slave node
Browse files Browse the repository at this point in the history
  • Loading branch information
evernat committed Dec 22, 2013
1 parent 8e09ae7 commit ac7cda8
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 45 deletions.
18 changes: 12 additions & 6 deletions src/main/java/net/bull/javamelody/NodesCollector.java
Expand Up @@ -18,8 +18,10 @@
*/
package net.bull.javamelody;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

Expand All @@ -44,7 +46,7 @@ void collectLocalContextWithoutErrors() {
private final boolean monitoringDisabled;
private final Timer timer;
private final Collector collector;
private List<JavaInformations> lastJavaInformationsList;
private Map<String, JavaInformations> lastJavaInformationsList;

/**
* Constructor.
Expand Down Expand Up @@ -126,7 +128,8 @@ public void stop() {
*/
public void collectWithoutErrors() {
try {
lastJavaInformationsList = RemoteCallHelper.collectJavaInformationsList();
lastJavaInformationsList = new RemoteCallHelper(null)
.collectJavaInformationsListByName();

// inspired by https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/LoadStatistics.java#L197
// (note: jobs in quiet period are not counted)
Expand All @@ -136,7 +139,9 @@ public void collectWithoutErrors() {
// including values for buildQueueLength in translations*.properties
JdbcWrapper.BUILD_QUEUE_LENGTH.set(queueLength);

collector.collectWithoutErrors(lastJavaInformationsList);
final List<JavaInformations> javaInformations = new ArrayList<JavaInformations>(
getLastJavaInformationsList().values());
collector.collectWithoutErrors(javaInformations);
} catch (final Throwable t) { // NOPMD
LOG.warn("exception while collecting data", t);
}
Expand All @@ -156,8 +161,9 @@ void scheduleReportMailForSlaves(final Period period) {
public void run() {
try {
// send the report
new MailReport().sendReportMail(getCollector(), true,
getLastJavaInformationsList(), period);
final List<JavaInformations> javaInformations = new ArrayList<JavaInformations>(
getLastJavaInformationsList().values());
new MailReport().sendReportMail(getCollector(), true, javaInformations, period);
} catch (final Throwable t) { // NOPMD
// no error in this task
LOG.warn("sending mail report failed", t);
Expand All @@ -178,7 +184,7 @@ Collector getCollector() {
return collector;
}

List<JavaInformations> getLastJavaInformationsList() {
Map<String, JavaInformations> getLastJavaInformationsList() {
return lastJavaInformationsList;
}

Expand Down
55 changes: 35 additions & 20 deletions src/main/java/net/bull/javamelody/NodesController.java
Expand Up @@ -36,6 +36,7 @@
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -52,17 +53,26 @@
*/
public class NodesController {
private final Collector collector;
private final String nodeName;
private final List<JavaInformations> lastJavaInformationsList;
private final HttpCookieManager httpCookieManager = new HttpCookieManager();

/**
* Constructor.
* @param nodesCollector NodesCollector
* @param nodeName Nom du node
*/
public NodesController(NodesCollector nodesCollector) {
public NodesController(NodesCollector nodesCollector, String nodeName) {
super();
this.collector = nodesCollector.getCollector();
this.lastJavaInformationsList = nodesCollector.getLastJavaInformationsList();
this.nodeName = nodeName;
if (nodeName == null) {
this.lastJavaInformationsList = new ArrayList<JavaInformations>(nodesCollector
.getLastJavaInformationsList().values());
} else {
this.lastJavaInformationsList = Collections.singletonList(nodesCollector
.getLastJavaInformationsList().get(nodeName));
}
}

/**
Expand Down Expand Up @@ -93,8 +103,8 @@ public void doMonitoring(HttpServletRequest req, HttpServletResponse resp) throw
final String threadId = req.getParameter(THREAD_ID_PARAMETER);
final String jobId = req.getParameter(JOB_ID_PARAMETER);
final String cacheId = req.getParameter(CACHE_ID_PARAMETER);
messageForReport = RemoteCallHelper.forwardAction(actionName, sessionId,
threadId, jobId, cacheId);
messageForReport = getRemoteCallHelper().forwardAction(actionName,
sessionId, threadId, jobId, cacheId);
} else {
// necessaire si action clear_counter
messageForReport = monitoringController.executeActionIfNeeded(req);
Expand All @@ -103,8 +113,8 @@ public void doMonitoring(HttpServletRequest req, HttpServletResponse resp) throw
final SerializableController serializableController = new SerializableController(
collector);
final Range range = serializableController.getRangeForSerializable(req);
final List<JavaInformations> javaInformationsList = RemoteCallHelper
.collectJavaInformationsList();
final List<JavaInformations> javaInformationsList = new ArrayList<JavaInformations>(
getRemoteCallHelper().collectJavaInformationsListByName().values());
final Serializable serializable = serializableController
.createDefaultSerializable(javaInformationsList, range,
messageForReport);
Expand All @@ -117,8 +127,8 @@ public void doMonitoring(HttpServletRequest req, HttpServletResponse resp) throw

final String formatParameter = req.getParameter(FORMAT_PARAMETER);
if (req.getParameter(JMX_VALUE) != null) {
final List<String> jmxValues = RemoteCallHelper.collectJmxValues(req
.getParameter(JMX_VALUE));
final List<String> jmxValues = getRemoteCallHelper().collectJmxValues(
req.getParameter(JMX_VALUE));
doJmxValue(resp, jmxValues);
} else if (TransportFormat.isATransportFormat(req.getParameter(FORMAT_PARAMETER))) {
doCompressedSerializable(req, resp, monitoringController);
Expand Down Expand Up @@ -160,18 +170,19 @@ private void writeMessage(HttpServletResponse resp, String message, String partT
private void doPdf(HttpServletRequest req, HttpServletResponse resp,
MonitoringController monitoringController) throws IOException, InterruptedException,
ExecutionException {
if (PROCESSES_PART.equalsIgnoreCase(req.getParameter(PART_PARAMETER))) {
final String partParameter = req.getParameter(PART_PARAMETER);
if (PROCESSES_PART.equalsIgnoreCase(partParameter)) {
monitoringController.addPdfContentTypeAndDisposition(req, resp);
final Map<String, List<ProcessInformations>> processInformationsByNodeName = RemoteCallHelper
final Map<String, List<ProcessInformations>> processInformationsByNodeName = getRemoteCallHelper()
.collectProcessInformationsByNodeName();
try {
doPdfProcesses(resp, processInformationsByNodeName);
} finally {
resp.getOutputStream().flush();
}
} else if (MBEANS_PART.equalsIgnoreCase(req.getParameter(PART_PARAMETER))) {
} else if (MBEANS_PART.equalsIgnoreCase(partParameter)) {
monitoringController.addPdfContentTypeAndDisposition(req, resp);
final Map<String, List<MBeanNode>> mbeanNodesByNodeName = RemoteCallHelper
final Map<String, List<MBeanNode>> mbeanNodesByNodeName = getRemoteCallHelper()
.collectMBeanNodesByNodeName();
try {
doPdfMBeans(resp, mbeanNodesByNodeName);
Expand Down Expand Up @@ -224,15 +235,15 @@ private void doPart(HttpServletRequest req, HttpServletResponse resp,
InterruptedException, ExecutionException {
// ici, ni web.xml ni pom.xml
if (MBEANS_PART.equalsIgnoreCase(partParameter)) {
final Map<String, List<MBeanNode>> mbeanNodesByNodeName = RemoteCallHelper
final Map<String, List<MBeanNode>> mbeanNodesByNodeName = getRemoteCallHelper()
.collectMBeanNodesByNodeName();
doMBeans(req, resp, mbeanNodesByNodeName);
} else if (PROCESSES_PART.equalsIgnoreCase(partParameter)) {
final Map<String, List<ProcessInformations>> processInformationsByNodeName = RemoteCallHelper
final Map<String, List<ProcessInformations>> processInformationsByNodeName = getRemoteCallHelper()
.collectProcessInformationsByNodeName();
doProcesses(req, resp, processInformationsByNodeName);
} else if (HEAP_HISTO_PART.equalsIgnoreCase(partParameter)) {
final HeapHistogram heapHistoTotal = RemoteCallHelper.collectGlobalHeapHistogram();
final HeapHistogram heapHistoTotal = getRemoteCallHelper().collectGlobalHeapHistogram();
doHeapHisto(req, resp, heapHistoTotal, monitoringController);
} else {
monitoringController.doReport(req, resp, lastJavaInformationsList);
Expand Down Expand Up @@ -312,13 +323,13 @@ private void doCompressedSerializable(HttpServletRequest httpRequest,
private Serializable createSerializable(HttpServletRequest httpRequest) throws Exception { // NOPMD
final String part = httpRequest.getParameter(PART_PARAMETER);
if (MBEANS_PART.equalsIgnoreCase(part)) {
return new LinkedHashMap<String, List<MBeanNode>>(
RemoteCallHelper.collectMBeanNodesByNodeName());
return new LinkedHashMap<String, List<MBeanNode>>(getRemoteCallHelper()
.collectMBeanNodesByNodeName());
} else if (PROCESSES_PART.equalsIgnoreCase(part)) {
return new LinkedHashMap<String, List<ProcessInformations>>(
RemoteCallHelper.collectProcessInformationsByNodeName());
return new LinkedHashMap<String, List<ProcessInformations>>(getRemoteCallHelper()
.collectProcessInformationsByNodeName());
} else if (HEAP_HISTO_PART.equalsIgnoreCase(part)) {
return RemoteCallHelper.collectGlobalHeapHistogram();
return getRemoteCallHelper().collectGlobalHeapHistogram();
} else if (THREADS_PART.equalsIgnoreCase(part)) {
final ArrayList<List<ThreadInformations>> result = new ArrayList<List<ThreadInformations>>();
for (final JavaInformations javaInformations : lastJavaInformationsList) {
Expand Down Expand Up @@ -348,6 +359,10 @@ private static PrintWriter createWriterFromOutputStream(HttpServletResponse http
return new PrintWriter(MonitoringController.getWriter(httpResponse));
}

private RemoteCallHelper getRemoteCallHelper() {
return new RemoteCallHelper(nodeName);
}

/**
* Is it necessary to collect java informations for this monitoring request?
* @param httpRequest HttpServletRequest
Expand Down
31 changes: 17 additions & 14 deletions src/main/java/net/bull/javamelody/RemoteCallHelper.java
Expand Up @@ -135,19 +135,24 @@ public T call() throws Throwable {
}
}

private RemoteCallHelper() {
private final String nodeName;

RemoteCallHelper(String nodeName) {
super();
this.nodeName = nodeName;
}

private static <T> Map<String, T> collectDataByNodeName(Callable<T, Throwable> task)
private <T> Map<String, T> collectDataByNodeName(Callable<T, Throwable> task)
throws IOException, InterruptedException, ExecutionException {
final Computer[] computers = Hudson.getInstance().getComputers();
final Map<String, Future<T>> futuresByNodeName = new LinkedHashMap<String, Future<T>>(
computers.length);
final DelegatingTask<T> delegatingTask = new DelegatingTask<T>(task);
for (final Computer c : computers) {
if (c.isOnline()) {
futuresByNodeName.put(c.getName(), c.getChannel().callAsync(delegatingTask));
if (nodeName == null || nodeName.equals(c.getName())) {
futuresByNodeName.put(c.getName(), c.getChannel().callAsync(delegatingTask));
}
}
}
final long now = System.currentTimeMillis();
Expand All @@ -156,42 +161,40 @@ private static <T> Map<String, T> collectDataByNodeName(Callable<T, Throwable> t

final Map<String, T> result = new LinkedHashMap<String, T>(futuresByNodeName.size());
for (final Map.Entry<String, Future<T>> entry : futuresByNodeName.entrySet()) {
final String nodeName = entry.getKey();
final String node = entry.getKey();
final Future<T> future = entry.getValue();
final long timeout = Math.max(0, end - System.currentTimeMillis());
try {
result.put(nodeName, future.get(timeout, TimeUnit.MILLISECONDS));
result.put(node, future.get(timeout, TimeUnit.MILLISECONDS));
} catch (final TimeoutException e) {
continue;
}
}
return result;
}

static List<JavaInformations> collectJavaInformationsList() throws IOException,
Map<String, JavaInformations> collectJavaInformationsListByName() throws IOException,
InterruptedException, ExecutionException {
final Map<String, JavaInformations> javaInformationsByNodeName = collectDataByNodeName(JAVA_INFORMATIONS_TASK);
return new ArrayList<JavaInformations>(javaInformationsByNodeName.values());

return collectDataByNodeName(JAVA_INFORMATIONS_TASK);
}

static List<String> collectJmxValues(String jmxValueParameter) throws IOException,
List<String> collectJmxValues(String jmxValueParameter) throws IOException,
InterruptedException, ExecutionException {
return new ArrayList<String>(collectDataByNodeName(new JmxValueTask(jmxValueParameter))
.values());
}

static Map<String, List<MBeanNode>> collectMBeanNodesByNodeName() throws IOException,
Map<String, List<MBeanNode>> collectMBeanNodesByNodeName() throws IOException,
InterruptedException, ExecutionException {
return collectDataByNodeName(MBEANS_TASK);
}

static Map<String, List<ProcessInformations>> collectProcessInformationsByNodeName()
Map<String, List<ProcessInformations>> collectProcessInformationsByNodeName()
throws IOException, InterruptedException, ExecutionException {
return collectDataByNodeName(PROCESS_INFORMATIONS_TASK);
}

static HeapHistogram collectGlobalHeapHistogram() throws IOException, InterruptedException,
HeapHistogram collectGlobalHeapHistogram() throws IOException, InterruptedException,
ExecutionException {
final Map<String, HeapHistogram> heapHistograms = collectDataByNodeName(HEAP_HISTOGRAM_TASK);
HeapHistogram heapHistoTotal = null;
Expand All @@ -208,7 +211,7 @@ static HeapHistogram collectGlobalHeapHistogram() throws IOException, Interrupte
return heapHistoTotal;
}

static String forwardAction(String actionName, String sessionId, String threadId, String jobId,
String forwardAction(String actionName, String sessionId, String threadId, String jobId,
String cacheId) throws IOException, InterruptedException, ExecutionException {
final ActionTask task = new ActionTask(actionName, sessionId, threadId, jobId, cacheId);
final Map<String, String> messagesByNodeName = collectDataByNodeName(task);
Expand Down
Expand Up @@ -75,9 +75,15 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
}

if (requestURI.equals(monitoringSlavesUrl)) {
if (requestURI.startsWith(monitoringSlavesUrl)) {
final String nodeName;
if (requestURI.equals(monitoringSlavesUrl)) {
nodeName = null;
} else {
nodeName = requestURI.substring(monitoringSlavesUrl.length()).replace("/", "");
}
final HttpServletResponse httpResponse = (HttpServletResponse) response;
doMonitoring(httpRequest, httpResponse);
doMonitoring(httpRequest, httpResponse, nodeName);
return;
}

Expand All @@ -89,14 +95,15 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
*
* @param httpRequest Http request
* @param httpResponse Http response
* @param nodeName nom du node (slave ou "")
* @throws IOException e
*/
private void doMonitoring(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException {
private void doMonitoring(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
String nodeName) throws IOException {
if (NodesController.isJavaInformationsNeeded(httpRequest)) {
getNodesCollector().collectWithoutErrors();
}
final NodesController nodesController = new NodesController(getNodesCollector());
final NodesController nodesController = new NodesController(getNodesCollector(), nodeName);
nodesController.doMonitoring(httpRequest, httpResponse);
}

Expand Down

0 comments on commit ac7cda8

Please sign in to comment.