import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequestBuilder;
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequestBuilder;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsRequestBuilder;
-import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
import org.elasticsearch.action.admin.indices.cache.clear.ClearIndicesCacheRequestBuilder;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder;
import org.sonar.server.es.request.ProxyRefreshRequestBuilder;
import org.sonar.server.es.request.ProxySearchRequestBuilder;
import org.sonar.server.es.request.ProxySearchScrollRequestBuilder;
-import org.sonar.server.search.ClusterHealth;
import org.sonar.server.search.SearchClient;
/**
this.client = client;
}
- public ClusterHealth getClusterHealth() {
- ClusterHealth health = new ClusterHealth();
- ClusterStatsResponse clusterStatsResponse = this.prepareClusterStats().get();
-
- // Cluster health
- health.setClusterAvailable(clusterStatsResponse.getStatus() != ClusterHealthStatus.RED);
-
- // Number of nodes
- health.setNumberOfNodes(clusterStatsResponse.getNodesStats().getCounts().getTotal());
-
- return health;
- }
-
public RefreshRequestBuilder prepareRefresh(String... indices) {
return new ProxyRefreshRequestBuilder(client).setIndices(indices);
}
// Elasticsearch
SearchClient.class,
IndexClient.class,
- SearchHealth.class,
EsClient.class,
// users
SystemWs.class,
SystemMonitor.class,
SonarQubeMonitor.class,
- EsClusterMonitor.class,
- EsNodesMonitor.class,
+ EsMonitor.class,
PluginsMonitor.class,
JvmPropertiesMonitor.class,
DatabaseMonitor.class
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.platform.monitoring;
-
-import com.google.common.base.Joiner;
-import org.sonar.server.search.ClusterHealth;
-import org.sonar.server.search.IndexHealth;
-import org.sonar.server.search.SearchHealth;
-
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe;
-
-public class EsClusterMonitor extends BaseMonitorMBean
- implements EsClusterMonitorMBean {
-
- private final SearchHealth searchHealth;
-
- public EsClusterMonitor(SearchHealth searchHealth) {
- this.searchHealth = searchHealth;
- }
-
- @Override
- public String getClusterState() {
- return clusterHealth().isClusterAvailable() ? "Available" : "Unavailable";
- }
-
- @Override
- public int getNumberOfNodes() {
- return clusterHealth().getNumberOfNodes();
- }
-
- @Override
- public String getIndexesHealth() {
- Map<String, String> formattedIndexes = new HashMap<>();
- for (Map.Entry<String, IndexHealth> healthEntry : searchHealth.getIndexHealth().entrySet()) {
- formattedIndexes.put(healthEntry.getKey(), formatIndexHealth(healthEntry.getValue()));
- }
-
- return Joiner.on(" | ").withKeyValueSeparator(": ").join(formattedIndexes);
- }
-
- @Override
- public String name() {
- return "ElasticSearchCluster";
- }
-
- @Override
- public LinkedHashMap<String, Object> attributes() {
- LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
- attributes.put("Cluster State", getClusterState());
- attributes.put("Number of Nodes", getNumberOfNodes());
- for (Map.Entry<String, IndexHealth> healthEntry : searchHealth.getIndexHealth().entrySet()) {
- attributes.put(healthEntry.getKey() + " - Document Count", healthEntry.getValue().getDocumentCount());
- attributes.put(healthEntry.getKey() + " - Last Sync", formatDateTimeNullSafe(healthEntry.getValue().getLastSynchronization()));
- attributes.put(healthEntry.getKey() + " - Optimization", formatIndexHealthOptimisation(healthEntry.getValue()));
- }
- return attributes;
- }
-
- private String formatIndexHealthOptimisation(IndexHealth indexHealth) {
- return indexHealth.isOptimized() ? "Optimized" : "Unoptimized " +
- "(Segments: " + indexHealth.getSegmentcount() + ", Pending Deletions: "
- + indexHealth.getPendingDeletion() + ")";
- }
-
- private ClusterHealth clusterHealth() {
- return searchHealth.getClusterHealth();
- }
-
- private String formatIndexHealth(IndexHealth indexHealth) {
- return new StringBuilder()
- .append(indexHealth.getDocumentCount())
- .append("/")
- .append(formatDateTimeNullSafe(indexHealth.getLastSynchronization()))
- .append("/")
- .append(formatIndexHealthOptimisation(indexHealth)).toString();
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.platform.monitoring;
-
-public interface EsClusterMonitorMBean {
- String getClusterState();
- int getNumberOfNodes();
- String getIndexesHealth();
-}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.platform.monitoring;
+
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
+import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
+import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
+import org.elasticsearch.action.admin.indices.stats.IndexStats;
+import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
+import org.elasticsearch.monitor.process.ProcessStats;
+import org.sonar.server.es.EsClient;
+
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.sonar.api.utils.DateUtils.formatDateTime;
+
+public class EsMonitor extends BaseMonitorMBean implements EsMonitorMBean {
+
+ private final EsClient esClient;
+
+ public EsMonitor(EsClient esClient) {
+ this.esClient = esClient;
+ }
+
+ @Override
+ public String name() {
+ return "ElasticSearch";
+ }
+
+ /**
+ * MXBean does not allow to return enum {@link ClusterHealthStatus}, so
+ * returning String.
+ */
+ @Override
+ public String getState() {
+ return getStateAsEnum().name();
+ }
+
+ private ClusterHealthStatus getStateAsEnum() {
+ return clusterStats().getStatus();
+ }
+
+ @Override
+ public int getNumberOfNodes() {
+ return clusterStats().getNodesStats().getCounts().getTotal();
+ }
+
+ @Override
+ public LinkedHashMap<String, Object> attributes() {
+ LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
+ attributes.put("State", getStateAsEnum());
+ attributes.put("Indices", indexAttributes());
+ attributes.put("Number of Nodes", getNumberOfNodes());
+ attributes.put("Nodes", nodeAttributes());
+ return attributes;
+ }
+
+ private LinkedHashMap<String, LinkedHashMap<String, Object>> indexAttributes() {
+ LinkedHashMap<String,LinkedHashMap<String,Object>> indices = new LinkedHashMap<>();
+ IndicesStatsResponse indicesStats = esClient.prepareStats().all().get();
+
+ for (Map.Entry<String, IndexStats> indexStats : indicesStats.getIndices().entrySet()) {
+ LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
+ indices.put(indexStats.getKey(), attributes);
+ attributes.put("Docs", indexStats.getValue().getPrimaries().getDocs().getCount());
+ attributes.put("Shards", indexStats.getValue().getShards().length);
+ attributes.put("Store Size", byteCountToDisplaySize(indexStats.getValue().getPrimaries().getStore().getSizeInBytes()));
+ }
+ return indices;
+ }
+
+ /**
+ * map of {node name -> node attributes}
+ */
+ private LinkedHashMap<String, LinkedHashMap<String, Object>> nodeAttributes() {
+ LinkedHashMap<String,LinkedHashMap<String,Object>> nodes = new LinkedHashMap<>();
+ NodesStatsResponse nodesStats = esClient.prepareNodesStats().all().get();
+ for (Map.Entry<String, NodeStats> entry : nodesStats.getNodesMap().entrySet()) {
+
+ LinkedHashMap<String, Object> nodeAttributes = new LinkedHashMap<>();
+ nodes.put(entry.getKey(), nodeAttributes);
+ NodeStats stats = entry.getValue();
+ nodeAttributes.put("Address", stats.getNode().getAddress().toString());
+ nodeAttributes.put("Type", stats.getNode().isMasterNode() ? "Master" : "Slave");
+ nodeAttributes.put("Disk Usage", byteCountToDisplaySize(stats.getFs().getTotal().getTotal().bytes()));
+ nodeAttributes.put("Disk Available", byteCountToDisplaySize(stats.getFs().getTotal().getAvailable().bytes()));
+ nodeAttributes.put("Store Size", byteCountToDisplaySize(stats.getIndices().getStore().getSizeInBytes()));
+ nodeAttributes.put("Open Files", stats.getProcess().getOpenFileDescriptors());
+ ProcessStats.Cpu cpu = stats.getProcess().getCpu();
+ if (cpu != null) {
+ nodeAttributes.put("CPU Load Average", formatPercent(cpu.getPercent()));
+ }
+ nodeAttributes.put("JVM Heap Usage", formatPercent(stats.getJvm().getMem().getHeapUsedPrecent()));
+ nodeAttributes.put("JVM Heap Used", byteCountToDisplaySize(stats.getJvm().getMem().getHeapUsed().bytes()));
+ nodeAttributes.put("JVM Heap Max", byteCountToDisplaySize(stats.getJvm().getMem().getHeapMax().bytes()));
+ nodeAttributes.put("JVM Non Heap Used", byteCountToDisplaySize(stats.getJvm().getMem().getNonHeapUsed().bytes()));
+ nodeAttributes.put("JVM Threads", stats.getJvm().getThreads().count());
+ nodeAttributes.put("JVM Started Since", formatDateTime(new Date(stats.getJvm().getUptime().getMillis())));
+ nodeAttributes.put("Field Cache Memory", byteCountToDisplaySize(stats.getIndices().getFieldData().getMemorySizeInBytes()));
+ nodeAttributes.put("Filter Cache Memory", byteCountToDisplaySize(stats.getIndices().getFilterCache().getMemorySizeInBytes()));
+ nodeAttributes.put("ID Cache Memory", byteCountToDisplaySize(stats.getIndices().getIdCache().getMemorySizeInBytes()));
+ nodeAttributes.put("Query Cache Memory", byteCountToDisplaySize(stats.getIndices().getQueryCache().getMemorySizeInBytes()));
+ }
+ return nodes;
+ }
+
+ private ClusterStatsResponse clusterStats() {
+ return esClient.prepareClusterStats().get();
+ }
+
+ private String formatPercent(long amount) {
+ return String.format("%.1f%%", 100 * amount * 1.0D / 100L);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.platform.monitoring;
+
+/**
+ * The public attributes of {@link org.sonar.server.platform.monitoring.EsMonitor}
+ * to be exported in JMX bean.
+ */
+public interface EsMonitorMBean {
+ String getState();
+ int getNumberOfNodes();
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.platform.monitoring;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.Table;
-import org.sonar.server.search.NodeHealth;
-import org.sonar.server.search.NodeHealth.Performance;
-import org.sonar.server.search.SearchHealth;
-
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
-import static org.sonar.api.utils.DateUtils.formatDateTime;
-
-public class EsNodesMonitor extends BaseMonitorMBean implements EsNodesMonitorMBean {
-
- private final SearchHealth searchHealth;
-
- public EsNodesMonitor(SearchHealth searchHealth) {
- this.searchHealth = searchHealth;
- }
-
- @Override
- public String getNodes() {
- List<String> formattedNodes = new ArrayList<>();
- Table<String, String, String> nodes = nodes();
-
- for (String nodeName : nodes.rowKeySet()) {
- StringBuilder formattedNode = new StringBuilder();
- formattedNode.append(nodeName)
- .append(" - ")
- .append(Joiner.on(", ").withKeyValueSeparator(": ").join(nodes.row(nodeName)));
- formattedNodes.add(formattedNode.toString());
- }
-
- return Joiner.on(" | ").join(formattedNodes);
- }
-
- @Override
- public String name() {
- return "ElasticSearchNodes";
- }
-
- @Override
- public LinkedHashMap<String, Object> attributes() {
- LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
- Table<String, String, String> nodes = nodes();
- for (String nodeName : nodes.rowKeySet()) {
- attributes.put(nodeName, nodes.row(nodeName));
- }
- return attributes;
- }
-
- private Table<String, String, String> nodes() {
- Table<String, String, String> nodes = HashBasedTable.create();
- for (Map.Entry<String, NodeHealth> nodeEntry : searchHealth.getNodesHealth().entrySet()) {
- String name = nodeEntry.getKey();
- nodes.put(name, "Name", nodeEntry.getKey());
- NodeHealth nodeHealth = nodeEntry.getValue();
- nodes.put(name, "Type", nodeHealth.isMaster() ? "Master" : "Slave");
- nodes.put(name, "Address", nodeHealth.getAddress());
- nodes.put(name, "JVM Heap Usage", nodeHealth.getJvmHeapUsedPercent());
- nodes.put(name, "JVM Threads", String.valueOf(nodeHealth.getJvmThreads()));
- nodes.put(name, "JVM Started Since", formatDateTime(nodeHealth.getJvmUpSince()));
- nodes.put(name, "Disk Usage", nodeHealth.getFsUsedPercent());
- nodes.put(name, "Open Files", String.valueOf(nodeHealth.getOpenFiles()));
- nodes.put(name, "CPU Load Average", nodeHealth.getProcessCpuPercent());
- nodes.put(name, "Field Cache Size", byteCountToDisplaySize(nodeHealth.getFieldCacheMemory()));
- nodes.put(name, "Filter Cache Size", byteCountToDisplaySize(nodeHealth.getFilterCacheMemory()));
-
- for (Performance performance : nodeHealth.getPerformanceStats()) {
- String message = "";
- if (Performance.Status.ERROR.equals(performance.getStatus()) || Performance.Status.WARN.equals(performance.getStatus())) {
- message = String.format("- %s: %s", performance.getStatus(), performance.getMessage());
- }
- if (performance.getName().contains("Eviction")) {
- nodes.put(name, performance.getName(), String.format("%f %s", performance.getValue(), message));
- } else {
- nodes.put(name, performance.getName(), String.format("%.1f ms %s", performance.getValue(), message));
- }
- }
- }
-
- return nodes;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-package org.sonar.server.platform.monitoring;
-
-public interface EsNodesMonitorMBean {
-
- String getNodes();
-}
public LinkedHashMap<String, Object> attributes() {
LinkedHashMap<String, Object> attributes = new LinkedHashMap<>();
for (PluginMetadata plugin : plugins()) {
- attributes.put(plugin.getName(), plugin.getVersion());
+ LinkedHashMap<String, Object> pluginAttributes = new LinkedHashMap<>();
+ pluginAttributes.put("Name", plugin.getName());
+ pluginAttributes.put("Version", plugin.getVersion());
+ attributes.put(plugin.getKey(), pluginAttributes);
}
return attributes;
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.search;
-
-public class ClusterHealth {
-
- private boolean clusterAvailable;
- private int numberOfNodes;
-
- public void setClusterAvailable(boolean clusterAvailable) {
- this.clusterAvailable = clusterAvailable;
- }
-
- public boolean isClusterAvailable() {
- return clusterAvailable;
- }
-
- public void setNumberOfNodes(int total) {
- this.numberOfNodes = total;
- }
-
- public int getNumberOfNodes() {
- return numberOfNodes;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.search;
-
-import java.util.Date;
-
-public class IndexHealth {
- private static final int SEGMENTS_THRESHOLD = 5;
- private static final double PENDING_DELETION_THRESHOLD = 0.08D;
-
- String name;
- long documentCount;
- Date lastSync;
- long segmentCount;
- long pendingDeletion;
-
- public String getName() {
- return name;
- }
-
- public long getDocumentCount() {
- return documentCount;
- }
-
- public Date getLastSynchronization() {
- return lastSync;
- }
-
- public boolean isOptimized() {
- return segmentCount < SEGMENTS_THRESHOLD && pendingDeletion < documentCount * PENDING_DELETION_THRESHOLD;
- }
-
- public long getSegmentcount() {
- return segmentCount;
- }
-
- public long getPendingDeletion() {
- return pendingDeletion;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.search;
-
-import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-
-public class NodeHealth {
-
- private boolean master;
- private String address;
- private long jvmHeapMax;
- private long jvmHeapUsed;
- private long fsAvailable;
- private long fsTotal;
- private long jvmThreads;
- private int cpuPercent;
- private long openFiles;
- private long jvmUptimeMillis;
- private long fieldCacheMemory;
- private long filterCacheMemory;
- private List<Performance> performanceStats;
-
- NodeHealth(NodeStats nodesStats) {
- // Master/slave
- setMaster(nodesStats.getNode().isMasterNode());
-
- // Host IP and port
- setAddress(nodesStats.getNode().getAddress().toString());
-
- // JVM Heap Usage
- setJvmHeapMax(nodesStats.getJvm().getMem().getHeapMax().bytes());
- setJvmHeapUsed(nodesStats.getJvm().getMem().getHeapUsed().bytes());
-
- // Disk Usage
- setFsTotal(nodesStats.getFs().getTotal().getTotal().bytes());
- setFsAvailable(nodesStats.getFs().getTotal().getAvailable().bytes());
-
- // Ping ?
-
- // Threads
- setJvmThreads(nodesStats.getJvm().getThreads().count());
-
- // CPU
- if (nodesStats.getProcess().getCpu() != null) {
- setProcessCpuPercent(nodesStats.getProcess().cpu().getPercent());
- }
-
- // Open Files
- setOpenFiles(nodesStats.getProcess().getOpenFileDescriptors());
-
- // Uptime
- setJvmUptimeMillis(nodesStats.getJvm().getUptime().getMillis());
-
- initPerformanceStats(nodesStats);
- }
-
- public boolean isMaster() {
- return master;
- }
-
- void setMaster(boolean master) {
- this.master = master;
- }
-
- public String getAddress() {
- return address;
- }
-
- void setAddress(String address) {
- this.address = address;
- }
-
- public long getJvmHeapMax() {
- return jvmHeapMax;
- }
-
- void setJvmHeapMax(long bytes) {
- this.jvmHeapMax = bytes;
- }
-
- public long getJvmHeapUsed() {
- return jvmHeapUsed;
- }
-
- void setJvmHeapUsed(long bytes) {
- this.jvmHeapUsed = bytes;
- }
-
- public String getJvmHeapUsedPercent() {
- return formatPercent(getJvmHeapUsed(), getJvmHeapMax());
- }
-
- void setFsAvailable(long bytes) {
- this.fsAvailable = bytes;
- }
-
- void setFsTotal(long bytes) {
- this.fsTotal = bytes;
- }
-
- public String getFsUsedPercent() {
- return formatPercent(fsTotal - fsAvailable, fsTotal);
- }
-
- private String formatPercent(long amount, long total) {
- return String.format("%.1f%%", 100 * amount * 1.0D / total);
- }
-
- public long getJvmThreads() {
- return jvmThreads;
- }
-
- void setJvmThreads(long threads) {
- this.jvmThreads = threads;
- }
-
- public String getProcessCpuPercent() {
- return formatPercent(cpuPercent, 100L);
- }
-
- void setProcessCpuPercent(int cpuPercent) {
- this.cpuPercent = cpuPercent;
- }
-
- public long getOpenFiles() {
- return openFiles;
- }
-
- void setOpenFiles(long avgOpenFileDescriptors) {
- this.openFiles = avgOpenFileDescriptors;
- }
-
- public long getJvmUptimeMillis() {
- return jvmUptimeMillis;
- }
-
- void setJvmUptimeMillis(long millis) {
- this.jvmUptimeMillis = millis;
- }
-
- public Date getJvmUpSince() {
- Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(calendar.getTimeInMillis() - getJvmUptimeMillis());
- return calendar.getTime();
- }
-
- public List<Performance> getPerformanceStats() {
- return performanceStats;
- }
-
- public long getFieldCacheMemory() {
- return fieldCacheMemory;
- }
-
- public long getFilterCacheMemory() {
- return filterCacheMemory;
- }
-
- private void initPerformanceStats(NodeStats nodesStats) {
- // Performance Stat
- performanceStats = new ArrayList<Performance>();
-
- // IndexStat
- long indexCount = nodesStats.getIndices().getIndexing().getTotal().getIndexCount();
- long indexTotalTime = nodesStats.getIndices().getIndexing().getTotal().getIndexTimeInMillis();
- performanceStats.add(
- new Performance("Average Indexing Time")
- .setWarnThreshold(10)
- .setErrorThreshold(50)
- .setMessage("Too complex documents or low IO/CPU")
- .setValue(indexCount > 0L ? indexTotalTime / (double) indexCount : 0.0));
-
- // Query stats
- long queryCount = nodesStats.getIndices().getSearch().getTotal().getQueryCount();
- long queryTotalTime = nodesStats.getIndices().getSearch().getTotal().getQueryTimeInMillis();
- performanceStats.add(
- new Performance("Average Querying Time")
- .setWarnThreshold(50)
- .setErrorThreshold(500)
- .setMessage("Inefficient query and/or filters")
- .setValue(queryCount > 0L ? queryTotalTime / (double) queryCount : 0.0));
-
- // Fetch stats
- long fetchCount = nodesStats.getIndices().getSearch().getTotal().getFetchCount();
- long fetchTotalTime = nodesStats.getIndices().getSearch().getTotal().getFetchTimeInMillis();
- performanceStats.add(
- new Performance("Average Fetching Time")
- .setWarnThreshold(8)
- .setErrorThreshold(15)
- .setMessage("Slow IO, fetch-size too large or documents too big")
- .setValue(fetchCount > 0L ? fetchTotalTime / (double) fetchCount : 0.0));
-
- // Get stats
- long getCount = nodesStats.getIndices().getGet().getCount();
- long getTotalTime = nodesStats.getIndices().getGet().getTimeInMillis();
- performanceStats.add(
- new Performance("Average Get Time")
- .setWarnThreshold(5)
- .setErrorThreshold(10)
- .setMessage("Slow IO")
- .setValue(getCount > 0L ? getTotalTime / (double) getCount : 0.0));
-
- // Refresh Stat
- long refreshCount = nodesStats.getIndices().getRefresh().getTotal();
- long refreshTotalTime = nodesStats.getIndices().getRefresh().getTotalTimeInMillis();
- performanceStats.add(
- new Performance("Average Refreshing Time")
- .setWarnThreshold(10)
- .setErrorThreshold(20)
- .setMessage("Slow IO")
- .setValue(refreshCount > 0L ? refreshTotalTime / (double) refreshCount : 0.0));
-
- // Field Cache
- fieldCacheMemory = nodesStats.getIndices().getFieldData().getMemorySizeInBytes();
- long fieldCacheEviction = nodesStats.getIndices().getFieldData().getEvictions();
- performanceStats.add(
- new Performance("Field Cache Eviction Count")
- .setWarnThreshold(1)
- .setErrorThreshold(1)
- .setMessage("Insufficient RAM available for queries")
- .setValue(fieldCacheEviction));
-
- // Filter Cache
- filterCacheMemory = nodesStats.getIndices().getFilterCache().getMemorySizeInBytes();
- long filterCacheEviction = nodesStats.getIndices().getFilterCache().getEvictions();
- performanceStats.add(
- new Performance("Filter Cache Eviction Count")
- .setWarnThreshold(1)
- .setErrorThreshold(1)
- .setMessage("Insufficient RAM or too many orphaned filters")
- .setValue(filterCacheEviction));
- }
-
- public static class Performance {
-
- private final String name;
- private String message;
- private double value;
- private double warnThreshold;
- private double errorThreshold;
- public Performance(String name) {
- this.name = name;
- }
-
- public Status getStatus() {
- if (value >= errorThreshold) {
- return Status.ERROR;
- } else if (value >= warnThreshold) {
- return Status.WARN;
- } else {
- return Status.OK;
- }
- }
-
- public String getName() {
- return name;
- }
-
- public String getMessage() {
- return message;
- }
-
- public Performance setMessage(String message) {
- this.message = message;
- return this;
- }
-
- public double getValue() {
- return value;
- }
-
- public Performance setValue(double value) {
- this.value = value;
- return this;
- }
-
- public Performance setWarnThreshold(long warnThreshold) {
- this.warnThreshold = warnThreshold;
- return this;
- }
-
- public Performance setErrorThreshold(long errorThreshold) {
- this.errorThreshold = errorThreshold;
- return this;
- }
-
- public static enum Status {
- OK, WARN, ERROR
- }
- }
-}
package org.sonar.server.search;
import org.apache.commons.lang.StringUtils;
-import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
-import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequestBuilder;
-import org.elasticsearch.action.admin.cluster.state.ClusterStateRequestBuilder;
-import org.elasticsearch.action.admin.cluster.stats.ClusterStatsRequestBuilder;
-import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder;
-import org.elasticsearch.action.admin.indices.flush.FlushRequestBuilder;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequestBuilder;
-import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequestBuilder;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.count.CountRequestBuilder;
import org.elasticsearch.action.delete.DeleteRequestBuilder;
import org.sonar.process.LoopbackAddress;
import org.sonar.process.ProcessConstants;
import org.sonar.server.es.request.ProxyBulkRequestBuilder;
-import org.sonar.server.es.request.ProxyClusterStateRequestBuilder;
-import org.sonar.server.es.request.ProxyClusterStatsRequestBuilder;
import org.sonar.server.es.request.ProxyCountRequestBuilder;
import org.sonar.server.es.request.ProxyCreateIndexRequestBuilder;
import org.sonar.server.es.request.ProxyDeleteByQueryRequestBuilder;
import org.sonar.server.es.request.ProxyDeleteRequestBuilder;
-import org.sonar.server.es.request.ProxyFlushRequestBuilder;
import org.sonar.server.es.request.ProxyGetRequestBuilder;
import org.sonar.server.es.request.ProxyIndicesExistsRequestBuilder;
-import org.sonar.server.es.request.ProxyIndicesStatsRequestBuilder;
import org.sonar.server.es.request.ProxyMultiGetRequestBuilder;
-import org.sonar.server.es.request.ProxyNodesStatsRequestBuilder;
import org.sonar.server.es.request.ProxyPutMappingRequestBuilder;
import org.sonar.server.es.request.ProxyRefreshRequestBuilder;
import org.sonar.server.es.request.ProxySearchRequestBuilder;
settings.getInt(ProcessConstants.SEARCH_PORT)));
}
- public ClusterHealth getClusterHealth() {
- ClusterHealth health = new ClusterHealth();
- ClusterStatsResponse clusterStatsResponse = this.prepareClusterStats().get();
-
- // Cluster health
- health.setClusterAvailable(clusterStatsResponse.getStatus() != ClusterHealthStatus.RED);
-
- // Number of nodes
- health.setNumberOfNodes(clusterStatsResponse.getNodesStats().getCounts().getTotal());
-
- return health;
- }
-
private void initLogging() {
ESLoggerFactory.setDefaultFactory(new Slf4jESLoggerFactory());
}
return new ProxyRefreshRequestBuilder(this).setIndices(indices);
}
- public FlushRequestBuilder prepareFlush(String... indices) {
- return new ProxyFlushRequestBuilder(this).setIndices(indices);
- }
-
- public IndicesStatsRequestBuilder prepareStats(String... indices) {
- return new ProxyIndicesStatsRequestBuilder(this).setIndices(indices);
- }
-
- public NodesStatsRequestBuilder prepareNodesStats(String... nodesIds) {
- return new ProxyNodesStatsRequestBuilder(this).setNodesIds(nodesIds);
- }
-
- public ClusterStatsRequestBuilder prepareClusterStats() {
- return new ProxyClusterStatsRequestBuilder(this);
- }
-
- public ClusterStateRequestBuilder prepareState() {
- return new ProxyClusterStateRequestBuilder(this);
- }
-
public IndicesExistsRequestBuilder prepareIndicesExist(String... indices) {
return new ProxyIndicesExistsRequestBuilder(this, indices);
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.search;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMap.Builder;
-import com.google.common.collect.Maps;
-import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
-import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequestBuilder;
-import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
-import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequestBuilder;
-import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
-
-import java.util.Map;
-import java.util.Map.Entry;
-
-public class SearchHealth {
-
- private SearchClient searchClient;
- private IndexClient indexClient;
-
- public SearchHealth(SearchClient searchClient, IndexClient indexClient) {
- this.searchClient = searchClient;
- this.indexClient = indexClient;
- }
-
- public ClusterHealth getClusterHealth() {
- return searchClient.getClusterHealth();
- }
-
- public Map<String, IndexHealth> getIndexHealth() {
- Builder<String, IndexHealth> builder = ImmutableMap.builder();
- for (Index index: indexClient.allIndices()) {
- IndexStat indexStat = index.getIndexStat();
- IndexHealth newIndexHealth = new IndexHealth();
- newIndexHealth.name = index.getIndexName() + "/" + index.getIndexType();
- newIndexHealth.documentCount = indexStat.getDocumentCount();
- newIndexHealth.lastSync = indexStat.getLastUpdate();
-
- IndicesStatsRequestBuilder statRequest = searchClient.prepareStats(index.getIndexName())
- .setTypes(index.getIndexType());
- IndicesStatsResponse indicesStatsResponse = statRequest.get();
- newIndexHealth.segmentCount = indicesStatsResponse.getTotal().getSegments().getCount();
- newIndexHealth.pendingDeletion = indicesStatsResponse.getTotal().getDocs().getDeleted();
-
- builder.put(newIndexHealth.name, newIndexHealth);
- }
- return builder.build();
- }
-
- public Map<String, NodeHealth > getNodesHealth() {
- NodesStatsRequestBuilder nodesStatsRequest = searchClient.prepareNodesStats().all();
- NodesStatsResponse nodesStats = nodesStatsRequest.get();
-
- Map<String, NodeHealth> health = Maps.newHashMap();
- for (Entry<String, NodeStats> nodeEntry: nodesStats.getNodesMap().entrySet()) {
- health.put(nodeEntry.getKey(), new NodeHealth(nodeEntry.getValue()));
- }
- return ImmutableMap.copyOf(health);
- }
-}
EsClient client = es.client();
client.start();
assertThat(client.nativeClient()).isNotNull();
- assertThat(client.getClusterHealth().isClusterAvailable()).isTrue();
assertThat(client.prepareBulk()).isInstanceOf(ProxyBulkRequestBuilder.class);
assertThat(client.prepareClusterStats()).isInstanceOf(ProxyClusterStatsRequestBuilder.class);
assertThat(client.prepareCount()).isInstanceOf(ProxyCountRequestBuilder.class);
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.search;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class NodeHealthPerformanceTest {
-
- @Test
- public void OK_threshold() throws Exception {
- NodeHealth.Performance OK = new NodeHealth.Performance("test")
- .setWarnThreshold(Integer.MAX_VALUE)
- .setErrorThreshold(Integer.MAX_VALUE)
- .setValue(1);
- assertThat(OK.getStatus()).isEqualTo(NodeHealth.Performance.Status.OK);
- }
-
- @Test
- public void WARN_threshold() throws Exception {
- NodeHealth.Performance OK = new NodeHealth.Performance("test")
- .setWarnThreshold(Integer.MIN_VALUE)
- .setErrorThreshold(Integer.MAX_VALUE)
- .setValue(1);
- assertThat(OK.getStatus()).isEqualTo(NodeHealth.Performance.Status.WARN);
- }
-
- @Test
- public void ERROR_threshold() throws Exception {
- NodeHealth.Performance OK = new NodeHealth.Performance("test")
- .setWarnThreshold(Integer.MIN_VALUE)
- .setErrorThreshold(Integer.MIN_VALUE)
- .setValue(1);
- assertThat(OK.getStatus()).isEqualTo(NodeHealth.Performance.Status.ERROR);
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.search;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.System2;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.rule.RuleTesting;
-import org.sonar.server.rule.db.RuleDao;
-import org.sonar.server.tester.ServerTester;
-
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class SearchHealthMediumTest {
-
- @ClassRule
- public static ServerTester tester = new ServerTester();
-
- DbSession dbSession;
-
- @Before
- public void setUp() throws Exception {
- dbSession = tester.get(DbClient.class).openSession(false);
- }
-
- @After
- public void tearDown() throws Exception {
- dbSession.close();
- }
-
- @Test
- public void get_search_health() {
- tester.get(RuleDao.class).insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "S001")));
- dbSession.commit();
-
- SearchHealth health = tester.get(SearchHealth.class);
- Date now = new Date();
-
- ClusterHealth clusterHealth = health.getClusterHealth();
- assertThat(clusterHealth.isClusterAvailable()).isTrue();
- assertThat(clusterHealth.getNumberOfNodes()).isEqualTo(1);
-
- NodeHealth nodeHealth = health.getNodesHealth().values().iterator().next();
- assertThat(nodeHealth.isMaster()).isTrue();
- assertThat(nodeHealth.getAddress()).contains(":");
- assertThat(nodeHealth.getJvmHeapUsedPercent()).contains("%");
- assertThat(nodeHealth.getFsUsedPercent()).contains("%");
- assertThat(nodeHealth.getJvmThreads()).isGreaterThanOrEqualTo(0L);
- assertThat(nodeHealth.getFieldCacheMemory()).isGreaterThanOrEqualTo(0L);
- assertThat(nodeHealth.getFilterCacheMemory()).isGreaterThanOrEqualTo(0L);
- assertThat(nodeHealth.getProcessCpuPercent()).contains("%");
- long openFiles = nodeHealth.getOpenFiles();
- if (!tester.get(System2.class).isOsWindows()) {
- assertThat(openFiles).isGreaterThanOrEqualTo(0L);
- }
- assertThat(nodeHealth.getJvmUpSince().before(now)).isTrue();
-
- List<NodeHealth.Performance> performances = nodeHealth.getPerformanceStats();
- assertThat(performances).hasSize(7);
- for (NodeHealth.Performance performance : performances) {
- assertThat(performance.getName()).isNotNull();
- assertThat(performance.getValue()).isNotNull();
- assertThat(performance.getMessage()).isNotNull();
- assertThat(performance.getStatus()).isNotNull();
- }
-
- Map<String, IndexHealth> indexHealth = health.getIndexHealth();
- assertThat(indexHealth).isNotEmpty();
- for (IndexHealth index : indexHealth.values()) {
- assertThat(index.getDocumentCount()).isGreaterThanOrEqualTo(0L);
- Date lastSync = index.getLastSynchronization();
- if (lastSync != null) {
- assertThat(lastSync.before(now)).isTrue();
- }
- assertThat(index.isOptimized()).isIn(true, false);
- }
- }
-
-}
Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerPlatformMonitoring::SonarQubeMonitor.java_class),
Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerPlatformMonitoring::DatabaseMonitor.java_class),
Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerPlatformMonitoring::SystemMonitor.java_class),
- Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerPlatformMonitoring::EsClusterMonitor.java_class),
- Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerPlatformMonitoring::EsNodesMonitor.java_class),
+ Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerPlatformMonitoring::EsMonitor.java_class),
Java::OrgSonarServerPlatform::Platform.component(Java::OrgSonarServerPlatformMonitoring::JvmPropertiesMonitor.java_class)
]
end
</tr>
</thead>
<tbody>
- <% monitor.attributes().entrySet().each do |attribute| %>
- <tr class="<%= cycle('even','odd') -%>">
- <td width="25%" nowrap="nowrap"><%= h attribute.getKey() -%></td>
- <td width="75%"><%= h attribute.getValue() -%></td>
- </tr>
+ <% monitor.attributes().to_hash.each do |attribute_key, attribute_value| %>
+
+ <% if attribute_value.respond_to?(:to_hash)
+ attribute_value.to_hash.each do |group_key, group_value|
+ %>
+ <tr class="<%= cycle('even','odd', :name => monitor.name()) -%>">
+ <td width="25%" nowrap="nowrap"><%= h "#{attribute_key} - #{group_key}" -%></td>
+ <td width="75%">
+ <table>
+ <% group_value.to_hash.each do |group_attr_key, group_attr_value| %>
+ <tr><td style="padding: 2px 10px 2px 0"><%= h group_attr_key -%></td><td><%= h group_attr_value -%></td></tr>
+ <% end %>
+ </table>
+ </td>
+ </tr>
+ <% end %>
+
+ <% else %>
+ <tr class="<%= cycle('even','odd', :name => monitor.name()) -%>">
+ <td width="25%" nowrap="nowrap"><%= h attribute_key -%></td>
+ <td width="75%"><%= h attribute_value -%></td>
+ </tr>
+ <% end %>
+
<% end %>
</tbody>
</table>
stream.value((Boolean) value);
} else if (value instanceof Date) {
valueDateTime((Date) value);
+ } else if (value instanceof Enum) {
+ stream.value(((Enum)value).name());
} else if (value instanceof Map) {
stream.beginObject();
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) value).entrySet()) {
.name("aFloat").valueObject(3.14)
.name("aLong").valueObject(42L)
.name("aList").valueObject(Arrays.asList("one", 2, "three"))
+ .name("anEnum").valueObject(ColorEnum.GREEN)
.name("aMap").valueObject(ImmutableMap.of("hello", "world", "good", "bye"))
.endObject().close();
- expect("{\"aString\":\"stringValue\",\"aBoolean\":true,\"aInt\":42,\"aFloat\":3.14,\"aLong\":42,\"aList\":[\"one\",2,\"three\"],\"aMap\":{\"hello\":\"world\",\"good\":\"bye\"}}");
+ expect("{\"aString\":\"stringValue\",\"aBoolean\":true,\"aInt\":42,\"aFloat\":3.14,\"aLong\":42,\"aList\":[\"one\",2,\"three\"],\"anEnum\":\"GREEN\",\"aMap\":{\"hello\":\"world\",\"good\":\"bye\"}}");
}
@Test
new JsonWriter(gson).beginArray();
}
+
+ private static enum ColorEnum {
+ RED, GREEN
+ }
}