SimpleCoreMetricVisitor.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.common.AnalysisException;
import org.apache.doris.monitor.jvm.JvmStats;
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 com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Iterator;
import java.util.Map;

/*
 * SimpleCoreMetricVisitor only show some core metrics of FE, with format:
 *      name type value
 * eg:
 *      jvm_young_used_percent DOUBLE 70.1
 *      query_latency_ms_75 LONG 2
 */
public class SimpleCoreMetricVisitor extends MetricVisitor {
    private static final Logger LOG = LogManager.getLogger(SimpleCoreMetricVisitor.class);
    private static final String TYPE_LONG = "LONG";
    private static final String TYPE_DOUBLE = "DOUBLE";

    public static final String JVM_YOUNG_USED_PERCENT = "jvm_young_used_percent";
    public static final String JVM_OLD_USED_PERCENT = "jvm_old_used_percent";
    public static final String JVM_THREAD = "jvm_thread";

    public static final String MAX_JOURMAL_ID = "max_journal_id";
    public static final String CONNECTION_TOTAL = "connection_total";
    public static final String QUERY_LATENCY_MS = "query_latency_ms";

    public static final String QUERY_PER_SECOND = "qps";
    public static final String REQUEST_PER_SECOND = "rps";
    public static final String QUERY_ERR_RATE = "query_err_rate";

    public static final String MAX_TABLET_COMPACTION_SCORE = "max_tablet_compaction_score";

    private static final Map<String, String> CORE_METRICS = Maps.newHashMap();

    static {
        CORE_METRICS.put(MAX_JOURMAL_ID, TYPE_LONG);
        CORE_METRICS.put(CONNECTION_TOTAL, TYPE_LONG);
        CORE_METRICS.put(QUERY_LATENCY_MS, TYPE_LONG);
        CORE_METRICS.put(QUERY_PER_SECOND, TYPE_DOUBLE);
        CORE_METRICS.put(REQUEST_PER_SECOND, TYPE_DOUBLE);
        CORE_METRICS.put(QUERY_ERR_RATE, TYPE_DOUBLE);
        CORE_METRICS.put(MAX_TABLET_COMPACTION_SCORE, TYPE_LONG);
    }

    public SimpleCoreMetricVisitor() {
        super();
    }

    @Override
    public void visitJvm(JvmStats jvmStats) {
        Iterator<MemoryPool> memIter = jvmStats.getMem().iterator();
        while (memIter.hasNext()) {
            MemoryPool memPool = memIter.next();
            if (memPool.getName().equalsIgnoreCase("young")) {
                long used = memPool.getUsed().getBytes();
                long max = memPool.getMax().getBytes();
                String percent = String.format("%.1f", (double) used / (max + 1) * 100);
                sb.append(Joiner.on(" ").join(JVM_YOUNG_USED_PERCENT, TYPE_DOUBLE, percent)).append("\n");
            } else if (memPool.getName().equalsIgnoreCase("old")) {
                long used = memPool.getUsed().getBytes();
                long max = memPool.getMax().getBytes();
                String percent = String.format("%.1f", (double) used / (max + 1) * 100);
                sb.append(Joiner.on(" ").join(JVM_OLD_USED_PERCENT, TYPE_DOUBLE, percent)).append("\n");
            }
        }

        Threads threads = jvmStats.getThreads();
        sb.append(Joiner.on(" ").join(JVM_THREAD, TYPE_LONG, threads.getCount())).append("\n");
        return;
    }

    @Override
    public void visit(String prefix, Metric metric) {
        if (!CORE_METRICS.containsKey(metric.getName())) {
            return;
        }

        if (CORE_METRICS.get(metric.getName()).equals(TYPE_DOUBLE)) {
            sb.append(Joiner.on(" ").join(prefix + metric.getName(), TYPE_DOUBLE,
                    String.format("%.2f", Double.valueOf(metric.getValue().toString())))).append("\n");
        } else {
            sb.append(Joiner.on(" ")
                    .join(prefix + metric.getName(), CORE_METRICS.get(metric.getName()), metric.getValue().toString()))
                    .append("\n");
        }
    }

    @Override
    public void visitHistogram(String prefix, String name, Histogram histogram) {
        if (!CORE_METRICS.containsKey(name)) {
            return;
        }
        Snapshot snapshot = histogram.getSnapshot();
        sb.append(Joiner.on(" ").join(prefix + name + "_75", CORE_METRICS.get(name),
                String.format("%.0f", snapshot.get75thPercentile()))).append("\n");
        sb.append(Joiner.on(" ").join(prefix + name + "_95", CORE_METRICS.get(name),
                String.format("%.0f", snapshot.get95thPercentile()))).append("\n");
        sb.append(Joiner.on(" ").join(prefix + name + "_99", CORE_METRICS.get(name),
                String.format("%.0f", snapshot.get99thPercentile()))).append("\n");
    }

    @Override
    public void visitNodeInfo() {
        long feDeadNum = Env.getCurrentEnv().getFrontends(null).stream().filter(f -> !f.isAlive()).count();
        long beDeadNum = 0;
        try {
            beDeadNum = Env.getCurrentSystemInfo().getAllBackendsByAllCluster()
                    .values().stream().filter(b -> !b.isAlive())
                    .count();
        } catch (AnalysisException e) {
            LOG.warn("failed get backend, ", e);
        }
        long brokerDeadNum = Env.getCurrentEnv().getBrokerMgr().getAllBrokers().stream().filter(b -> !b.isAlive)
                .count();
        sb.append("doris_fe_frontend_dead_num").append(" ").append(feDeadNum).append("\n");
        sb.append("doris_fe_backend_dead_num").append(" ").append(beDeadNum).append("\n");
        sb.append("doris_fe_broker_dead_num").append(" ").append(brokerDeadNum).append("\n");
    }

    @Override
    public void visitCloudTableStats() {
        return;
    }

    @Override
    public void visitWorkloadGroup() {
        return;
    }

}