org.glowroot.agent.live.LiveTraceRepositoryImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of glowroot-agent-it-harness Show documentation
Show all versions of glowroot-agent-it-harness Show documentation
Glowroot Agent Integration Test Harness
/*
* 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);
}
}
}