/* * Copyright 2014 NAVER Corp. * * 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 com.navercorp.pinpoint.web.controller; import com.navercorp.pinpoint.common.server.bo.SpanBo; import com.navercorp.pinpoint.common.util.DateUtils; import com.navercorp.pinpoint.common.util.TransactionId; import com.navercorp.pinpoint.common.util.TransactionIdComparator; import com.navercorp.pinpoint.web.filter.Filter; import com.navercorp.pinpoint.web.filter.FilterBuilder; import com.navercorp.pinpoint.web.scatter.ScatterData; import com.navercorp.pinpoint.web.service.FilteredMapService; import com.navercorp.pinpoint.web.service.ScatterChartService; import com.navercorp.pinpoint.web.util.LimitUtils; import com.navercorp.pinpoint.web.view.ServerTime; import com.navercorp.pinpoint.web.view.TransactionMetaDataViewModel; import com.navercorp.pinpoint.web.vo.LimitedScanResult; import com.navercorp.pinpoint.web.vo.Range; import com.navercorp.pinpoint.web.vo.TransactionMetadataQuery; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StopWatch; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import java.util.Collections; import java.util.List; import java.util.Map; /** * @author netspider * @author emeroad * @author jaehong.kim */ @Controller public class ScatterChartController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private ScatterChartService scatter; @Autowired private FilteredMapService flow; @Autowired private FilterBuilder filterBuilder; private static final String PREFIX_TRANSACTION_ID = "I"; private static final String PREFIX_TIME = "T"; private static final String PREFIX_RESPONSE_TIME = "R"; @Deprecated @RequestMapping(value = "/scatterpopup", method = RequestMethod.GET) public String scatterPopup(Model model, @RequestParam("application") String applicationName, @RequestParam("from") long from, @RequestParam("to") long to, @RequestParam("period") long period, @RequestParam("usePeriod") boolean usePeriod, @RequestParam(value = "filter", required = false) String filterText) { model.addAttribute("applicationName", applicationName); model.addAttribute("from", from); model.addAttribute("to", to); model.addAttribute("period", period); model.addAttribute("usePeriod", usePeriod); model.addAttribute("filter", filterText); return "scatterPopup"; } /** * selected points from scatter chart data query * * @param requestParam * @return */ @RequestMapping(value = "/transactionmetadata", method = RequestMethod.POST) @ResponseBody public TransactionMetaDataViewModel transactionmetadata(@RequestParam Map<String, String> requestParam) { TransactionMetaDataViewModel viewModel = new TransactionMetaDataViewModel(); TransactionMetadataQuery query = parseSelectTransaction(requestParam); if (query.size() > 0) { List<SpanBo> metadata = scatter.selectTransactionMetadata(query); viewModel.setSpanBoList(metadata); } return viewModel; } private TransactionMetadataQuery parseSelectTransaction(Map<String, String> requestParam) { final TransactionMetadataQuery query = new TransactionMetadataQuery(); int index = 0; while (true) { final String transactionId = requestParam.get(PREFIX_TRANSACTION_ID + index); final String time = requestParam.get(PREFIX_TIME + index); final String responseTime = requestParam.get(PREFIX_RESPONSE_TIME + index); if (transactionId == null || time == null || responseTime == null) { break; } query.addQueryCondition(transactionId, Long.parseLong(time), Integer.parseInt(responseTime)); index++; } logger.debug("query:{}", query); return query; } /** * @param applicationName * @param from * @param to * @param limit max number of data return. if the requested data exceed this limit, we need additional calls to * fetch the rest of the data * @return */ @RequestMapping(value = "/getScatterData", method = RequestMethod.GET) public ModelAndView getScatterData( @RequestParam("application") String applicationName, @RequestParam("from") long from, @RequestParam("to") long to, @RequestParam("xGroupUnit") int xGroupUnit, @RequestParam("yGroupUnit") int yGroupUnit, @RequestParam("limit") int limit, @RequestParam(value = "backwardDirection", required = false, defaultValue = "true") boolean backwardDirection, @RequestParam(value = "filter", required = false) String filterText, @RequestParam(value = "_callback", required = false) String jsonpCallback, @RequestParam(value = "v", required = false, defaultValue = "1") int version) { if (xGroupUnit <= 0) { throw new IllegalArgumentException("xGroupUnit(" + xGroupUnit + ") must be positive number"); } if (yGroupUnit < 0) { throw new IllegalArgumentException("yGroupUnit(" + yGroupUnit + ") may not be negative number"); } limit = LimitUtils.checkRange(limit); StopWatch watch = new StopWatch(); watch.start("getScatterData"); // TODO range check verification exception occurs. "from" is bigger than "to" final Range range = Range.createUncheckedRange(from, to); logger.debug("fetch scatter data. RANGE={}, X-Group-Unit:{}, Y-Group-Unit:{}, LIMIT={}, BACKWARD_DIRECTION:{}, FILTER:{}", range, xGroupUnit, yGroupUnit, limit, backwardDirection, filterText); ModelAndView mv = null; if (StringUtils.isEmpty(filterText)) { mv = selectScatterData(applicationName, range, xGroupUnit, Math.max(yGroupUnit, 1), limit, backwardDirection, version); } else { mv = selectFilterScatterData(applicationName, range, xGroupUnit, Math.max(yGroupUnit, 1), limit, backwardDirection, filterText, version); } if (jsonpCallback == null) { mv.setViewName("jsonView"); } else { mv.setViewName("jsonpView"); } watch.stop(); logger.info("Fetch scatterData time : {}ms", watch.getLastTaskTimeMillis()); return mv; } private ModelAndView selectScatterData(String applicationName, Range range, int xGroupUnit, int yGroupUnit, int limit, boolean backwardDirection, int version) { ModelAndView mv = null; if (version == 1) { final ScatterData scatterData = scatter.selectScatterData(applicationName, range, xGroupUnit, yGroupUnit, limit, backwardDirection); boolean requestComplete = scatterData.getDotSize() < limit; mv = createScatterDataV1(scatterData, requestComplete); } else { mv = new ModelAndView(); } mv.addObject("currentServerTime", new ServerTime().getCurrentServerTime()); mv.addObject("from", range.getFrom()); mv.addObject("to", range.getTo()); return mv; } private ModelAndView selectFilterScatterData(String applicationName, Range range, int xGroupUnit, int yGroupUnit, int limit, boolean backwardDirection, String filterText, int version) { final LimitedScanResult<List<TransactionId>> limitedScanResult = flow.selectTraceIdsFromApplicationTraceIndex(applicationName, range, limit, backwardDirection); final List<TransactionId> transactionIdList = limitedScanResult.getScanData(); logger.trace("submitted transactionId count={}", transactionIdList.size()); boolean requestComplete = transactionIdList.size() < limit; Collections.sort(transactionIdList, TransactionIdComparator.INSTANCE); Filter filter = filterBuilder.build(filterText); ModelAndView mv; if (version == 1) { ScatterData scatterData = scatter.selectScatterData(transactionIdList, applicationName, range, xGroupUnit, yGroupUnit, filter); if (logger.isDebugEnabled()) { logger.debug("getScatterData range scan(limited:{}, backwardDirection:{}) from ~ to:{} ~ {}, limited:{}, filterDataSize:{}", limit, backwardDirection, DateUtils.longToDateStr(range.getFrom()), DateUtils.longToDateStr(range.getTo()), DateUtils.longToDateStr(limitedScanResult.getLimitedTime()), transactionIdList.size()); } mv = createScatterDataV1(scatterData, requestComplete); } else { mv = new ModelAndView(); } mv.addObject("currentServerTime", new ServerTime().getCurrentServerTime()); mv.addObject("from", range.getFrom()); mv.addObject("to", range.getTo()); return mv; } private ModelAndView createScatterDataV1(ScatterData scatterData, boolean complete) { ModelAndView mv = new ModelAndView(); mv.addObject("resultFrom", scatterData.getOldestAcceptedTime()); mv.addObject("resultTo", scatterData.getLatestAcceptedTime()); mv.addObject("complete", complete); mv.addObject("scatter", scatterData); return mv; } }