JsonMetricVisitor.java
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
package org.apache.doris.metric;
import org.apache.doris.catalog.Env;
import org.apache.doris.monitor.jvm.JvmStats;
import org.apache.doris.monitor.jvm.JvmStats.GarbageCollector;
import org.apache.doris.monitor.jvm.JvmStats.MemoryPool;
import org.apache.doris.monitor.jvm.JvmStats.Threads;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Snapshot;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class JsonMetricVisitor extends MetricVisitor {
private int metricOrdinal = 0;
private int histogramOrdinal = 0;
private boolean closed = false;
// jvm
private static final String JVM_HEAP_SIZE_BYTES = "jvm_heap_size_bytes";
private static final String JVM_NON_HEAP_SIZE_BYTES = "jvm_non_heap_size_bytes";
private static final String JVM_YOUNG_SIZE_BYTES = "jvm_young_size_bytes";
private static final String JVM_OLD_SIZE_BYTES = "jvm_old_size_bytes";
private static final String JVM_THREAD = "jvm_thread";
private static final String JVM_GC = "jvm_gc";
public JsonMetricVisitor() {
super();
sb.append("[\n");
}
@Override
public void visitJvm(JvmStats jvmStats) {
// heap
setJvmJsonMetric(sb, JVM_HEAP_SIZE_BYTES, null, "max", "bytes", jvmStats.getMem().getHeapMax().getBytes());
setJvmJsonMetric(sb, JVM_HEAP_SIZE_BYTES, null, "committed", "bytes",
jvmStats.getMem().getHeapCommitted().getBytes());
setJvmJsonMetric(sb, JVM_HEAP_SIZE_BYTES, null, "used", "bytes", jvmStats.getMem().getHeapUsed().getBytes());
// non heap
setJvmJsonMetric(sb, JVM_NON_HEAP_SIZE_BYTES, null, "committed", "bytes",
jvmStats.getMem().getNonHeapCommitted().getBytes());
setJvmJsonMetric(sb, JVM_NON_HEAP_SIZE_BYTES, null, "used", "bytes",
jvmStats.getMem().getNonHeapUsed().getBytes());
// mem pool
Iterator<MemoryPool> memIter = jvmStats.getMem().iterator();
while (memIter.hasNext()) {
MemoryPool memPool = memIter.next();
if (memPool.getName().equalsIgnoreCase("young")) {
setJvmJsonMetric(sb, JVM_YOUNG_SIZE_BYTES, null, "used", "bytes", memPool.getUsed().getBytes());
setJvmJsonMetric(sb, JVM_YOUNG_SIZE_BYTES, null, "peak_used", "bytes",
memPool.getPeakUsed().getBytes());
setJvmJsonMetric(sb, JVM_YOUNG_SIZE_BYTES, null, "max", "bytes", memPool.getMax().getBytes());
} else if (memPool.getName().equalsIgnoreCase("old")) {
setJvmJsonMetric(sb, JVM_OLD_SIZE_BYTES, null, "used", "bytes", memPool.getUsed().getBytes());
setJvmJsonMetric(sb, JVM_OLD_SIZE_BYTES, null, "peak_used", "bytes", memPool.getPeakUsed().getBytes());
setJvmJsonMetric(sb, JVM_OLD_SIZE_BYTES, null, "max", "bytes", memPool.getMax().getBytes());
}
}
// gc
for (GarbageCollector gc : jvmStats.getGc()) {
setJvmJsonMetric(sb, JVM_GC, gc.getName() + " Count", "count", "nounit", gc.getCollectionCount());
setJvmJsonMetric(sb, JVM_GC, gc.getName() + " Time", "time", "milliseconds",
gc.getCollectionTime().getMillis());
}
// threads
Threads threads = jvmStats.getThreads();
setJvmJsonMetric(sb, JVM_THREAD, null, "count", "nounit", threads.getCount());
setJvmJsonMetric(sb, JVM_THREAD, null, "peak_count", "nounit", threads.getPeakCount());
setJvmJsonMetric(sb, JVM_THREAD, null, "new_count", "nounit", threads.getThreadsNewCount());
setJvmJsonMetric(sb, JVM_THREAD, null, "runnable_count", "nounit", threads.getThreadsRunnableCount());
setJvmJsonMetric(sb, JVM_THREAD, null, "blocked_count", "nounit", threads.getThreadsBlockedCount());
setJvmJsonMetric(sb, JVM_THREAD, null, "waiting_count", "nounit", threads.getThreadsWaitingCount());
setJvmJsonMetric(sb, JVM_THREAD, null, "timed_waiting_count", "nounit", threads.getThreadsTimedWaitingCount());
setJvmJsonMetric(sb, JVM_THREAD, null, "terminated_count", "nounit", threads.getThreadsTerminatedCount());
}
private void setJvmJsonMetric(StringBuilder sb, String metric, String name, String type, String unit, long value) {
sb.append("{\n\t\"tags\":\n\t{\n");
sb.append("\t\t\"metric\":\"").append(metric).append("\"");
if (name != null) {
sb.append(",\n");
sb.append("\t\t\"name\":\"").append(name).append("\"\n");
}
if (type != null) {
sb.append(",\n");
sb.append("\t\t\"type\":\"").append(type).append("\"\n");
}
sb.append("\n\t},\n");
sb.append("\t\"unit\":\"").append(unit).append("\",\n");
sb.append("\t\"value\":").append(value).append("\n}");
sb.append(",\n");
}
@Override
public void visit(String prefix, @SuppressWarnings("rawtypes") Metric metric) {
if (metricOrdinal++ != 0) {
sb.append(",\n");
}
sb.append("{\n\t\"tags\":\n\t{\n");
sb.append("\t\t\"metric\":\"").append(prefix).append(metric.getName()).append("\"");
// name
@SuppressWarnings("unchecked") List<MetricLabel> labels = metric.getLabels();
if (!labels.isEmpty()) {
sb.append(",\n");
int i = 0;
for (MetricLabel label : labels) {
if (i++ > 0) {
sb.append(",\n");
}
sb.append("\t\t\"").append(label.getKey()).append("\":\"").append(label.getValue()).append("\"");
}
}
sb.append("\n\t},\n");
sb.append("\t\"unit\":\"").append(metric.getUnit().name().toLowerCase()).append("\",\n");
// value
sb.append("\t\"value\":").append(metric.getValue().toString()).append("\n}");
}
@Override
public void visitHistogram(String prefix, String name, Histogram histogram) {
if (histogramOrdinal++ == 0) {
sb.append(",\n");
}
// part.part.part.k1=v1.k2=v2
List<String> names = new ArrayList<>();
List<String> tags = new ArrayList<>();
for (String part : name.split("\\.")) {
String[] kv = part.split("=");
if (kv.length == 1) {
names.add(kv[0]);
} else if (kv.length == 2) {
tags.add(String.format("\"%s\":\"%s\"", kv[0], kv[1]));
}
}
final String fullName = prefix + String.join("_", names);
Snapshot snapshot = histogram.getSnapshot();
setHistogramJsonMetric(sb, fullName, "\"quantile\":\"0.75\"", tags, snapshot.get75thPercentile());
setHistogramJsonMetric(sb, fullName, "\"quantile\":\"0.95\"", tags, snapshot.get95thPercentile());
setHistogramJsonMetric(sb, fullName, "\"quantile\":\"0.98\"", tags, snapshot.get98thPercentile());
setHistogramJsonMetric(sb, fullName, "\"quantile\":\"0.99\"", tags, snapshot.get99thPercentile());
setHistogramJsonMetric(sb, fullName, "\"quantile\":\"0.999\"", tags, snapshot.get999thPercentile());
setHistogramJsonMetric(sb, fullName.concat("_sum"), null, null,
histogram.getCount() * snapshot.getMean());
setHistogramJsonMetric(sb, fullName.concat("_count"), null, null, histogram.getCount());
}
private void setHistogramJsonMetric(StringBuilder sb, String metric, String quantile,
List<String> tags, double value) {
sb.append("{\n\t\"tags\":\n\t{\n");
sb.append("\t\t\"metric\":\"").append(metric).append("\"");
if (quantile != null) {
sb.append(",\n");
sb.append("\t\t").append(quantile).append("\n");
}
if (tags != null) {
for (String tag : tags) {
sb.append(",\n");
sb.append("\t\t").append(tag).append("\n");
}
}
sb.append("\n\t},\n");
sb.append("\t\"unit\":\"").append("ms").append("\",\n");
sb.append("\t\"value\":").append(value).append("\n}");
sb.append(",\n");
}
@Override
public void visitNodeInfo() {
if (Env.getCurrentEnv().isMaster()) {
setNodeInfo(sb, "node_info", "is_master", null, 1, false);
}
setNodeInfo(sb, "node_info", "fe_node_num", "total",
Env.getCurrentEnv().getFrontends(null).size(), false);
setNodeInfo(sb, "node_info", "be_node_num", "total",
Env.getCurrentSystemInfo().getAllBackendIds(false).size(), false);
setNodeInfo(sb, "node_info", "be_node_num", "alive",
Env.getCurrentSystemInfo().getAllBackendIds(true).size(), false);
setNodeInfo(sb, "node_info", "be_node_num", "decommissioned",
Env.getCurrentSystemInfo().getDecommissionedBackendIds().size(), false);
setNodeInfo(sb, "node_info", "be_node_num", "dead",
Env.getCurrentEnv().getBrokerMgr().getAllBrokers().stream().filter(b -> !b.isAlive).count(), true);
}
@Override
public void visitCloudTableStats() {
return;
}
private void setNodeInfo(StringBuilder sb, String metric, String type,
String status, long value, boolean lastMetric) {
sb.append("{\n\t\"tags\":\n\t{\n");
sb.append("\t\t\"metric\":\"").append(metric).append("\"");
if (type != null) {
sb.append(",\n");
sb.append("\t\t\"type\":\"").append(type).append("\"");
}
if (status != null) {
sb.append(",\n");
sb.append("\t\t\"state\":\"").append(status).append("\"\n");
}
sb.append("\n\t},\n");
sb.append("\t\"unit\":\"").append("nounit").append("\",\n");
sb.append("\t\"value\":").append(value).append("\n}");
if (!lastMetric) {
sb.append(",\n");
}
}
@Override
public void visitWorkloadGroup() {
return;
}
@Override
public String finish() {
if (!closed) {
sb.append("\n]");
closed = true;
}
return sb.toString();
}
}