org.glowroot.common2.repo.util.MetricService.java Source code

Java tutorial

Introduction

Here is the source code for org.glowroot.common2.repo.util.MetricService.java

Source

/*
 * Copyright 2015-2019 the original author or authors.
 *
 * 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.
 */
package org.glowroot.common2.repo.util;

import java.util.List;

import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.Nullable;

import org.glowroot.common.live.ImmutableAggregateQuery;
import org.glowroot.common.live.LiveAggregateRepository.OverviewAggregate;
import org.glowroot.common.live.LiveAggregateRepository.PercentileAggregate;
import org.glowroot.common.live.LiveAggregateRepository.ThroughputAggregate;
import org.glowroot.common.model.LazyHistogram;
import org.glowroot.common2.repo.AggregateRepository;
import org.glowroot.common2.repo.GaugeValueRepository;
import org.glowroot.common2.repo.ImmutableTraceQuery;
import org.glowroot.common2.repo.TraceRepository;
import org.glowroot.common2.repo.util.RollupLevelService.DataKind;
import org.glowroot.wire.api.model.AgentConfigOuterClass.AgentConfig.AlertConfig.AlertCondition.MetricCondition;
import org.glowroot.wire.api.model.CollectorServiceOuterClass.GaugeValueMessage.GaugeValue;

import static com.google.common.base.Preconditions.checkState;

class MetricService {

    private static final double NANOSECONDS_PER_MILLISECOND = 1000000.0;

    private final AggregateRepository aggregateRepository;
    private final GaugeValueRepository gaugeValueRepository;
    private final TraceRepository traceRepository;
    private final RollupLevelService rollupLevelService;

    public MetricService(AggregateRepository aggregateRepository, GaugeValueRepository gaugeValueRepository,
            TraceRepository traceRepository, RollupLevelService rollupLevelService) {
        this.aggregateRepository = aggregateRepository;
        this.gaugeValueRepository = gaugeValueRepository;
        this.traceRepository = traceRepository;
        this.rollupLevelService = rollupLevelService;
    }

    @Nullable
    Number getMetricValue(String agentRollupId, MetricCondition metricCondition, long startTime, long endTime)
            throws Exception {
        String metric = metricCondition.getMetric();
        if (metric.equals("transaction:x-percentile")) {
            return getTransactionDurationPercentile(agentRollupId, metricCondition.getTransactionType(),
                    Strings.emptyToNull(metricCondition.getTransactionName()),
                    metricCondition.getPercentile().getValue(), startTime, endTime);
        } else if (metric.equals("transaction:average")) {
            return getTransactionAverage(agentRollupId, metricCondition.getTransactionType(),
                    Strings.emptyToNull(metricCondition.getTransactionName()), startTime, endTime);
        } else if (metric.equals("transaction:count")) {
            return getTransactionCount(agentRollupId, metricCondition.getTransactionType(),
                    Strings.emptyToNull(metricCondition.getTransactionName()), startTime, endTime);
        } else if (metric.equals("error:rate")) {
            return getErrorRate(agentRollupId, metricCondition.getTransactionType(),
                    Strings.emptyToNull(metricCondition.getTransactionName()), startTime, endTime);
        } else if (metric.equals("error:count")) {
            return getErrorCount(agentRollupId, metricCondition.getTransactionType(),
                    Strings.emptyToNull(metricCondition.getTransactionName()),
                    metricCondition.getErrorMessageFilter(), startTime, endTime);
        } else if (metric.startsWith("gauge:")) {
            return getGaugeValue(agentRollupId, metric.substring("gauge:".length()), startTime, endTime);
        } else {
            throw new IllegalStateException("Unexpected metric: " + metric);
        }
    }

    private @Nullable Double getTransactionDurationPercentile(String agentRollupId, String transactionType,
            @Nullable String transactionName, double percentile, long startTime, long endTime) throws Exception {
        int rollupLevel = rollupLevelService.getRollupLevelForView(startTime, endTime, DataKind.GENERAL);
        // startTime + 1 in order to not include the aggregate value at startTime
        List<PercentileAggregate> aggregates = aggregateRepository.readPercentileAggregates(agentRollupId,
                ImmutableAggregateQuery.builder().transactionType(transactionType).transactionName(transactionName)
                        .from(startTime + 1).to(endTime).rollupLevel(rollupLevel).build());
        if (aggregates.isEmpty()) {
            return null;
        }
        LazyHistogram durationNanosHistogram = new LazyHistogram();
        for (PercentileAggregate aggregate : aggregates) {
            durationNanosHistogram.merge(aggregate.durationNanosHistogram());
        }
        return durationNanosHistogram.getValueAtPercentile(percentile) / NANOSECONDS_PER_MILLISECOND;
    }

    private @Nullable Double getTransactionAverage(String agentRollupId, String transactionType,
            @Nullable String transactionName, long startTime, long endTime) throws Exception {
        List<OverviewAggregate> aggregates = getOverviewAggregates(agentRollupId, transactionType, transactionName,
                startTime, endTime);
        if (aggregates.isEmpty()) {
            return null;
        }
        double totalDurationNanos = 0;
        long totalTransactionCount = 0;
        for (OverviewAggregate aggregate : aggregates) {
            totalDurationNanos += aggregate.totalDurationNanos();
            totalTransactionCount += aggregate.transactionCount();
        }
        // individual aggregate transaction counts cannot be zero, and aggregates is non-empty
        // (see above conditional), so totalTransactionCount is guaranteed non-zero
        checkState(totalTransactionCount != 0);
        return totalDurationNanos / (totalTransactionCount * NANOSECONDS_PER_MILLISECOND);
    }

    public long getTransactionCount(String agentRollupId, String transactionType, @Nullable String transactionName,
            long startTime, long endTime) throws Exception {
        List<ThroughputAggregate> throughputAggregates = getThroughputAggregates(agentRollupId, transactionType,
                transactionName, startTime, endTime);
        long totalTransactionCount = 0;
        for (ThroughputAggregate throughputAggregate : throughputAggregates) {
            totalTransactionCount += throughputAggregate.transactionCount();
        }
        return totalTransactionCount;
    }

    private @Nullable Double getErrorRate(String agentRollupId, String transactionType,
            @Nullable String transactionName, long startTime, long endTime) throws Exception {
        List<ThroughputAggregate> aggregates = getThroughputAggregates(agentRollupId, transactionType,
                transactionName, startTime, endTime);
        if (aggregates.isEmpty()) {
            return null;
        }
        long totalTransactionCount = 0;
        long totalErrorCount = 0;
        for (ThroughputAggregate aggregate : aggregates) {
            totalTransactionCount += aggregate.transactionCount();
            totalErrorCount += MoreObjects.firstNonNull(aggregate.errorCount(), 0L);
        }
        // individual aggregate transaction counts cannot be zero, and aggregates is non-empty
        // (see above conditional), so totalTransactionCount is guaranteed non-zero
        checkState(totalTransactionCount != 0);
        return (100.0 * totalErrorCount) / totalTransactionCount;
    }

    private long getErrorCount(String agentRollupId, String transactionType, @Nullable String transactionName,
            @Nullable String errorMessageFilter, long startTime, long endTime) throws Exception {
        if (errorMessageFilter == null) {
            List<ThroughputAggregate> aggregates = getThroughputAggregates(agentRollupId, transactionType,
                    transactionName, startTime, endTime);
            long totalErrorCount = 0;
            for (ThroughputAggregate aggregate : aggregates) {
                totalErrorCount += MoreObjects.firstNonNull(aggregate.errorCount(), 0L);
            }
            return totalErrorCount;
        } else {
            ImmutableTraceQuery traceQuery = ImmutableTraceQuery.builder().transactionType(transactionType)
                    .transactionName(transactionName).from(startTime).to(endTime).build();
            return traceRepository.readErrorMessageCount(agentRollupId, traceQuery, errorMessageFilter);

        }
    }

    private @Nullable Double getGaugeValue(String agentRollupId, String gaugeName, long startTime, long endTime)
            throws Exception {
        int rollupLevel = rollupLevelService.getGaugeRollupLevelForView(startTime, endTime,
                agentRollupId.endsWith("::"));
        // startTime + 1 in order to not include the gauge value at startTime
        List<GaugeValue> gaugeValues = gaugeValueRepository.readGaugeValues(agentRollupId, gaugeName, startTime + 1,
                endTime, rollupLevel);
        if (gaugeValues.isEmpty()) {
            return null;
        }
        double totalWeightedValue = 0;
        long totalWeight = 0;
        for (GaugeValue gaugeValue : gaugeValues) {
            totalWeightedValue += gaugeValue.getValue() * gaugeValue.getWeight();
            totalWeight += gaugeValue.getWeight();
        }
        // individual gauge value weights cannot be zero, and gaugeValues is non-empty
        // (see above conditional), so totalWeight is guaranteed non-zero
        checkState(totalWeight != 0);
        return totalWeightedValue / totalWeight;
    }

    private List<ThroughputAggregate> getThroughputAggregates(String agentRollupId, String transactionType,
            @Nullable String transactionName, long startTime, long endTime) throws Exception {
        int rollupLevel = rollupLevelService.getRollupLevelForView(startTime, endTime, DataKind.GENERAL);
        // startTime + 1 in order to not include the aggregate at startTime
        return aggregateRepository.readThroughputAggregates(agentRollupId,
                ImmutableAggregateQuery.builder().transactionType(transactionType).transactionName(transactionName)
                        .from(startTime + 1).to(endTime).rollupLevel(rollupLevel).build());
    }

    private List<OverviewAggregate> getOverviewAggregates(String agentRollupId, String transactionType,
            @Nullable String transactionName, long startTime, long endTime) throws Exception {
        int rollupLevel = rollupLevelService.getRollupLevelForView(startTime, endTime, DataKind.GENERAL);
        // startTime + 1 in order to not include the aggregate at startTime
        return aggregateRepository.readOverviewAggregates(agentRollupId,
                ImmutableAggregateQuery.builder().transactionType(transactionType).transactionName(transactionName)
                        .from(startTime + 1).to(endTime).rollupLevel(rollupLevel).build());
    }
}