/*
* 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.collector.handler;
import java.util.List;
import com.navercorp.pinpoint.collector.dao.TraceDao;
import com.navercorp.pinpoint.common.server.bo.SpanBo;
import com.navercorp.pinpoint.common.server.bo.SpanEventBo;
import com.navercorp.pinpoint.common.server.bo.SpanFactory;
import com.navercorp.pinpoint.common.service.ServiceTypeRegistryService;
import com.navercorp.pinpoint.common.trace.ServiceType;
import org.apache.commons.collections.CollectionUtils;
import org.apache.thrift.TBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.navercorp.pinpoint.collector.dao.ApplicationTraceIndexDao;
import com.navercorp.pinpoint.collector.dao.HostApplicationMapDao;
import com.navercorp.pinpoint.thrift.dto.TSpan;
import org.springframework.stereotype.Service;
/**
* @author emeroad
* @author netspider
*/
@Service
public class SpanHandler implements SimpleHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private TraceDao traceDao;
@Autowired
private ApplicationTraceIndexDao applicationTraceIndexDao;
@Autowired
private StatisticsHandler statisticsHandler;
@Autowired
private HostApplicationMapDao hostApplicationMapDao;
@Autowired
private ServiceTypeRegistryService registry;
@Autowired
private SpanFactory spanFactory;
public void handleSimple(TBase<?, ?> tbase) {
if (!(tbase instanceof TSpan)) {
throw new IllegalArgumentException("unexpected tbase:" + tbase + " expected:" + this.getClass().getName());
}
try {
final TSpan tSpan = (TSpan) tbase;
if (logger.isDebugEnabled()) {
logger.debug("Received SPAN={}", tSpan);
}
final SpanBo spanBo = spanFactory.buildSpanBo(tSpan);
traceDao.insert(spanBo);
applicationTraceIndexDao.insert(tSpan);
// insert statistics info for server map
insertAcceptorHost(spanBo);
insertSpanStat(spanBo);
insertSpanEventStat(spanBo);
} catch (Exception e) {
logger.warn("Span handle error. Caused:{}. Span:{}",e.getMessage(), tbase, e);
}
}
private void insertSpanStat(SpanBo span) {
final ServiceType applicationServiceType = getApplicationServiceType(span);
final ServiceType spanServiceType = registry.findServiceType(span.getServiceType());
final boolean isError = span.getErrCode() != 0;
int bugCheck = 0;
if (span.getParentSpanId() == -1) {
if (spanServiceType.isQueue()) {
// create virtual queue node
statisticsHandler.updateCaller(span.getAcceptorHost(), spanServiceType, span.getRemoteAddr(), span.getApplicationId(), applicationServiceType, span.getEndPoint(), span.getElapsed(), isError);
statisticsHandler.updateCallee(span.getApplicationId(), applicationServiceType, span.getAcceptorHost(), spanServiceType, span.getAgentId(), span.getElapsed(), isError);
} else {
// create virtual user
statisticsHandler.updateCaller(span.getApplicationId(), ServiceType.USER, span.getAgentId(), span.getApplicationId(), applicationServiceType, span.getAgentId(), span.getElapsed(), isError);
// update the span information of the current node (self)
statisticsHandler.updateCallee(span.getApplicationId(), applicationServiceType, span.getApplicationId(), ServiceType.USER, span.getAgentId(), span.getElapsed(), isError);
}
bugCheck++;
}
// save statistics info only when parentApplicationContext exists
// when drawing server map based on statistics info, you must know the application name of the previous node.
if (span.getParentApplicationId() != null) {
String parentApplicationName = span.getParentApplicationId();
logger.debug("Received parent application name. {}", parentApplicationName);
ServiceType parentApplicationType = registry.findServiceType(span.getParentApplicationServiceType());
// create virtual queue node if current' span's service type is a queue AND :
// 1. parent node's application service type is not a queue (it may have come from a queue that is traced)
// 2. current node's application service type is not a queue (current node may be a queue that is traced)
if (spanServiceType.isQueue()) {
if (!applicationServiceType.isQueue() && !parentApplicationType.isQueue()) {
// emulate virtual queue node's accept Span and record it's acceptor host
hostApplicationMapDao.insert(span.getRemoteAddr(), span.getAcceptorHost(), spanServiceType.getCode(), parentApplicationName, parentApplicationType.getCode());
// emulate virtual queue node's send SpanEvent
statisticsHandler.updateCaller(span.getAcceptorHost(), spanServiceType, span.getRemoteAddr(), span.getApplicationId(), applicationServiceType, span.getEndPoint(), span.getElapsed(), isError);
parentApplicationName = span.getAcceptorHost();
parentApplicationType = spanServiceType;
}
}
statisticsHandler.updateCallee(span.getApplicationId(), applicationServiceType, parentApplicationName, parentApplicationType, span.getAgentId(), span.getElapsed(), isError);
bugCheck++;
}
// record the response time of the current node (self).
// blow code may be conflict of idea above callee key.
// it is odd to record reversely, because of already recording the caller data at previous node.
// the data may be different due to timeout or network error.
statisticsHandler.updateResponseTime(span.getApplicationId(), applicationServiceType, span.getAgentId(), span.getElapsed(), isError);
if (bugCheck != 1) {
logger.warn("ambiguous span found(bug). span:{}", span);
}
}
private void insertSpanEventStat(SpanBo span) {
final List<SpanEventBo> spanEventList = span.getSpanEventBoList();
if (CollectionUtils.isEmpty(spanEventList)) {
return;
}
final ServiceType applicationServiceType = getApplicationServiceType(span);
logger.debug("handle spanEvent size:{}", spanEventList.size());
// TODO need to batch update later.
for (SpanEventBo spanEvent : spanEventList) {
final ServiceType spanEventType = registry.findServiceType(spanEvent.getServiceType());
if (!spanEventType.isRecordStatistics()) {
continue;
}
// if terminal update statistics
final int elapsed = spanEvent.getEndElapsed();
final boolean hasException = spanEvent.hasException();
/*
* save information to draw a server map based on statistics
*/
// save the information of caller (the spanevent that called span)
statisticsHandler.updateCaller(span.getApplicationId(), applicationServiceType, span.getAgentId(), spanEvent.getDestinationId(), spanEventType, spanEvent.getEndPoint(), elapsed, hasException);
// save the information of callee (the span that spanevent called)
statisticsHandler.updateCallee(spanEvent.getDestinationId(), spanEventType, span.getApplicationId(), applicationServiceType, span.getEndPoint(), elapsed, hasException);
}
}
private void insertAcceptorHost(SpanBo span) {
// save host application map
// acceptor host is set at profiler module only when the span is not the kind of root span
final String acceptorHost = span.getAcceptorHost();
if (acceptorHost == null) {
return;
}
final String spanApplicationName = span.getApplicationId();
final short applicationServiceTypeCode = getApplicationServiceType(span).getCode();
final String parentApplicationName = span.getParentApplicationId();
final short parentServiceType = span.getParentApplicationServiceType();
final ServiceType spanServiceType = registry.findServiceType(span.getServiceType());
if (spanServiceType.isQueue()) {
hostApplicationMapDao.insert(span.getEndPoint(), spanApplicationName, applicationServiceTypeCode, parentApplicationName, parentServiceType);
} else {
hostApplicationMapDao.insert(acceptorHost, spanApplicationName, applicationServiceTypeCode, parentApplicationName, parentServiceType);
}
}
private ServiceType getApplicationServiceType(SpanBo span) {
// Check if applicationServiceType is set. If not, use span's service type.
final short applicationServiceTypeCode = span.getApplicationServiceType();
return registry.findServiceType(applicationServiceTypeCode);
}
}