/* * Copyright 2016 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.plugin.activemq.client.interceptor; import com.navercorp.pinpoint.bootstrap.config.Filter; import com.navercorp.pinpoint.bootstrap.context.*; import com.navercorp.pinpoint.bootstrap.interceptor.SpanSimpleAroundInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; import com.navercorp.pinpoint.common.trace.ServiceType; import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants; import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientHeader; import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientUtils; import com.navercorp.pinpoint.plugin.activemq.client.descriptor.ActiveMQConsumerEntryMethodDescriptor; import com.navercorp.pinpoint.plugin.activemq.client.field.getter.ActiveMQSessionGetter; import com.navercorp.pinpoint.plugin.activemq.client.field.getter.SocketGetter; import com.navercorp.pinpoint.plugin.activemq.client.field.getter.TransportGetter; import com.navercorp.pinpoint.plugin.activemq.client.field.getter.URIGetter; import org.apache.activemq.ActiveMQConnection; import org.apache.activemq.ActiveMQMessageConsumer; import org.apache.activemq.ActiveMQSession; import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQMessage; import org.apache.activemq.command.Message; import org.apache.activemq.command.MessageDispatch; import org.apache.activemq.transport.Transport; import org.apache.activemq.transport.TransportFilter; import org.apache.activemq.transport.failover.FailoverTransport; import java.net.Socket; import java.net.SocketAddress; import java.net.URI; /** * @author HyunGil Jeong */ @Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE) public class ActiveMQMessageConsumerDispatchInterceptor extends SpanSimpleAroundInterceptor { private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); private final boolean isDebug = logger.isDebugEnabled(); private final Filter<String> excludeDestinationFilter; public ActiveMQMessageConsumerDispatchInterceptor(TraceContext traceContext, Filter<String> excludeDestinationFilter) { this(traceContext, new ActiveMQConsumerEntryMethodDescriptor(), excludeDestinationFilter); } private ActiveMQMessageConsumerDispatchInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor, Filter<String> excludeDestinationFilter) { super(traceContext, methodDescriptor, ActiveMQMessageConsumerDispatchInterceptor.class); this.excludeDestinationFilter = excludeDestinationFilter; traceContext.cacheApi(methodDescriptor); } @Override protected Trace createTrace(Object target, Object[] args) { if (!validate(target, args)) { return null; } MessageDispatch md = (MessageDispatch) args[0]; ActiveMQMessage message = (ActiveMQMessage) md.getMessage(); if (filterDestination(message.getDestination())) { return null; } // These might trigger unmarshalling. if (!ActiveMQClientHeader.getSampled(message, true)) { return traceContext.disableSampling(); } String transactionId = ActiveMQClientHeader.getTraceId(message, null); if (transactionId != null) { long parentSpanId = ActiveMQClientHeader.getParentSpanId(message, SpanId.NULL); long spanId = ActiveMQClientHeader.getSpanId(message, SpanId.NULL); short flags = ActiveMQClientHeader.getFlags(message, (short) 0); final TraceId traceId = traceContext.createTraceId(transactionId, parentSpanId, spanId, flags); return traceContext.continueTraceObject(traceId); } else { return traceContext.newTraceObject(); } } private boolean filterDestination(ActiveMQDestination destination) { String destinationName = destination.getPhysicalName(); return this.excludeDestinationFilter.filter(destinationName); } @Override protected void doInBeforeTrace(SpanRecorder recorder, Object target, Object[] args) { recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT); ActiveMQSession session = ((ActiveMQSessionGetter) target)._$PINPOINT$_getActiveMQSession(); ActiveMQConnection connection = session.getConnection(); Transport transport = getRootTransport(((TransportGetter) connection)._$PINPOINT$_getTransport()); String endPoint = null; String remoteAddress = transport.getRemoteAddress(); if (transport instanceof SocketGetter) { Socket socket = ((SocketGetter) transport)._$PINPOINT$_getSocket(); SocketAddress localSocketAddress = socket.getLocalSocketAddress(); endPoint = ActiveMQClientUtils.getEndPoint(localSocketAddress); } else if (transport instanceof URIGetter) { URI uri = ((URIGetter) transport)._$PINPOINT$_getUri(); endPoint = uri.getHost() + ":" + uri.getPort(); } // Endpoint should be the local socket address of the consumer. recorder.recordEndPoint(endPoint); // Remote address is the socket address of where the consumer is connected to. recorder.recordRemoteAddress(remoteAddress); MessageDispatch md = (MessageDispatch) args[0]; ActiveMQMessage message = (ActiveMQMessage) md.getMessage(); ActiveMQDestination destination = message.getDestination(); // Rpc name is the URI of the queue/topic we're consuming from. recorder.recordRpcName(destination.getQualifiedName()); // Record acceptor host as the queue/topic name in order to generate virtual queue node. recorder.recordAcceptorHost(destination.getPhysicalName()); String parentApplicationName = ActiveMQClientHeader.getParentApplicationName(message, null); if (!recorder.isRoot() && parentApplicationName != null) { short parentApplicationType = ActiveMQClientHeader.getParentApplicationType(message, ServiceType.UNDEFINED.getCode()); recorder.recordParentApplication(parentApplicationName, parentApplicationType); } } @Override protected void doInAfterTrace(SpanRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { recorder.recordApi(methodDescriptor); if (throwable != null) { recorder.recordException(throwable); } } private boolean validate(Object target, Object[] args) { if (!(target instanceof ActiveMQMessageConsumer)) { return false; } if (!(target instanceof ActiveMQSessionGetter)) { if (isDebug) { logger.debug("Invalid target object. Need field accessor({}).", ActiveMQSessionGetter.class.getName()); } return false; } if (!validateTransport(((ActiveMQSessionGetter) target)._$PINPOINT$_getActiveMQSession())) { return false; } if (args == null || args.length < 1) { return false; } if (!(args[0] instanceof MessageDispatch)) { return false; } MessageDispatch md = (MessageDispatch) args[0]; Message message = md.getMessage(); if (!(message instanceof ActiveMQMessage)) { return false; } return true; } private boolean validateTransport(ActiveMQSession session) { if (session == null) { return false; } ActiveMQConnection connection = session.getConnection(); if (!(connection instanceof TransportGetter)) { if (isDebug) { logger.debug("Invalid connection object. Need field accessor({}).", TransportGetter.class.getName()); } return false; } Transport transport = getRootTransport(((TransportGetter) connection)._$PINPOINT$_getTransport()); if (!(transport instanceof SocketGetter)) { if (isDebug) { logger.debug("Transport not traceable({}).", transport.getClass().getName()); } return false; } return true; } private Transport getRootTransport(Transport transport) { Transport possiblyWrappedTransport = transport; while (possiblyWrappedTransport instanceof TransportFilter) { possiblyWrappedTransport = ((TransportFilter) possiblyWrappedTransport).getNext(); if (possiblyWrappedTransport instanceof FailoverTransport) { possiblyWrappedTransport = ((FailoverTransport) possiblyWrappedTransport).getConnectedTransport(); } } return possiblyWrappedTransport; } }