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

org.glowroot.agent.live.LiveTraceRepositoryImpl Maven / Gradle / Ivy

There is a newer version: 0.9.24
Show newest version
/*
 * Copyright 2015-2016 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.live;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import javax.annotation.Nullable;

import org.glowroot.agent.shaded.google.common.annotations.VisibleForTesting;
import org.glowroot.agent.shaded.google.common.base.Function;
import org.glowroot.agent.shaded.google.common.base.Ticker;
import org.glowroot.agent.shaded.google.common.collect.ImmutableList;
import org.glowroot.agent.shaded.google.common.collect.Iterables;
import org.glowroot.agent.shaded.google.common.collect.Lists;
import org.glowroot.agent.shaded.google.common.collect.Ordering;

import org.glowroot.agent.impl.TransactionCollector;
import org.glowroot.agent.impl.TransactionRegistry;
import org.glowroot.agent.model.ErrorMessage;
import org.glowroot.agent.model.TraceCreator;
import org.glowroot.agent.model.Transaction;
import org.glowroot.agent.shaded.glowroot.common.live.ImmutableTracePoint;
import org.glowroot.agent.shaded.glowroot.common.live.LiveTraceRepository;
import org.glowroot.agent.shaded.glowroot.common.util.Clock;
import org.glowroot.agent.shaded.glowroot.wire.api.model.ProfileOuterClass.Profile;
import org.glowroot.agent.shaded.glowroot.wire.api.model.TraceOuterClass.Trace;

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

public class LiveTraceRepositoryImpl implements LiveTraceRepository {

    private final TransactionRegistry transactionRegistry;
    private final TransactionCollector transactionCollector;
    private final Clock clock;
    private final Ticker ticker;

    public LiveTraceRepositoryImpl(TransactionRegistry transactionRegistry,
            TransactionCollector transactionCollector, Clock clock, Ticker ticker) {
        this.transactionRegistry = transactionRegistry;
        this.transactionCollector = transactionCollector;
        this.clock = clock;
        this.ticker = ticker;
    }

    // checks active traces first, then pending traces (and finally caller should check stored
    // traces) to make sure that the trace is not missed if it is in transition between these states
    @Override
    public @Nullable Trace.Header getHeader(String agentId, String traceId) throws IOException {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                return createTraceHeader(transaction);
            }
        }
        return null;
    }

    // this is only called if the trace does have traces, so empty list response means trace was not
    // found (e.g. has expired)
    @Override
    public List getEntries(String agentId, String traceId) {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                return transaction.getEntriesProtobuf(ticker.read());
            }
        }
        return ImmutableList.of();
    }

    @Override
    public @Nullable Profile getMainThreadProfile(String agentId, String traceId)
            throws IOException {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                return transaction.getMainThreadProfileProtobuf();
            }
        }
        return null;
    }

    @Override
    public @Nullable Profile getAuxThreadProfile(String agentId, String traceId)
            throws IOException {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                return transaction.getAuxThreadProfileProtobuf();
            }
        }
        return null;
    }

    @Override
    public @Nullable Trace getFullTrace(String agentId, String traceId) throws IOException {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                return createFullTrace(transaction);
            }
        }
        return null;
    }

    @Override
    public int getMatchingTraceCount(String agentId, String transactionType,
            @Nullable String transactionName) {
        // include active traces, this is mostly for the case where there is just a single very
        // long running active trace and it would be misleading to display Traces (0) on the tab
        int count = 0;
        for (Transaction transaction : transactionRegistry.getTransactions()) {
            // don't include partially stored traces since those are already counted above
            if (matchesActive(transaction, transactionType, transactionName)
                    && !transaction.isPartiallyStored()) {
                count++;
            }
        }
        return count;
    }

    @Override
    public List getMatchingActiveTracePoints(TraceKind traceKind, String agentId,
            String transactionType, @Nullable String transactionName, TracePointFilter filter,
            int limit, long captureTime, long captureTick) {
        List activeTracePoints = Lists.newArrayList();
        for (Transaction transaction : transactionRegistry.getTransactions()) {
            long startTick = transaction.getStartTick();
            if (matches(transaction, traceKind, transactionType, transactionName, filter)
                    && startTick < captureTick) {
                activeTracePoints.add(ImmutableTracePoint.builder()
                        .agentId(agentId)
                        .traceId(transaction.getTraceId())
                        .captureTime(captureTime)
                        .durationNanos(captureTick - startTick)
                        .error(transaction.getErrorMessage() != null)
                        .build());
            }
        }
        Collections.sort(activeTracePoints,
                Ordering.natural().reverse().onResultOf(new Function() {
                    @Override
                    public Long apply(@Nullable TracePoint tracePoint) {
                        checkNotNull(tracePoint);
                        return tracePoint.durationNanos();
                    }
                }));
        if (limit != 0 && activeTracePoints.size() > limit) {
            activeTracePoints = activeTracePoints.subList(0, limit);
        }
        return activeTracePoints;
    }

    @Override
    public List getMatchingPendingPoints(TraceKind traceKind, String agentId,
            String transactionType, @Nullable String transactionName, TracePointFilter filter,
            long captureTime) {
        List points = Lists.newArrayList();
        for (Transaction transaction : transactionCollector.getPendingTransactions()) {
            if (matches(transaction, traceKind, transactionType, transactionName, filter)) {
                points.add(ImmutableTracePoint.builder()
                        .agentId(agentId)
                        .traceId(transaction.getTraceId())
                        .captureTime(captureTime)
                        .durationNanos(transaction.getDurationNanos())
                        .error(transaction.getErrorMessage() != null)
                        .build());
            }
        }
        return points;
    }

    @VisibleForTesting
    boolean matchesActive(Transaction transaction, String transactionType,
            @Nullable String transactionName) {
        if (!transactionCollector.shouldStoreSlow(transaction)) {
            return false;
        }
        if (!transactionType.equals(transaction.getTransactionType())) {
            return false;
        }
        if (transactionName != null && !transactionName.equals(transaction.getTransactionName())) {
            return false;
        }
        return true;
    }

    private Trace.Header createTraceHeader(Transaction transaction) throws IOException {
        // capture time before checking if complete to guard against condition where partial
        // trace header is created with captureTime > the real (completed) capture time
        long captureTime = clock.currentTimeMillis();
        long captureTick = ticker.read();
        if (transaction.isCompleted()) {
            return TraceCreator.createCompletedTraceHeader(transaction);
        } else {
            return TraceCreator.createPartialTraceHeader(transaction, captureTime, captureTick);
        }
    }

    private Trace createFullTrace(Transaction transaction) throws IOException {
        if (transaction.isCompleted()) {
            return TraceCreator.createCompletedTrace(transaction, true);
        } else {
            return TraceCreator.createPartialTrace(transaction, clock.currentTimeMillis(),
                    ticker.read());
        }
    }

    private boolean matches(Transaction transaction, TraceKind traceKind, String transactionType,
            @Nullable String transactionName, TracePointFilter filter) {
        ErrorMessage errorMessage = transaction.getErrorMessage();
        return matchesKind(transaction, traceKind)
                && matchesTransactionType(transaction, transactionType)
                && matchesTransactionName(transaction, transactionName)
                && filter.matchesDuration(transaction.getDurationNanos())
                && filter.matchesHeadline(transaction.getHeadline())
                && filter.matchesError(errorMessage == null ? "" : errorMessage.message())
                && filter.matchesUser(transaction.getUser())
                && filter.matchesAttributes(transaction.getAttributes().asMap());
    }

    private boolean matchesKind(Transaction transaction, TraceKind traceKind) {
        if (traceKind == TraceKind.SLOW) {
            return transactionCollector.shouldStoreSlow(transaction);
        } else {
            // TraceKind.ERROR
            return transactionCollector.shouldStoreError(transaction);
        }
    }

    private boolean matchesTransactionType(Transaction transaction, String transactionType) {
        return transactionType.equals(transaction.getTransactionType());
    }

    private boolean matchesTransactionName(Transaction transaction,
            @Nullable String transactionName) {
        return transactionName == null || transactionName.equals(transaction.getTransactionName());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy