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

org.glowroot.collector.EntriesChunkSourceCreator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-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.collector;

import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import org.glowroot.shaded.fasterxml.jackson.core.JsonFactory;
import org.glowroot.shaded.fasterxml.jackson.core.JsonGenerator;
import org.glowroot.shaded.google.common.collect.ImmutableList;

import org.glowroot.common.ChunkSource;
import org.glowroot.common.ChunkSource.ChunkCopier;
import org.glowroot.common.Tickers;
import org.glowroot.plugin.api.transaction.MessageSupplier;
import org.glowroot.plugin.api.transaction.internal.ReadableMessage;
import org.glowroot.transaction.ErrorMessage;
import org.glowroot.transaction.ThrowableInfo;
import org.glowroot.transaction.model.Profile;
import org.glowroot.transaction.model.StackTraceElementPlus;
import org.glowroot.transaction.model.TraceEntryImpl;

public class EntriesChunkSourceCreator {

    private static final JsonFactory jsonFactory = new JsonFactory();

    private EntriesChunkSourceCreator() {}

    public static @Nullable ChunkSource createEntriesChunkSource(Iterable entries,
            long transactionStartTick, long captureTick) {
        Iterator entriesIterator = entries.iterator();
        if (!entriesIterator.hasNext()) {
            return null;
        }
        return new EntriesChunkSource(entriesIterator, transactionStartTick, captureTick);
    }

    static void writeThrowable(ThrowableInfo throwable, JsonGenerator jg) throws IOException {
        jg.writeStartObject();
        jg.writeStringField("display", throwable.display());
        jg.writeFieldName("stackTrace");
        writeStackTrace(throwable.stackTrace(), jg);
        jg.writeNumberField("framesInCommonWithCaused", throwable.framesInCommonWithCaused());
        ThrowableInfo cause = throwable.cause();
        if (cause != null) {
            jg.writeFieldName("cause");
            writeThrowable(cause, jg);
        }
        jg.writeEndObject();
    }

    private static void writeStackTrace(List stackTrace, JsonGenerator jw)
            throws IOException {
        jw.writeStartArray();
        List elements = Profile.stripSyntheticTimerMethods(stackTrace);
        for (StackTraceElementPlus element : elements) {
            jw.writeString(element.stackTraceElement().toString());
        }
        jw.writeEndArray();
    }

    private static class EntriesChunkSource extends ChunkSource {

        private final Iterator entries;
        private final long transactionStartTick;
        private final long captureTick;

        private EntriesChunkSource(Iterator entries, long transactionStartTick,
                long captureTick) {
            this.entries = entries;
            this.transactionStartTick = transactionStartTick;
            this.captureTick = captureTick;
        }

        @Override
        public ChunkCopier getCopier(Writer writer) throws IOException {
            return new EntriesChunkCopier(entries, writer, transactionStartTick, captureTick);
        }
    }

    private static class EntriesChunkCopier implements ChunkCopier {

        private final Iterator entries;
        private final long transactionStartTick;
        private final long captureTick;
        private final JsonGenerator jg;

        private boolean closed;

        private EntriesChunkCopier(Iterator entries, Writer writer,
                long transactionStartTick, long captureTick) throws IOException {
            this.entries = entries;
            this.transactionStartTick = transactionStartTick;
            this.captureTick = captureTick;
            jg = jsonFactory.createGenerator(writer);
            jg.writeStartArray();
        }

        // timings for transactions that are still active are normalized to the capture tick in
        // order to *attempt* to present a picture of the transaction at that exact tick
        // (without using synchronization to block updates to the transaction while it is being
        // read)
        @Override
        public boolean copyNext() throws IOException {
            if (closed) {
                return false;
            }
            TraceEntryImpl traceEntry = entries.next();
            if (!Tickers.lessThanOrEqual(traceEntry.getStartTick(), captureTick)) {
                // this entry started after the capture tick
                jg.writeEndArray();
                jg.flush();
                closed = true;
                return true;
            }
            if (traceEntry.isLimitExceededMarker()) {
                writeLimitExceededEntry();
            } else if (traceEntry.isLimitExtendedMarker()) {
                writeLimitExtendedEntry();
            } else {
                writeNormalEntry(traceEntry);
            }
            if (!entries.hasNext()) {
                jg.writeEndArray();
                jg.flush();
                closed = true;
            }
            return true;
        }

        private void writeLimitExceededEntry() throws IOException {
            jg.writeStartObject();
            jg.writeBooleanField("limitExceededMarker", true);
            jg.writeEndObject();
        }

        private void writeLimitExtendedEntry() throws IOException {
            jg.writeStartObject();
            jg.writeBooleanField("limitExtendedMarker", true);
            jg.writeEndObject();
        }

        private void writeNormalEntry(TraceEntryImpl traceEntry) throws IOException {
            jg.writeStartObject();
            jg.writeNumberField("offset", traceEntry.getStartTick() - transactionStartTick);
            jg.writeFieldName("duration");
            long endTick = traceEntry.getEndTick();
            if (traceEntry.isCompleted() && Tickers.lessThanOrEqual(endTick, captureTick)) {
                // duration is calculated relative to revised start tick
                jg.writeNumber(endTick - traceEntry.getRevisedStartTick());
            } else {
                // duration is calculated relative to revised start tick
                jg.writeNumber(captureTick - traceEntry.getRevisedStartTick());
                jg.writeBooleanField("active", true);
            }
            jg.writeNumberField("nestingLevel", traceEntry.getNestingLevel());
            MessageSupplier messageSupplier = traceEntry.getMessageSupplier();
            if (messageSupplier != null) {
                jg.writeFieldName("message");
                writeMessage((ReadableMessage) messageSupplier.get(),
                        getRowCountSuffix(traceEntry));
            }
            ErrorMessage errorMessage = traceEntry.getErrorMessage();
            if (errorMessage != null) {
                jg.writeFieldName("error");
                writeErrorMessage(errorMessage);
            }
            ImmutableList stackTrace = traceEntry.getStackTrace();
            if (stackTrace != null) {
                jg.writeFieldName("stackTrace");
                writeStackTrace(stackTrace, jg);
            }
            jg.writeEndObject();
        }

        private String getRowCountSuffix(TraceEntryImpl traceEntry) {
            if (!traceEntry.isQueryNavigationAttempted()) {
                return "";
            }
            long rowCount = traceEntry.getRowCount();
            if (rowCount == 1) {
                return " => 1 row";
            } else {
                return " => " + rowCount + " rows";
            }
        }

        private void writeMessage(ReadableMessage message, String messageSuffix)
                throws IOException {
            jg.writeStartObject();
            jg.writeStringField("text", message.getText() + messageSuffix);
            Map detail = message.getDetail();
            if (!detail.isEmpty()) {
                jg.writeFieldName("detail");
                new DetailMapWriter(jg).write(detail);
            }
            jg.writeEndObject();
        }

        private void writeErrorMessage(ErrorMessage errorMessage) throws IOException {
            jg.writeStartObject();
            jg.writeStringField("message", errorMessage.message());
            ThrowableInfo throwable = errorMessage.throwable();
            if (throwable != null) {
                jg.writeFieldName("throwable");
                writeThrowable(throwable, jg);
            }
            jg.writeEndObject();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy