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

org.glowroot.transaction.model.TraceEntryImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2011-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.transaction.model;

import java.util.concurrent.TimeUnit;

import javax.annotation.Nullable;

import org.glowroot.shaded.google.common.base.Ticker;
import org.glowroot.shaded.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.glowroot.shaded.slf4j.Logger;
import org.glowroot.shaded.slf4j.LoggerFactory;

import org.glowroot.common.Tickers;
import org.glowroot.plugin.api.transaction.MessageSupplier;
import org.glowroot.plugin.api.transaction.QueryEntry;
import org.glowroot.plugin.api.transaction.Timer;
import org.glowroot.transaction.ErrorMessage;
import org.glowroot.transaction.ErrorMessageBase;

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

// this supports updating by a single thread and reading by multiple threads
public class TraceEntryImpl implements QueryEntry, Timer {

    private static final Logger logger = LoggerFactory.getLogger(TraceEntryImpl.class);
    private static final Ticker ticker = Tickers.getTicker();

    private final @Nullable TraceEntryImpl parentTraceEntry;
    private final @Nullable MessageSupplier messageSupplier;
    // not volatile, so depends on memory barrier in Transaction for visibility
    private @Nullable ErrorMessage errorMessage;

    private final long startTick;
    // not volatile, so depends on memory barrier in Transaction for visibility
    private long revisedStartTick;
    // not volatile, so depends on memory barrier in Transaction for visibility
    private int selfNestingLevel;
    // not volatile, so depends on memory barrier in Transaction for visibility
    private long endTick;

    private final int nestingLevel;

    // this is for maintaining linear list of trace entries
    private @Nullable TraceEntryImpl nextTraceEntry;

    // only null for limitExceededMarker and limitExtendedMarker
    private final @Nullable TimerImpl timer;
    // not volatile, so depends on memory barrier in Transaction for visibility
    private @Nullable ImmutableList stackTrace;

    // only used by transaction thread
    private long stackTraceThreshold;
    // only used by transaction thread
    private @MonotonicNonNull TimerImpl extendedTimer;

    // queryData, currRow and maxRow are only used by query entries
    private final @Nullable QueryData queryData;
    // row numbers start at 1
    private long currRow = -1;
    private long maxRow;

    TraceEntryImpl(@Nullable TraceEntryImpl parentTraceEntry,
            @Nullable MessageSupplier messageSupplier, @Nullable QueryData queryData,
            long queryExecutionCount, long startTick, int nestingLevel, @Nullable TimerImpl timer) {
        this.parentTraceEntry = parentTraceEntry;
        this.messageSupplier = messageSupplier;
        this.queryData = queryData;
        this.startTick = startTick;
        this.nestingLevel = nestingLevel;
        this.timer = timer;
        revisedStartTick = startTick;
        selfNestingLevel = 1;
        if (queryData != null) {
            queryData.start(startTick, queryExecutionCount);
        }
    }

    @Override
    public @Nullable MessageSupplier getMessageSupplier() {
        return messageSupplier;
    }

    public @Nullable ErrorMessage getErrorMessage() {
        return errorMessage;
    }

    public long getStartTick() {
        return startTick;
    }

    public long getRevisedStartTick() {
        return revisedStartTick;
    }

    public boolean isCompleted() {
        return selfNestingLevel == 0;
    }

    public long getEndTick() {
        return endTick;
    }

    public int getNestingLevel() {
        return nestingLevel;
    }

    public @Nullable ImmutableList getStackTrace() {
        return stackTrace;
    }

    public boolean isLimitExceededMarker() {
        return false;
    }

    public boolean isLimitExtendedMarker() {
        return false;
    }

    @Override
    public void end() {
        long endTick = ticker.read();
        endInternal(endTick, null);
    }

    @Override
    public void endWithStackTrace(long threshold, TimeUnit unit) {
        if (threshold < 0) {
            logger.error("endWithStackTrace(): argument 'threshold' must be non-negative");
            end();
            return;
        }
        long endTick = ticker.read();
        long thresholdNanos = unit.toNanos(threshold);
        if (endTick - startTick >= thresholdNanos) {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            // need to strip back a few stack calls:
            // skip i=0 which is "java.lang.Thread.getStackTrace()"
            // skip i=1 which is "...TraceEntry.endWithStackTrace()"
            // skip i=2 which is the plugin advice
            this.stackTrace = ImmutableList.copyOf(stackTrace).subList(3, stackTrace.length);
        } else {
            // store threshold in case this trace entry is extended, see extend() below
            stackTraceThreshold = thresholdNanos;
        }
        endInternal(endTick, null);
    }

    @Override
    public void endWithError(Throwable t) {
        endWithErrorInternal(ErrorMessageBase.from(t));
    }

    @Override
    public void endWithError(@Nullable String message) {
        endWithErrorInternal(ErrorMessageBase.from(message));
    }

    @Override
    public void endWithError(@Nullable String message, Throwable t) {
        endWithErrorInternal(ErrorMessageBase.from(message, t));
    }

    @Override
    public Timer extend() {
        // timer is only null for limitExceededMarker, limitExtendedMarker and trace entries added
        // using addEntry(), and none of these trace entries are returned from plugin api so
        // no way for extend() to be called when timer is null
        checkNotNull(timer);
        if (selfNestingLevel++ == 0) {
            long priorDuration = endTick - revisedStartTick;
            long currTick = ticker.read();
            revisedStartTick = currTick - priorDuration;
            extendedTimer = timer.extend(currTick);
            if (queryData != null) {
                queryData.extend(currTick);
            }
        }
        return this;
    }

    // this is called for stopping an extension
    @Override
    public void stop() {
        // the timer interface for this class is only expose through return value of extend()
        if (--selfNestingLevel == 0) {
            endTick = ticker.read();
            checkNotNull(extendedTimer);
            extendedTimer.end(endTick);
            if (queryData != null) {
                queryData.end(endTick);
            }
            if (stackTrace == null && stackTraceThreshold != 0
                    && endTick - revisedStartTick >= stackTraceThreshold) {
                StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
                // need to strip back a few stack calls:
                // skip i=0 which is "java.lang.Thread.getStackTrace()"
                // skip i=1 which is "...Timer.stop()"
                // skip i=2 which is the plugin advice
                this.stackTrace = ImmutableList.copyOf(stackTrace).subList(3, stackTrace.length);
            }
        }
    }

    // TODO deal with copy paste between here and DummyTraceEntryOrQuery
    @Override
    public void incrementCurrRow() {
        if (currRow == -1) {
            currRow = 1;
            maxRow = 1;
            if (queryData != null) {
                // queryData can be null here if the aggregated query limit is exceeded
                // (though typically query limit is larger than trace entry limit)
                queryData.incrementRowCount(1);
            }
        } else if (currRow == maxRow) {
            currRow++;
            maxRow = currRow;
            if (queryData != null) {
                // queryData can be null here if the query limit is exceeded
                // (though typically query limit is larger than trace entry limit)
                queryData.incrementRowCount(1);
            }
        } else {
            currRow++;
        }
    }

    // TODO deal with copy paste between here and DummyTraceEntryOrQuery
    @Override
    public void setCurrRow(long row) {
        if (row > maxRow) {
            if (queryData != null) {
                // queryData can be null here if the aggregated query limit is exceeded
                // (though typically query limit is larger than trace entry limit)
                queryData.incrementRowCount(row - maxRow);
            }
            maxRow = row;
        }
        currRow = row;
    }

    public void setStackTrace(ImmutableList stackTrace) {
        this.stackTrace = stackTrace;
    }

    // row count -1 means no navigation has been attempted
    // row count 0 means that navigation has been attempted but there were 0 rows
    public boolean isQueryNavigationAttempted() {
        return currRow != -1;
    }

    public long getRowCount() {
        return maxRow;
    }

    @Nullable
    TraceEntryImpl getParentTraceEntry() {
        return parentTraceEntry;
    }

    @Nullable
    TraceEntryImpl getNextTraceEntry() {
        return nextTraceEntry;
    }

    void setNextTraceEntry(TraceEntryImpl nextTraceEntry) {
        this.nextTraceEntry = nextTraceEntry;
    }

    void setErrorMessage(@Nullable ErrorMessage errorMessage) {
        this.errorMessage = errorMessage;
    }

    void setEndTick(long endTick) {
        this.endTick = endTick;
        this.selfNestingLevel--;
    }

    private void endWithErrorInternal(ErrorMessage errorMessage) {
        endInternal(ticker.read(), errorMessage);
        if (errorMessage.throwable() == null) {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            // need to strip back a few stack calls:
            // skip i=0 which is "java.lang.Thread.getStackTrace()"
            // skip i=1 which is "...TraceEntryImpl.endWithErrorInternal()"
            // skip i=2 which is "...TraceEntryImpl.endWithError()"
            // skip i=3 which is the plugin advice
            setStackTrace(ImmutableList.copyOf(stackTrace).subList(4, stackTrace.length));
        }
    }

    private void endInternal(long endTick, @Nullable ErrorMessage errorMessage) {
        // timer is only null for limitExceededMarker, limitExtendedMarker and trace entries added
        // using addEntry(), and none of these trace entries are returned from plugin api so
        // no way for end...() to be called
        checkNotNull(timer);
        Transaction transaction = timer.getTransaction();
        timer.end(endTick);
        if (queryData != null) {
            queryData.end(endTick);
        }
        setErrorMessage(errorMessage);
        setEndTick(endTick);
        transaction.popEntry(this, endTick);
    }

    static TraceEntryImpl getLimitExceededMarker() {
        return new LimitExceededTraceEntry();
    }

    static TraceEntryImpl getLimitExtendedMarker() {
        return new LimitExtendedTraceEntry();
    }

    private static class LimitExceededTraceEntry extends TraceEntryImpl {
        private LimitExceededTraceEntry() {
            super(null, null, null, 0, 0, 0, null);
        }
        @Override
        public boolean isLimitExceededMarker() {
            return true;
        }
    }

    private static class LimitExtendedTraceEntry extends TraceEntryImpl {
        private LimitExtendedTraceEntry() {
            super(null, null, null, 0, 0, 0, null);
        }
        @Override
        public boolean isLimitExtendedMarker() {
            return true;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy