/* * 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.mapper; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.navercorp.pinpoint.common.buffer.Buffer; import com.navercorp.pinpoint.common.buffer.OffsetFixedBuffer; import com.navercorp.pinpoint.common.server.bo.AnnotationBo; import com.navercorp.pinpoint.common.server.bo.serializer.RowKeyDecoder; import com.navercorp.pinpoint.common.server.bo.serializer.trace.v1.AnnotationBoDecoder; import com.navercorp.pinpoint.common.server.bo.SpanBo; import com.navercorp.pinpoint.common.server.bo.SpanEventBo; import com.navercorp.pinpoint.common.hbase.HBaseTables; import com.navercorp.pinpoint.common.hbase.RowMapper; import com.navercorp.pinpoint.common.server.bo.serializer.trace.v1.SpanDecoder; import com.navercorp.pinpoint.common.server.bo.serializer.trace.v1.SpanDecodingContext; import com.navercorp.pinpoint.common.util.TransactionId; import org.apache.commons.collections.CollectionUtils; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.client.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import java.util.*; import static com.navercorp.pinpoint.web.mapper.SpanMapperV2.*; /** * @author emeroad */ @Component public class SpanMapper implements RowMapper<List<SpanBo>> { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final AnnotationBoDecoder annotationBoDecoder = new AnnotationBoDecoder(); private final SpanDecoder spanDecoder = new SpanDecoder(); private final RowKeyDecoder<TransactionId> rowKeyDecoder; @Autowired public SpanMapper(@Qualifier("traceRowKeyDecoderV1") RowKeyDecoder<TransactionId> rowKeyDecoder) { if (rowKeyDecoder == null) { throw new NullPointerException("rowKeyDecoder must not be null"); } this.rowKeyDecoder = rowKeyDecoder; } @Override public List<SpanBo> mapRow(Result result, int rowNum) throws Exception { if (result.isEmpty()) { return Collections.emptyList(); } byte[] rowKey = result.getRow(); final TransactionId transactionId = this.rowKeyDecoder.decodeRowKey(rowKey); final Cell[] rawCells = result.rawCells(); Map<AgentKey, SpanBo> spanMap = new LinkedHashMap<>(); ListMultimap<AgentKey, SpanEventBo> spanEventBoListMap = ArrayListMultimap.create(); ListMultimap<Long, AnnotationBo> annotationBoListMap = ArrayListMultimap.create(); final SpanDecodingContext decodingContext = new SpanDecodingContext(); decodingContext.setTransactionId(transactionId); for (Cell cell : rawCells) { decodingContext.setCollectorAcceptedTime(cell.getTimestamp()); // only if family name is "span" if (CellUtil.matchingFamily(cell, HBaseTables.TRACES_CF_SPAN)) { Buffer qualifierBuffer = new OffsetFixedBuffer(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); Buffer valueBuffer = new OffsetFixedBuffer(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()); final SpanBo spanBo = spanDecoder.decodeSpanBo(qualifierBuffer, valueBuffer, decodingContext); AgentKey agentKey = newAgentKey(spanBo); spanMap.put(agentKey, spanBo); } else if (CellUtil.matchingFamily(cell, HBaseTables.TRACES_CF_TERMINALSPAN)) { final Buffer qualifier = new OffsetFixedBuffer(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); final Buffer valueBuffer = new OffsetFixedBuffer(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()); SpanEventBo spanEventBo = spanDecoder.decodeSpanEventBo(qualifier, valueBuffer, decodingContext); AgentKey agentKey = newAgentKey(decodingContext); spanEventBoListMap.put(agentKey, spanEventBo); } else if (CellUtil.matchingFamily(cell, HBaseTables.TRACES_CF_ANNOTATION)) { final Buffer qualifier = new OffsetFixedBuffer(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()); final Buffer valueBuffer = new OffsetFixedBuffer(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()); List<AnnotationBo> annotationBoList = annotationBoDecoder.decode(qualifier, valueBuffer, decodingContext); if (CollectionUtils.isNotEmpty(annotationBoList)) { long spanId = decodingContext.getSpanId(); annotationBoListMap.putAll(spanId, annotationBoList); } } spanDecoder.next(decodingContext); } decodingContext.finish(); for (Map.Entry<AgentKey, SpanEventBo> spanBoEntry : spanEventBoListMap.entries()) { final AgentKey agentKey = spanBoEntry.getKey(); SpanBo spanBo = spanMap.get(agentKey); if (spanBo != null) { SpanEventBo value = spanBoEntry.getValue(); spanBo.addSpanEvent(value); } else { if (logger.isInfoEnabled()) { logger.info("Span not exist spanId:{} spanEvent:{}", spanBoEntry.getKey(), spanBoEntry.getValue()); } } } List<SpanBo> spanList = Lists.newArrayList(spanMap.values()); if (!annotationBoListMap.isEmpty()) { addAnnotation(spanList, annotationBoListMap); } return spanList; } private AgentKey newAgentKey(SpanBo spanBo) { return new AgentKey(spanBo.getApplicationId(), spanBo.getAgentId(), spanBo.getAgentStartTime(), spanBo.getSpanId()); } private AgentKey newAgentKey(SpanDecodingContext decodingContext) { return new AgentKey(decodingContext.getApplicationId(), decodingContext.getAgentId(), decodingContext.getAgentStartTime(), decodingContext.getSpanId()); } private void addAnnotation(List<SpanBo> spanList, ListMultimap<Long, AnnotationBo> annotationMap) { for (SpanBo spanBo : spanList) { long spanId = spanBo.getSpanId(); List<AnnotationBo> anoList = annotationMap.get(spanId); spanBo.setAnnotationBoList(anoList); } } }