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.14.0-beta.3
Show newest version
/*
 * Copyright 2015-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.live;

import java.util.Collections;
import java.util.List;

import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.annotations.VisibleForTesting;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Function;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Ticker;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.ImmutableList;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Iterables;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Ordering;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.Nullable;

import org.glowroot.agent.collector.Collector.TraceReader;
import org.glowroot.agent.collector.Collector.TraceVisitor;
import org.glowroot.agent.impl.TraceCreator;
import org.glowroot.agent.impl.Transaction;
import org.glowroot.agent.impl.Transaction.TraceEntryVisitor;
import org.glowroot.agent.impl.TransactionCollector;
import org.glowroot.agent.impl.TransactionRegistry;
import org.glowroot.agent.model.ErrorMessage;
import org.glowroot.agent.shaded.org.glowroot.common.live.ImmutableEntries;
import org.glowroot.agent.shaded.org.glowroot.common.live.ImmutableQueries;
import org.glowroot.agent.shaded.org.glowroot.common.live.ImmutableTracePoint;
import org.glowroot.agent.shaded.org.glowroot.common.live.LiveTraceRepository;
import org.glowroot.agent.shaded.org.glowroot.common.util.Clock;
import org.glowroot.agent.shaded.org.glowroot.wire.api.model.AggregateOuterClass.Aggregate;
import org.glowroot.agent.shaded.org.glowroot.wire.api.model.ProfileOuterClass.Profile;
import org.glowroot.agent.shaded.org.glowroot.wire.api.model.TraceOuterClass.Trace;

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

public class LiveTraceRepositoryImpl implements LiveTraceRepository {

    private static final String AGENT_ID = "";

    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 Trace. /*@Nullable*/ Header getHeader(String agentId, String traceId) {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                return createTraceHeader(transaction);
            }
        }
        return null;
    }

    @Override
    public @Nullable Entries getEntries(String agentId, String traceId) {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                CollectingEntryVisitor visitor = new CollectingEntryVisitor();
                transaction.visitEntries(ticker.read(), visitor);
                return ImmutableEntries.builder()
                        .addAllEntries(visitor.entries)
                        .addAllSharedQueryTexts(
                                TraceCreator.toProto(transaction.getSharedQueryTexts()))
                        .build();
            }
        }
        return null;
    }

    @Override
    public @Nullable Queries getQueries(String agentId, String traceId) {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                return ImmutableQueries.builder()
                        .addAllQueries(transaction.getQueries())
                        .addAllSharedQueryTexts(
                                TraceCreator.toProto(transaction.getSharedQueryTexts()))
                        .build();
            }
        }
        return null;
    }

    @Override
    public @Nullable Profile getMainThreadProfile(String agentId, String traceId) {
        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) {
        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 Exception {
        for (Transaction transaction : Iterables.concat(transactionRegistry.getTransactions(),
                transactionCollector.getPendingTransactions())) {
            if (transaction.getTraceId().equals(traceId)) {
                CollectingTraceVisitor traceVisitor = new CollectingTraceVisitor();
                TraceReader traceReader = createTraceReader(transaction);
                traceReader.accept(traceVisitor);
                Trace.Builder builder = Trace.newBuilder()
                        .setId(traceId)
                        .setUpdate(transaction.isPartiallyStored());
                Profile mainThreadProfile = traceVisitor.mainThreadProfile;
                if (mainThreadProfile != null) {
                    builder.setMainThreadProfile(mainThreadProfile);
                }
                Profile auxThreadProfile = traceVisitor.auxThreadProfile;
                if (auxThreadProfile != null) {
                    builder.setAuxThreadProfile(auxThreadProfile);
                }
                return builder.setHeader(checkNotNull(traceVisitor.header))
                        .addAllEntry(traceVisitor.entries)
                        .addAllQuery(traceVisitor.queries)
                        .addAllSharedQueryText(TraceCreator.toProto(traceVisitor.sharedQueryTexts))
                        .build();
            }
        }
        return null;
    }

    @Override
    public int getMatchingTraceCount(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 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(AGENT_ID)
                        .traceId(transaction.getTraceId())
                        .captureTime(captureTime)
                        .durationNanos(captureTick - startTick)
                        .partial(true)
                        .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 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(AGENT_ID)
                        .traceId(transaction.getTraceId())
                        .captureTime(captureTime)
                        .durationNanos(transaction.getDurationNanos())
                        .partial(false)
                        .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;
        }
        return transactionName == null || transactionName.equals(transaction.getTransactionName());
    }

    private Trace.Header createTraceHeader(Transaction transaction) {
        // 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.isFullyCompleted()) {
            return TraceCreator.createCompletedTraceHeader(transaction);
        } else {
            return TraceCreator.createPartialTraceHeader(transaction, captureTime, captureTick);
        }
    }

    private TraceReader createTraceReader(Transaction transaction) {
        if (transaction.isFullyCompleted()) {
            return TraceCreator.createTraceReaderForCompleted(transaction, true);
        } else {
            return TraceCreator.createTraceReaderForPartial(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 static boolean matchesTransactionType(Transaction transaction, String transactionType) {
        return transactionType.equals(transaction.getTransactionType());
    }

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

    private static class CollectingTraceVisitor implements TraceVisitor {

        private final List entries = Lists.newArrayList();
        private List queries = ImmutableList.of();
        private List sharedQueryTexts = ImmutableList.of();
        private @Nullable Profile mainThreadProfile;
        private @Nullable Profile auxThreadProfile;
        private Trace. /*@Nullable*/ Header header;

        @Override
        public void visitEntry(Trace.Entry entry) {
            entries.add(entry);
        }

        @Override
        public void visitQueries(List queries) {
            this.queries = queries;
        }

        @Override
        public void visitSharedQueryTexts(List sharedQueryTexts) {
            this.sharedQueryTexts = sharedQueryTexts;
        }

        @Override
        public void visitMainThreadProfile(Profile profile) {
            mainThreadProfile = profile;
        }

        @Override
        public void visitAuxThreadProfile(Profile profile) {
            auxThreadProfile = profile;
        }

        @Override
        public void visitHeader(Trace.Header header) {
            this.header = header;
        }
    }

    private static class CollectingEntryVisitor implements TraceEntryVisitor {

        private final List entries = Lists.newArrayList();

        @Override
        public void visitEntry(Trace.Entry entry) {
            entries.add(entry);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy