org.glowroot.local.ui.ErrorJsonService.java Source code

Java tutorial

Introduction

Here is the source code for org.glowroot.local.ui.ErrorJsonService.java

Source

/*
 * 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.local.ui;

import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.CharStreams;
import org.immutables.value.Value;

import org.glowroot.collector.ErrorPoint;
import org.glowroot.collector.ErrorSummary;
import org.glowroot.common.Clock;
import org.glowroot.common.ObjectMappers;
import org.glowroot.local.store.AggregateDao;
import org.glowroot.local.store.AggregateDao.ErrorSummarySortOrder;
import org.glowroot.local.store.ErrorMessageCount;
import org.glowroot.local.store.ErrorMessageQuery;
import org.glowroot.local.store.ErrorSummaryQuery;
import org.glowroot.local.store.QueryResult;
import org.glowroot.local.store.TraceDao;
import org.glowroot.local.store.TraceErrorPoint;

@JsonService
class ErrorJsonService {

    private static final ObjectMapper mapper;

    static {
        mapper = ObjectMappers.create();
        mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    }

    private final ErrorCommonService errorCommonService;
    private final TransactionCommonService transactionCommonService;
    private final TraceDao traceDao;
    private final Clock clock;

    private final long fixedAggregateIntervalMillis;
    private final long fixedAggregateRollupMillis;

    ErrorJsonService(ErrorCommonService errorCommonService, TransactionCommonService transactionCommonService,
            TraceDao traceDao, Clock clock, long fixedAggregateIntervalSeconds, long fixedAggregateRollupSeconds) {
        this.errorCommonService = errorCommonService;
        this.transactionCommonService = transactionCommonService;
        this.traceDao = traceDao;
        this.clock = clock;
        fixedAggregateIntervalMillis = fixedAggregateIntervalSeconds * 1000;
        fixedAggregateRollupMillis = fixedAggregateRollupSeconds * 1000;
    }

    @GET("/backend/error/messages")
    String getData(String queryString) throws Exception {
        ErrorMessageRequest request = QueryStrings.decode(queryString, ErrorMessageRequest.class);

        ErrorMessageQuery query = ErrorMessageQuery.builder().transactionType(request.transactionType())
                .transactionName(request.transactionName()).from(request.from()).to(request.to())
                .addAllIncludes(request.includes()).addAllExcludes(request.excludes())
                .limit(request.errorMessageLimit()).build();
        QueryResult<ErrorMessageCount> queryResult = traceDao.readErrorMessageCounts(query);
        List<ErrorPoint> unfilteredErrorPoints = errorCommonService.readErrorPoints(query.transactionType(),
                query.transactionName(), query.from(), query.to());
        DataSeries dataSeries = new DataSeries(null);
        Map<Long, Long[]> dataSeriesExtra = Maps.newHashMap();
        if (query.includes().isEmpty() && query.excludes().isEmpty()) {
            populateDataSeries(query, unfilteredErrorPoints, dataSeries, dataSeriesExtra);
        } else {
            Map<Long, Long> transactionCountMap = Maps.newHashMap();
            for (ErrorPoint unfilteredErrorPoint : unfilteredErrorPoints) {
                transactionCountMap.put(unfilteredErrorPoint.captureTime(),
                        unfilteredErrorPoint.transactionCount());
            }
            ImmutableList<TraceErrorPoint> traceErrorPoints = traceDao.readErrorPoints(query,
                    getDataPointIntervalMillis(query));
            List<ErrorPoint> errorPoints = Lists.newArrayList();
            for (TraceErrorPoint traceErrorPoint : traceErrorPoints) {
                Long transactionCount = transactionCountMap.get(traceErrorPoint.captureTime());
                if (transactionCount != null) {
                    errorPoints.add(ErrorPoint.of(traceErrorPoint.captureTime(), traceErrorPoint.errorCount(),
                            transactionCount));
                }
            }
            populateDataSeries(query, errorPoints, dataSeries, dataSeriesExtra);
        }

        StringBuilder sb = new StringBuilder();
        JsonGenerator jg = mapper.getFactory().createGenerator(CharStreams.asWriter(sb));
        jg.writeStartObject();
        jg.writeObjectField("dataSeries", dataSeries);
        jg.writeObjectField("dataSeriesExtra", dataSeriesExtra);
        jg.writeFieldName("errorMessages");
        jg.writeObject(queryResult.records());
        jg.writeBooleanField("moreErrorMessagesAvailable", queryResult.moreAvailable());
        jg.writeEndObject();
        jg.close();
        return sb.toString();
    }

    @GET("/backend/error/summaries")
    String getSummaries(String queryString) throws Exception {
        ErrorSummaryRequest request = QueryStrings.decode(queryString, ErrorSummaryRequest.class);

        ErrorSummary overallSummary = errorCommonService.readOverallErrorSummary(request.transactionType(),
                request.from() + 1, request.to());

        ErrorSummaryQuery query = ErrorSummaryQuery.builder().transactionType(request.transactionType())
                .from(request.from() + 1).to(request.to()).sortOrder(request.sortOrder()).limit(request.limit())
                .build();
        QueryResult<ErrorSummary> queryResult = errorCommonService.readTransactionErrorSummaries(query);

        StringBuilder sb = new StringBuilder();
        JsonGenerator jg = mapper.getFactory().createGenerator(CharStreams.asWriter(sb));
        jg.writeStartObject();
        jg.writeFieldName("overall");
        jg.writeObject(overallSummary);
        jg.writeFieldName("transactions");
        jg.writeObject(queryResult.records());
        jg.writeBooleanField("moreAvailable", queryResult.moreAvailable());
        jg.writeEndObject();
        jg.close();
        return sb.toString();
    }

    @GET("/backend/error/tab-bar-data")
    String getTabBarData(String queryString) throws Exception {
        TabBarDataRequest request = QueryStrings.decode(queryString, TabBarDataRequest.class);

        String transactionName = request.transactionName();
        long traceCount;
        if (transactionName == null) {
            traceCount = traceDao.readOverallErrorCount(request.transactionType(), request.from(), request.to());
        } else {
            traceCount = traceDao.readTransactionErrorCount(request.transactionType(), transactionName,
                    request.from(), request.to());
        }
        boolean tracesExpired = false;
        if (traceCount == 0) {
            tracesExpired = transactionCommonService.shouldHaveErrorTraces(request.transactionType(),
                    transactionName, request.from(), request.to());
        }

        StringBuilder sb = new StringBuilder();
        JsonGenerator jg = mapper.getFactory().createGenerator(CharStreams.asWriter(sb));
        jg.writeStartObject();
        jg.writeNumberField("traceCount", traceCount);
        jg.writeBooleanField("tracesExpired", tracesExpired);
        jg.writeEndObject();
        jg.close();
        return sb.toString();
    }

    private void populateDataSeries(ErrorMessageQuery request, List<ErrorPoint> errorPoints, DataSeries dataSeries,
            Map<Long, Long[]> dataSeriesExtra) {
        DataSeriesHelper dataSeriesHelper = new DataSeriesHelper(clock, getDataPointIntervalMillis(request));
        ErrorPoint lastErrorPoint = null;
        for (ErrorPoint errorPoint : errorPoints) {
            if (lastErrorPoint == null) {
                // first aggregate
                dataSeriesHelper.addInitialUpslopeIfNeeded(request.from(), errorPoint.captureTime(), dataSeries);
            } else {
                dataSeriesHelper.addGapIfNeeded(lastErrorPoint.captureTime(), errorPoint.captureTime(), dataSeries);
            }
            lastErrorPoint = errorPoint;
            long transactionCount = errorPoint.transactionCount();
            dataSeries.add(errorPoint.captureTime(), 100 * errorPoint.errorCount() / (double) transactionCount);
            dataSeriesExtra.put(errorPoint.captureTime(), new Long[] { errorPoint.errorCount(), transactionCount });
        }
        if (lastErrorPoint != null) {
            dataSeriesHelper.addFinalDownslopeIfNeeded(request.to(), dataSeries, lastErrorPoint.captureTime());
        }
    }

    private long getDataPointIntervalMillis(ErrorMessageQuery request) {
        if (request.to() - request.from() > AggregateDao.ROLLUP_THRESHOLD_MILLIS) {
            return fixedAggregateRollupMillis;
        } else {
            return fixedAggregateIntervalMillis;
        }
    }

    @Value.Immutable
    @JsonSerialize
    abstract static class ErrorSummaryRequestBase {
        abstract long from();

        abstract long to();

        abstract String transactionType();

        abstract ErrorSummarySortOrder sortOrder();

        abstract int limit();
    }

    @Value.Immutable
    @JsonSerialize
    abstract static class TabBarDataRequestBase {
        abstract long from();

        abstract long to();

        abstract String transactionType();

        abstract @Nullable String transactionName();
    }

    @Value.Immutable
    @JsonSerialize
    abstract static class ErrorMessageRequestBase {
        abstract long from();

        abstract long to();

        abstract String transactionType();

        abstract @Nullable String transactionName();

        public abstract ImmutableList<String> includes();

        public abstract ImmutableList<String> excludes();

        abstract int errorMessageLimit();
    }
}