All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glowroot.agent.impl.AggregateCollector Maven / Gradle / Ivy

There is a newer version: 0.14.0-beta.3
Show newest version
/*
 * Copyright 2013-2018 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.agent.impl;

import java.util.List;

import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.Nullable;

import org.glowroot.agent.impl.Transaction.RootTimerCollector;
import org.glowroot.agent.impl.Transaction.ThreadStatsCollector;
import org.glowroot.agent.model.MutableAggregateTimer;
import org.glowroot.agent.model.QueryCollector;
import org.glowroot.agent.model.ServiceCallCollector;
import org.glowroot.agent.model.SharedQueryTextCollection;
import org.glowroot.agent.model.ThreadProfile;
import org.glowroot.agent.model.ThreadStats;
import org.glowroot.agent.model.TransactionTimer;
import org.glowroot.agent.shaded.org.glowroot.common.config.AdvancedConfig;
import org.glowroot.agent.shaded.org.glowroot.common.live.ImmutableOverviewAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.live.ImmutablePercentileAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.live.ImmutableThroughputAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.live.LiveAggregateRepository.OverviewAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.live.LiveAggregateRepository.PercentileAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.live.LiveAggregateRepository.ThroughputAggregate;
import org.glowroot.agent.shaded.org.glowroot.common.model.LazyHistogram;
import org.glowroot.agent.shaded.org.glowroot.common.model.LazyHistogram.ScratchBuffer;
import org.glowroot.agent.shaded.org.glowroot.common.model.MutableProfile;
import org.glowroot.agent.shaded.org.glowroot.common.model.OverallErrorSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.OverallSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.ProfileCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.TransactionNameErrorSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.model.TransactionNameSummaryCollector;
import org.glowroot.agent.shaded.org.glowroot.common.util.NotAvailableAware;
import org.glowroot.agent.shaded.org.glowroot.common.util.Styles;
import org.glowroot.agent.shaded.org.glowroot.wire.api.model.AggregateOuterClass.Aggregate;

import static org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Preconditions.checkNotNull;

// must be used under an appropriate lock
@Styles.Private
class AggregateCollector {

    private final @Nullable String transactionName;
    // aggregates use double instead of long to avoid (unlikely) 292 year nanosecond rollover
    private double totalDurationNanos;
    private long transactionCount;
    private long errorCount;
    private boolean asyncTransactions;
    private final RootTimerCollectorImpl mainThreadRootTimers = new RootTimerCollectorImpl();
    private final ThreadStatsCollectorImpl mainThreadStats = new ThreadStatsCollectorImpl();
    // histogram values are in nanoseconds, but with microsecond precision to reduce the number of
    // buckets (and memory) required
    private final LazyHistogram durationNanosHistogram = new LazyHistogram();
    private final QueryCollector queries;
    private final ServiceCallCollector serviceCalls;
    // lazy instantiated to reduce memory footprint
    private @MonotonicNonNull MutableAggregateTimer auxThreadRootTimer;
    private @MonotonicNonNull ThreadStatsCollectorImpl auxThreadStats;
    private @MonotonicNonNull RootTimerCollectorImpl asyncTimers;
    private @MonotonicNonNull MutableProfile mainThreadProfile;
    private @MonotonicNonNull MutableProfile auxThreadProfile;

    AggregateCollector(@Nullable String transactionName, int maxQueryAggregates,
            int maxServiceCallAggregates) {
        this.transactionName = transactionName;

        int queriesHardLimitMultiplierWhileBuilding = transactionName == null
                ? AdvancedConfig.OVERALL_AGGREGATE_QUERIES_HARD_LIMIT_MULTIPLIER
                : AdvancedConfig.TRANSACTION_AGGREGATE_QUERIES_HARD_LIMIT_MULTIPLIER;
        queries = new QueryCollector(maxQueryAggregates, queriesHardLimitMultiplierWhileBuilding);

        int serviceCallsHardLimitMultiplierWhileBuilding = transactionName == null
                ? AdvancedConfig.OVERALL_AGGREGATE_SERVICE_CALLS_HARD_LIMIT_MULTIPLIER
                : AdvancedConfig.TRANSACTION_AGGREGATE_SERVICE_CALLS_HARD_LIMIT_MULTIPLIER;
        serviceCalls = new ServiceCallCollector(maxServiceCallAggregates,
                serviceCallsHardLimitMultiplierWhileBuilding);
    }

    void mergeDataFrom(Transaction transaction) {
        long totalDurationNanos = transaction.getDurationNanos();
        this.totalDurationNanos += totalDurationNanos;
        transactionCount++;
        if (transaction.getErrorMessage() != null) {
            errorCount++;
        }
        if (transaction.isAsync()) {
            asyncTransactions = true;
        }
        mainThreadStats.mergeThreadStats(transaction.getMainThreadStats());
        mainThreadRootTimers.mergeRootTimer(transaction.getMainThreadRootTimer());
        if (transaction.hasAuxThreadContexts()) {
            if (auxThreadRootTimer == null) {
                auxThreadRootTimer = MutableAggregateTimer.createAuxThreadRootTimer();
            }
            transaction.mergeAuxThreadTimersInto(auxThreadRootTimer);
            if (auxThreadStats == null) {
                auxThreadStats = new ThreadStatsCollectorImpl();
            }
            transaction.mergeAuxThreadStatsInto(auxThreadStats);
        }
        if (transaction.hasAsyncTimers()) {
            if (asyncTimers == null) {
                asyncTimers = new RootTimerCollectorImpl();
            }
            transaction.mergeAsyncTimersInto(asyncTimers);
        }
        durationNanosHistogram.add(totalDurationNanos);
        transaction.mergeQueriesInto(queries);
        transaction.mergeServiceCallsInto(serviceCalls);
        ThreadProfile toBeMergedMainThreadProfile = transaction.getMainThreadProfile();
        if (toBeMergedMainThreadProfile != null) {
            if (mainThreadProfile == null) {
                mainThreadProfile = new MutableProfile();
            }
            toBeMergedMainThreadProfile.mergeInto(mainThreadProfile);
        }
        ThreadProfile toBeMergedAuxThreadProfile = transaction.getAuxThreadProfile();
        if (toBeMergedAuxThreadProfile != null) {
            if (auxThreadProfile == null) {
                auxThreadProfile = new MutableProfile();
            }
            toBeMergedAuxThreadProfile.mergeInto(auxThreadProfile);
        }
    }

    Aggregate build(SharedQueryTextCollection sharedQueryTextCollection,
            ScratchBuffer scratchBuffer) {
        Aggregate.Builder builder = Aggregate.newBuilder()
                .setTotalDurationNanos(totalDurationNanos)
                .setTransactionCount(transactionCount)
                .setErrorCount(errorCount)
                .setAsyncTransactions(asyncTransactions)
                .addAllMainThreadRootTimer(mainThreadRootTimers.toProto())
                .setMainThreadStats(mainThreadStats.toProto())
                .setDurationNanosHistogram(durationNanosHistogram.toProto(scratchBuffer));
        if (auxThreadRootTimer != null) {
            builder.setAuxThreadRootTimer(auxThreadRootTimer.toProto());
            // aux thread stats is non-null when aux thread root timer is non-null
            builder.setAuxThreadStats(checkNotNull(auxThreadStats).toProto());
        }
        if (asyncTimers != null) {
            builder.addAllAsyncTimer(asyncTimers.toProto());
        }
        if (queries != null) {
            builder.addAllQuery(queries.toAggregateProto(sharedQueryTextCollection, false));
        }
        if (serviceCalls != null) {
            builder.addAllServiceCall(serviceCalls.toAggregateProto());
        }
        if (mainThreadProfile != null) {
            builder.setMainThreadProfile(mainThreadProfile.toProto());
        }
        if (auxThreadProfile != null) {
            builder.setAuxThreadProfile(auxThreadProfile.toProto());
        }
        return builder.build();
    }

    void mergeOverallSummaryInto(OverallSummaryCollector collector) {
        collector.mergeSummary(totalDurationNanos, transactionCount, 0);
    }

    void mergeTransactionNameSummariesInto(TransactionNameSummaryCollector collector) {
        checkNotNull(transactionName);
        collector.collect(transactionName, totalDurationNanos, transactionCount, 0);
    }

    void mergeOverallErrorSummaryInto(OverallErrorSummaryCollector collector) {
        collector.mergeErrorSummary(errorCount, transactionCount, 0);
    }

    void mergeTransactionNameErrorSummariesInto(TransactionNameErrorSummaryCollector collector) {
        checkNotNull(transactionName);
        if (errorCount != 0) {
            collector.collect(transactionName, errorCount, transactionCount, 0);
        }
    }

    OverviewAggregate getOverviewAggregate(long captureTime) {
        ImmutableOverviewAggregate.Builder builder = ImmutableOverviewAggregate.builder()
                .captureTime(captureTime)
                .totalDurationNanos(totalDurationNanos)
                .transactionCount(transactionCount)
                .asyncTransactions(asyncTransactions)
                .mainThreadRootTimers(mainThreadRootTimers.toProto())
                .mainThreadStats(mainThreadStats.toProto());
        if (auxThreadRootTimer != null) {
            builder.auxThreadRootTimer(auxThreadRootTimer.toProto());
            // aux thread stats is non-null when aux thread root timer is non-null
            builder.auxThreadStats(checkNotNull(auxThreadStats).toProto());
        }
        if (asyncTimers != null) {
            builder.asyncTimers(asyncTimers.toProto());
        }
        return builder.build();
    }

    PercentileAggregate getPercentileAggregate(long captureTime) {
        return ImmutablePercentileAggregate.builder()
                .captureTime(captureTime)
                .totalDurationNanos(totalDurationNanos)
                .transactionCount(transactionCount)
                .durationNanosHistogram(durationNanosHistogram.toProto(new ScratchBuffer()))
                .build();
    }

    ThroughputAggregate getThroughputAggregate(long captureTime) {
        return ImmutableThroughputAggregate.builder()
                .captureTime(captureTime)
                .transactionCount(transactionCount)
                .errorCount(errorCount)
                .build();
    }

    @Nullable
    String getFullQueryText(String fullQueryTextSha1) {
        if (queries == null) {
            return null;
        }
        return queries.getFullQueryText(fullQueryTextSha1);
    }

    void mergeQueriesInto(org.glowroot.agent.shaded.org.glowroot.common.model.QueryCollector collector) {
        if (queries != null) {
            queries.mergeQueriesInto(collector);
        }
    }

    void mergeServiceCallsInto(org.glowroot.agent.shaded.org.glowroot.common.model.ServiceCallCollector collector) {
        if (serviceCalls != null) {
            serviceCalls.mergeServiceCallsInto(collector);
        }
    }

    void mergeMainThreadProfilesInto(ProfileCollector collector) {
        if (mainThreadProfile != null) {
            collector.mergeProfile(mainThreadProfile.toProto());
        }
    }

    void mergeAuxThreadProfilesInto(ProfileCollector collector) {
        if (auxThreadProfile != null) {
            collector.mergeProfile(auxThreadProfile.toProto());
        }
    }

    private static class RootTimerCollectorImpl implements RootTimerCollector {

        List rootMutableTimers = Lists.newArrayList();

        @Override
        public void mergeRootTimer(TransactionTimer toBeMergedRootTimer) {
            for (MutableAggregateTimer rootTimer : rootMutableTimers) {
                if (toBeMergedRootTimer.getName().equals(rootTimer.getName())
                        && toBeMergedRootTimer.isExtended() == rootTimer.isExtended()) {
                    rootTimer.addDataFrom(toBeMergedRootTimer);
                    return;
                }
            }
            MutableAggregateTimer rootTimer = new MutableAggregateTimer(
                    toBeMergedRootTimer.getName(), toBeMergedRootTimer.isExtended());
            rootTimer.addDataFrom(toBeMergedRootTimer);
            rootMutableTimers.add(rootTimer);
        }

        private List toProto() {
            List rootTimers = Lists.newArrayList();
            for (MutableAggregateTimer rootMutableTimer : rootMutableTimers) {
                rootTimers.add(rootMutableTimer.toProto());
            }
            return rootTimers;
        }
    }

    private static class ThreadStatsCollectorImpl implements ThreadStatsCollector {

        // aggregates use double instead of long to avoid (unlikely) 292 year nanosecond rollover
        private double totalCpuNanos;
        private long totalBlockedMillis;
        private long totalWaitedMillis;
        private double totalAllocatedBytes;

        @Override
        public void mergeThreadStats(ThreadStats threadStats) {
            totalCpuNanos = NotAvailableAware.add(totalCpuNanos, threadStats.getCpuNanos());
            totalBlockedMillis =
                    NotAvailableAware.add(totalBlockedMillis, threadStats.getBlockedMillis());
            totalWaitedMillis =
                    NotAvailableAware.add(totalWaitedMillis, threadStats.getWaitedMillis());
            totalAllocatedBytes = NotAvailableAware.add(totalAllocatedBytes,
                    threadStats.getAllocatedBytes());
        }

        public Aggregate.ThreadStats toProto() {
            return Aggregate.ThreadStats.newBuilder()
                    .setTotalCpuNanos(totalCpuNanos)
                    .setTotalBlockedNanos(NotAvailableAware.millisToNanos(totalBlockedMillis))
                    .setTotalWaitedNanos(NotAvailableAware.millisToNanos(totalWaitedMillis))
                    .setTotalAllocatedBytes(totalAllocatedBytes)
                    .build();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy