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.28
Show newest version
/*
 * Copyright 2015 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.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;

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.Strings;
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.ImmutableMap;
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.common.live.ImmutableTracePoint;
import org.glowroot.common.live.LiveTraceRepository;
import org.glowroot.common.live.StringComparator;
import org.glowroot.common.util.Clock;
import org.glowroot.wire.api.model.ProfileTreeOuterClass.ProfileTree;
import org.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 serverId, String traceId) throws IOException {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getId().equals(traceId)) {
                return createTraceHeader(transaction);
            }
        }
        return null;
    }

    @Override
    public List getEntries(String serverId, String traceId) {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getId().equals(traceId)) {
                return transaction.getEntriesProtobuf();
            }
        }
        return ImmutableList.of();
    }

    @Override
    public @Nullable ProfileTree getProfileTree(String serverId, String traceId)
            throws IOException {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getId().equals(traceId)) {
                return transaction.getProfileTreeProtobuf();
            }
        }
        return null;
    }

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

    @Override
    public int getMatchingTraceCount(String serverId, 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(String serverId, long captureTime,
            long captureTick, TracePointQuery query) {
        List activeTracePoints = Lists.newArrayList();
        for (Transaction transaction : transactionRegistry.getTransactions()) {
            long startTick = transaction.getStartTick();
            if (matches(transaction, query) && startTick < captureTick) {
                activeTracePoints.add(ImmutableTracePoint.builder()
                        .serverId(serverId)
                        .traceId(transaction.getId())
                        .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 (query.limit() != 0 && activeTracePoints.size() > query.limit()) {
            activeTracePoints = activeTracePoints.subList(0, query.limit());
        }
        return activeTracePoints;
    }

    @Override
    public List getMatchingPendingPoints(String serverId, long captureTime,
            TracePointQuery query) {
        List points = Lists.newArrayList();
        for (Transaction transaction : transactionCollector.getPendingTransactions()) {
            if (matches(transaction, query)) {
                points.add(ImmutableTracePoint.builder()
                        .serverId(serverId)
                        .traceId(transaction.getId())
                        .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, TracePointQuery query) {
        return matchesTotal(transaction, query)
                && matchesTransactionType(transaction, query)
                && matchesSlowOnly(transaction, query)
                && matchesErrorOnly(transaction, query)
                && matchesHeadline(transaction, query)
                && matchesTransactionName(transaction, query)
                && matchesError(transaction, query)
                && matchesUser(transaction, query)
                && matchesAttribute(transaction, query);
    }

    private boolean matchesTotal(Transaction transaction, TracePointQuery query) {
        long totalNanos = transaction.getDurationNanos();
        if (totalNanos < query.durationNanosLow()) {
            return false;
        }
        Long totalNanosHigh = query.durationNanosHigh();
        return totalNanosHigh == null || totalNanos <= totalNanosHigh;
    }

    private boolean matchesTransactionType(Transaction transaction, TracePointQuery query) {
        String transactionType = query.transactionType();
        if (Strings.isNullOrEmpty(transactionType)) {
            return true;
        }
        return transactionType.equals(transaction.getTransactionType());
    }

    private boolean matchesSlowOnly(Transaction transaction, TracePointQuery query) {
        return !query.slowOnly() || transactionCollector.shouldStoreSlow(transaction);
    }

    private boolean matchesErrorOnly(Transaction transaction, TracePointQuery query) {
        return !query.errorOnly() || transactionCollector.shouldStoreError(transaction);
    }

    private boolean matchesHeadline(Transaction transaction, TracePointQuery query) {
        return matchesUsingStringComparator(query.headlineComparator(), query.headline(),
                transaction.getHeadline());
    }

    private boolean matchesTransactionName(Transaction transaction, TracePointQuery query) {
        return matchesUsingStringComparator(query.transactionNameComparator(),
                query.transactionName(), transaction.getTransactionName());
    }

    private boolean matchesError(Transaction transaction, TracePointQuery query) {
        ErrorMessage errorMessage = transaction.getErrorMessage();
        String text = errorMessage == null ? "" : errorMessage.message();
        return matchesUsingStringComparator(query.errorComparator(), query.error(), text);
    }

    private boolean matchesUser(Transaction transaction, TracePointQuery query) {
        return matchesUsingStringComparator(query.userComparator(), query.user(),
                transaction.getUser());
    }

    private boolean matchesAttribute(Transaction transaction, TracePointQuery query) {
        if (Strings.isNullOrEmpty(query.attributeName())
                && (query.attributeValueComparator() == null
                        || Strings.isNullOrEmpty(query.attributeValue()))) {
            // no custom attribute filter
            return true;
        }
        ImmutableMap> attributes = transaction.getAttributes().asMap();
        for (Entry> entry : attributes.entrySet()) {
            String attributeName = entry.getKey();
            if (!matchesUsingStringComparator(StringComparator.EQUALS, query.attributeName(),
                    attributeName)) {
                // name doesn't match, no need to test values
                continue;
            }
            for (String attributeValue : entry.getValue()) {
                if (matchesUsingStringComparator(query.attributeValueComparator(),
                        query.attributeValue(), attributeValue)) {
                    // found matching name and value
                    return true;
                }
            }
        }
        return false;
    }

    private boolean matchesUsingStringComparator(@Nullable StringComparator requestComparator,
            @Nullable String requestText, String traceText) throws AssertionError {
        if (requestComparator == null || Strings.isNullOrEmpty(requestText)) {
            return true;
        }
        return requestComparator.matches(traceText, requestText);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy