diff --git a/plugins/activemq-client/.gitignore b/plugins/activemq-client/.gitignore new file mode 100644 index 000000000000..8c2d47305b59 --- /dev/null +++ b/plugins/activemq-client/.gitignore @@ -0,0 +1,5 @@ +/target/ +/.settings/ +/.classpath +/.project +/*.iml diff --git a/plugins/activemq-client/pom.xml b/plugins/activemq-client/pom.xml new file mode 100644 index 000000000000..4c12232301f8 --- /dev/null +++ b/plugins/activemq-client/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + com.navercorp.pinpoint + pom + ../.. + 1.6.0-SNAPSHOT + + + pinpoint-activemq-client-plugin + pinpoint-activemq-client-plugin + jar + + + + com.navercorp.pinpoint + pinpoint-bootstrap-core + provided + + + org.apache.activemq + activemq-client + provided + + + + + \ No newline at end of file diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientConstants.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientConstants.java new file mode 100644 index 000000000000..346c61ec0183 --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientConstants.java @@ -0,0 +1,75 @@ +/* + * 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; + +import com.navercorp.pinpoint.common.trace.AnnotationKey; +import com.navercorp.pinpoint.common.trace.AnnotationKeyFactory; +import com.navercorp.pinpoint.common.trace.AnnotationKeyProperty; +import com.navercorp.pinpoint.common.trace.ServiceType; +import com.navercorp.pinpoint.common.trace.ServiceTypeFactory; + +import static com.navercorp.pinpoint.common.trace.ServiceTypeProperty.QUEUE; +import static com.navercorp.pinpoint.common.trace.ServiceTypeProperty.RECORD_STATISTICS; + +/** + * @author HyunGil Jeong + */ +public final class ActiveMQClientConstants { + + private ActiveMQClientConstants() { + + } + + public static final ServiceType ACTIVEMQ_CLIENT = ServiceTypeFactory.of(8310, "ACTIVEMQ_CLIENT", QUEUE, RECORD_STATISTICS); + public static final ServiceType ACTIVEMQ_CLIENT_INTERNAL = ServiceTypeFactory.of(8311, "ACTIVEMQ_CLIENT_INTERNAL", "ACTIVE_MQ_CLIENT"); + + public static final AnnotationKey ACTIVEMQ_BROKER_URL = AnnotationKeyFactory.of(101, "activemq.broker.address", AnnotationKeyProperty.VIEW_IN_RECORD_SET); + public static final AnnotationKey ACTIVEMQ_MESSAGE = AnnotationKeyFactory.of(102, "activemq.message", AnnotationKeyProperty.VIEW_IN_RECORD_SET); + + public static final String UNKNOWN_ADDRESS = "Unknown"; + + public static final String ACTIVEMQ_CLIENT_SCOPE = "ActiveMQClientScope"; + + private static final String PLUGIN_BASE = "com.navercorp.pinpoint.plugin.activemq.client"; + private static final String INTERCEPTOR_BASE = PLUGIN_BASE + ".interceptor"; + + public static final String ACTIVEMQ_TCP_TRANSPORT_FQCN = "org.apache.activemq.transport.tcp.TcpTransport"; + + public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_FIFO_FQCN = "org.apache.activemq.FifoMessageDispatchChannel"; + public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_SIMPLE_PRIORITY_FQCN = "org.apache.activemq.SimplePriorityMessageDispatchChannel"; + public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_ENQUEUE_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".MessageDispatchChannelEnqueueInterceptor"; + public static final String ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_DEQUEUE_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".MessageDispatchChannelDequeueInterceptor"; + + public static final String ACTIVEMQ_MESSAGE_PRODUCER_FQCN = "org.apache.activemq.ActiveMQMessageProducer"; + public static final String ACTIVEMQ_MESSAGE_PRODUCER_SEND_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".ActiveMQMessageProducerSendInterceptor"; + + public static final String ACTIVEMQ_MESSAGE_CONSUMER_FQCN = "org.apache.activemq.ActiveMQMessageConsumer"; + public static final String ACTIVEMQ_MESSAGE_CONSUMER_DISPATCH_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".ActiveMQMessageConsumerDispatchInterceptor"; + public static final String ACTIVEMQ_MESSAGE_CONSUMER_RECEIVE_INTERCEPTOR_FQCN = INTERCEPTOR_BASE + ".ActiveMQMessageConsumerReceiveInterceptor"; + + // field names + public static final String FIELD_ACTIVEMQ_MESSAGE_PRODUCER_SESSION = "session"; + public static final String FIELD_ACTIVEMQ_MESSAGE_CONSUMER_SESSION = "session"; + public static final String FIELD_TCP_TRANSPORT_SOCKET = "socket"; + + private static final String FIELD_BASE = PLUGIN_BASE + ".field"; + private static final String FIELD_GETTER_BASE = FIELD_BASE + ".getter"; + + // field getter FQCN + public static final String FIELD_GETTER_ACTIVEMQ_SESSION = FIELD_GETTER_BASE + ".ActiveMQSessionGetter"; + public static final String FIELD_GETTER_SOCKET = FIELD_GETTER_BASE + ".SocketGetter"; +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientHeader.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientHeader.java new file mode 100644 index 000000000000..1ff10803ba05 --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientHeader.java @@ -0,0 +1,204 @@ +/* + * 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; + +import org.apache.activemq.command.ActiveMQMessage; + +import javax.jms.JMSException; +import javax.jms.Message; + +/** + * @author HyunGil Jeong + */ +public enum ActiveMQClientHeader { + ACTIVEMQ_TRACE_ID("Pinpoint-TraceID"), + ACTIVEMQ_SPAN_ID("Pinpoint-SpanID"), + ACTIVEMQ_PARENT_SPAN_ID("Pinpoint-pSpanID"), + ACTIVEMQ_SAMPLED("Pinpoint-Sampled"), + ACTIVEMQ_FLAGS("Pinpoint-Flags"), + ACTIVEMQ_PARENT_APPLICATION_NAME("Pinpoint-pAppName"), + ACTIVEMQ_PARENT_APPLICATION_TYPE("Pinpoint-pAppType"); +// ACTIVEMQ_HOST("Pinpoint-Host"); + + private final String id; + + ActiveMQClientHeader(String id) { + this.id = id; + } + + public String getId() { + return this.id; + } + + private interface MessageHandler { + void setMessage(Message message, ActiveMQClientHeader key, T value) throws JMSException; + + T getMessage(Message message, ActiveMQClientHeader key, T defaultValue); + } + + private static abstract class MessageHandlerBase implements MessageHandler { + + @Override + public final void setMessage(Message message, ActiveMQClientHeader key, T value) throws JMSException { + String id = key.id; + if (message instanceof ActiveMQMessage) { + ActiveMQMessage activeMQMessage = (ActiveMQMessage) message; + if (activeMQMessage.isReadOnlyProperties()) { + activeMQMessage.setReadOnlyProperties(false); + setMessage0(message, id, value); + activeMQMessage.setReadOnlyProperties(true); + return; + } + } + setMessage0(message, id, value); + } + + @Override + public final T getMessage(Message message, ActiveMQClientHeader key, T defaultValue) { + String id = key.id; + try { + if (message.propertyExists(id)) { + return getMessage0(message, id); + } + } catch (JMSException e) { + // just ignore and return default value + } + return defaultValue; + } + + protected abstract void setMessage0(Message message, String id, T value) throws JMSException; + + protected abstract T getMessage0(Message message, String id) throws JMSException; + } + + private static final MessageHandler STRING_MESSAGE_HANDLER = new MessageHandlerBase() { + + @Override + protected void setMessage0(Message message, String id, String value) throws JMSException { + message.setStringProperty(id, value); + } + + @Override + protected String getMessage0(Message message, String id) throws JMSException { + return message.getStringProperty(id); + } + }; + + private static final MessageHandler LONG_MESSAGE_HANDLER = new MessageHandlerBase() { + + @Override + protected void setMessage0(Message message, String id, Long value) throws JMSException { + message.setLongProperty(id, value); + } + + @Override + protected Long getMessage0(Message message, String id) throws JMSException { + return message.getLongProperty(id); + } + }; + + private static final MessageHandler SHORT_MESSAGE_HANDLER = new MessageHandlerBase() { + + @Override + protected void setMessage0(Message message, String id, Short value) throws JMSException { + message.setShortProperty(id, value); + } + + @Override + protected Short getMessage0(Message message, String id) throws JMSException { + return message.getShortProperty(id); + } + }; + + private static final MessageHandler BOOLEAN_MESSAGE_HANDLER = new MessageHandlerBase() { + + @Override + protected void setMessage0(Message message, String id, Boolean value) throws JMSException { + message.setBooleanProperty(id, value); + } + + @Override + protected Boolean getMessage0(Message message, String id) throws JMSException { + return message.getBooleanProperty(id); + } + }; + + public static void setTraceId(Message message, String traceId) throws JMSException { + STRING_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_TRACE_ID, traceId); + } + + public static String getTraceId(Message message, String defaultValue) { + return STRING_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_TRACE_ID, defaultValue); + } + + public static void setSpanId(Message message, Long spanId) throws JMSException { + LONG_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_SPAN_ID, spanId); + } + + public static Long getSpanId(Message message, Long defaultValue) { + return LONG_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_SPAN_ID, defaultValue); + } + + public static void setParentSpanId(Message message, Long parentSpanId) throws JMSException { + LONG_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_PARENT_SPAN_ID, parentSpanId); + } + + public static Long getParentSpanId(Message message, Long defaultValue) { + return LONG_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_PARENT_SPAN_ID, defaultValue); + } + + public static void setSampled(Message message, Boolean sampled) throws JMSException { + BOOLEAN_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_SAMPLED, sampled); + } + + public static Boolean getSampled(Message message, Boolean defaultValue) { + return BOOLEAN_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_SAMPLED, defaultValue); + } + + public static void setFlags(Message message, Short flags) throws JMSException { + SHORT_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_FLAGS, flags); + } + + public static Short getFlags(Message message, Short defaultValue) { + return SHORT_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_FLAGS, defaultValue); + } + + public static void setParentApplicationName(Message message, String parentApplicationName) throws JMSException { + STRING_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_PARENT_APPLICATION_NAME, parentApplicationName); + } + + public static String getParentApplicationName(Message message, String defaultValue) { + return STRING_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_PARENT_APPLICATION_NAME, defaultValue); + } + + public static void setParentApplicationType(Message message, Short parentApplicationType) throws JMSException { + SHORT_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_PARENT_APPLICATION_TYPE, parentApplicationType); + } + + public static Short getParentApplicationType(Message message, Short defaultValue) { + return SHORT_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_PARENT_APPLICATION_TYPE, defaultValue); + } + +// public static void setHost(Message message, String host) throws JMSException { +// STRING_MESSAGE_HANDLER.setMessage(message, ACTIVEMQ_HOST, host); +// } +// +// public static String getHost(Message message, String defaultValue) { +// return STRING_MESSAGE_HANDLER.getMessage(message, ACTIVEMQ_HOST, defaultValue); +// } + +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientPlugin.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientPlugin.java new file mode 100644 index 000000000000..c8043e2a6b60 --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientPlugin.java @@ -0,0 +1,130 @@ +/* + * 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; + +import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; +import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException; +import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; +import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor; +import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback; +import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate; +import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware; +import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin; +import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext; + +import java.security.ProtectionDomain; + +/** + * @author HyunGil Jeong + */ +public class ActiveMQClientPlugin implements ProfilerPlugin, TransformTemplateAware { + + private TransformTemplate transformTemplate; + + @Override + public void setup(ProfilerPluginSetupContext context) { + this.addTransportEditor(); + this.addMessageDispatchChannelEditor(); + this.addProducerEditor(); + this.addConsumerEditor(); + } + + private void addTransportEditor() { + transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_TCP_TRANSPORT_FQCN, new TransformCallback() { + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); + + target.addGetter(ActiveMQClientConstants.FIELD_GETTER_SOCKET, ActiveMQClientConstants.FIELD_TCP_TRANSPORT_SOCKET); + + return target.toBytecode(); + } + }); + } + + private void addMessageDispatchChannelEditor() { + String[] messageDispatchChannelImplsFqcn = { + ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_FIFO_FQCN, + ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_SIMPLE_PRIORITY_FQCN + }; + for (String messageDispatchChannelImplFqcn : messageDispatchChannelImplsFqcn) { + transformTemplate.transform(messageDispatchChannelImplFqcn, new TransformCallback() { + + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); + + final InstrumentMethod enqueue = target.getDeclaredMethod("enqueue", "org.apache.activemq.command.MessageDispatch"); + if (enqueue != null) { + enqueue.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_ENQUEUE_INTERCEPTOR_FQCN); + } + final InstrumentMethod dequeue = target.getDeclaredMethod("dequeue", "long"); + if (dequeue != null) { + dequeue.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_DISPATCH_CHANNEL_DEQUEUE_INTERCEPTOR_FQCN); + } + + return target.toBytecode(); + } + }); + } + } + + private void addProducerEditor() { + transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_PRODUCER_FQCN, new TransformCallback() { + + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); + + target.addGetter(ActiveMQClientConstants.FIELD_GETTER_ACTIVEMQ_SESSION, ActiveMQClientConstants.FIELD_ACTIVEMQ_MESSAGE_PRODUCER_SESSION); + + final InstrumentMethod method = target.getDeclaredMethod("send", "javax.jms.Destination", "javax.jms.Message", "int", "int", "long", "org.apache.activemq.AsyncCallback"); + if (method != null) { + method.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_PRODUCER_SEND_INTERCEPTOR_FQCN); + } + + return target.toBytecode(); + } + }); + } + + private void addConsumerEditor() { + transformTemplate.transform(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_CONSUMER_FQCN, new TransformCallback() { + + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); + + target.addGetter(ActiveMQClientConstants.FIELD_GETTER_ACTIVEMQ_SESSION, ActiveMQClientConstants.FIELD_ACTIVEMQ_MESSAGE_CONSUMER_SESSION); + + final InstrumentMethod dispatchMethod = target.getDeclaredMethod("dispatch", "org.apache.activemq.command.MessageDispatch"); + if (dispatchMethod != null) { + dispatchMethod.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_CONSUMER_DISPATCH_INTERCEPTOR_FQCN); + } + + target.addInterceptor(ActiveMQClientConstants.ACTIVEMQ_MESSAGE_CONSUMER_RECEIVE_INTERCEPTOR_FQCN); + + return target.toBytecode(); + } + }); + } + + @Override + public void setTransformTemplate(TransformTemplate transformTemplate) { + this.transformTemplate = transformTemplate; + } +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientTraceMetadataProvider.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientTraceMetadataProvider.java new file mode 100644 index 000000000000..ad5d0469248c --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientTraceMetadataProvider.java @@ -0,0 +1,38 @@ +/* + * 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; + +import com.navercorp.pinpoint.common.trace.AnnotationKey; +import com.navercorp.pinpoint.common.trace.AnnotationKeyMatchers; +import com.navercorp.pinpoint.common.trace.TraceMetadataProvider; +import com.navercorp.pinpoint.common.trace.TraceMetadataSetupContext; + +/** + * @author HyunGil Jeong + */ +public class ActiveMQClientTraceMetadataProvider implements TraceMetadataProvider { + + @Override + public void setup(TraceMetadataSetupContext context) { + context.addServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT, AnnotationKeyMatchers.exact(AnnotationKey.MESSAGE_QUEUE_URI)); + context.addServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL, AnnotationKeyMatchers.ARGS_MATCHER); + + context.addAnnotationKey(ActiveMQClientConstants.ACTIVEMQ_BROKER_URL); + context.addAnnotationKey(ActiveMQClientConstants.ACTIVEMQ_MESSAGE); + } + +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientUtils.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientUtils.java new file mode 100644 index 000000000000..99d970dfbe0a --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/ActiveMQClientUtils.java @@ -0,0 +1,42 @@ +/* + * 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; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +/** + * @author HyunGil Jeong + */ +public class ActiveMQClientUtils { + + private ActiveMQClientUtils() { + } + + public static String getEndPoint(SocketAddress socketAddress) { + String endPoint = ActiveMQClientConstants.UNKNOWN_ADDRESS; + if (socketAddress instanceof InetSocketAddress) { + InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; + InetAddress remoteAddress = inetSocketAddress.getAddress(); + if (remoteAddress != null) { + endPoint = remoteAddress.getHostAddress() + ":" + inetSocketAddress.getPort(); + } + } + return endPoint; + } +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/descriptor/ActiveMQConsumerEntryMethodDescriptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/descriptor/ActiveMQConsumerEntryMethodDescriptor.java new file mode 100644 index 000000000000..386a86660ecf --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/descriptor/ActiveMQConsumerEntryMethodDescriptor.java @@ -0,0 +1,88 @@ +/* + * 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.descriptor; + +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.common.trace.MethodType; + +/** + * @author HyunGil Jeong + */ +public class ActiveMQConsumerEntryMethodDescriptor implements MethodDescriptor { + + private int apiId = 0; + private int type = MethodType.WEB_REQUEST; + + @Override + public String getMethodName() { + return ""; + } + + @Override + public String getClassName() { + return ""; + } + + @Override + public String[] getParameterTypes() { + return null; + } + + @Override + public String[] getParameterVariableName() { + return null; + } + + @Override + public String getParameterDescriptor() { + return "()"; + } + + @Override + public int getLineNumber() { + return -1; + } + + @Override + public String getFullName() { + return ActiveMQConsumerEntryMethodDescriptor.class.getName(); + } + + @Override + public void setApiId(int apiId) { + this.apiId = apiId; + } + + @Override + public int getApiId() { + return this.apiId; + } + + @Override + public String getApiDescriptor() { + return "ActiveMQ Consumer Invocation"; + } + + @Override + public int getType() { + return this.type; + } + + public void setType(int type) { + this.type = type; + } +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/ActiveMQSessionGetter.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/ActiveMQSessionGetter.java new file mode 100644 index 000000000000..1fa6628b8e39 --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/ActiveMQSessionGetter.java @@ -0,0 +1,26 @@ +/* + * 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.field.getter; + +import org.apache.activemq.ActiveMQSession; + +/** + * @author HyunGil Jeong + */ +public interface ActiveMQSessionGetter { + ActiveMQSession _$PINPOINT$_getActiveMQSession(); +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/SocketGetter.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/SocketGetter.java new file mode 100644 index 000000000000..029b87b7182d --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/field/getter/SocketGetter.java @@ -0,0 +1,26 @@ +/* + * 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.field.getter; + +import java.net.Socket; + +/** + * @author HyunGil Jeong + */ +public interface SocketGetter { + Socket _$PINPOINT$_getSocket(); +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerDispatchInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerDispatchInterceptor.java new file mode 100644 index 000000000000..33830955ba82 --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerDispatchInterceptor.java @@ -0,0 +1,185 @@ +/* + * 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.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanId; +import com.navercorp.pinpoint.bootstrap.context.SpanRecorder; +import com.navercorp.pinpoint.bootstrap.context.Trace; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.context.TraceId; +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 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 java.net.Socket; +import java.net.SocketAddress; + +/** + * @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(); + + public ActiveMQMessageConsumerDispatchInterceptor(TraceContext traceContext) { + this(traceContext, new ActiveMQConsumerEntryMethodDescriptor()); + } + + private ActiveMQMessageConsumerDispatchInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) { + super(traceContext, methodDescriptor, ActiveMQMessageConsumerDispatchInterceptor.class); + 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(); + // 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(); + } + } + + @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(connection.getTransport()); + Socket socket = ((SocketGetter) transport)._$PINPOINT$_getSocket(); + + SocketAddress localSocketAddress = socket.getLocalSocketAddress(); + String endPoint = ActiveMQClientUtils.getEndPoint(localSocketAddress); + // Endpoint should be the local socket address of the consumer. + recorder.recordEndPoint(endPoint); + + SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress(); + String remoteAddress = ActiveMQClientUtils.getEndPoint(remoteSocketAddress); + // 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 == null) { + return false; + } + Transport transport = getRootTransport(connection.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(); + } + return possiblyWrappedTransport; + } +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerReceiveInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerReceiveInterceptor.java new file mode 100644 index 000000000000..fc137352e606 --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageConsumerReceiveInterceptor.java @@ -0,0 +1,92 @@ +/* + * 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.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin; +import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope; +import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethod; +import com.navercorp.pinpoint.bootstrap.interceptor.annotation.TargetMethods; +import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants; +import org.apache.activemq.command.ActiveMQTextMessage; + +import javax.jms.JMSException; + +/** + * @author HyunGil Jeong + */ +@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE) +@TargetMethods({ + @TargetMethod(name = "receive"), + @TargetMethod(name = "receive", paramTypes = "long"), + @TargetMethod(name = "receiveNoWait") +}) +public class ActiveMQMessageConsumerReceiveInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { + + public ActiveMQMessageConsumerReceiveInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + super(traceContext, descriptor); + } + + // These methods may be polled, producing a lot of garbage log. + // Instead, only log when the method is actually traced. + @Override + protected void logBeforeInterceptor(Object target, Object[] args) { + return; + } + + @Override + protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) { + if (isDebug) { + super.logBeforeInterceptor(target, args); + } + } + + // These methods may be polled, producing a lot of garbage log. + // Instead, only log when the method is actually traced. + @Override + protected void logAfterInterceptor(Object target, Object[] args, Object result, Throwable throwable) { + return; + } + + @Override + protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + if (isDebug) { + super.logAfterInterceptor(target, args, result, throwable); + } + recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL); + recorder.recordApi(getMethodDescriptor()); + if (throwable != null) { + recorder.recordException(throwable); + } else { + if (result != null) { + StringBuilder sb = new StringBuilder(result.getClass().getSimpleName()); + try { + // should we record other message types as well? + if (result instanceof ActiveMQTextMessage) { + // could trigger decoding (would it affect the client? if so, we might need to copy first) + sb.append("{").append(((ActiveMQTextMessage) result).getText()).append("}"); + } + } catch (JMSException e) { + // ignore + } + recorder.recordAttribute(ActiveMQClientConstants.ACTIVEMQ_MESSAGE, sb.toString()); + } + } + } +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageProducerSendInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageProducerSendInterceptor.java new file mode 100644 index 000000000000..2861f5a61e0d --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/ActiveMQMessageProducerSendInterceptor.java @@ -0,0 +1,196 @@ +/* + * 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.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.Trace; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.context.TraceId; +import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor; +import com.navercorp.pinpoint.bootstrap.logging.PLogger; +import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; +import com.navercorp.pinpoint.common.trace.AnnotationKey; +import com.navercorp.pinpoint.exception.PinpointException; +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.field.getter.ActiveMQSessionGetter; +import com.navercorp.pinpoint.plugin.activemq.client.field.getter.SocketGetter; +import org.apache.activemq.ActiveMQConnection; +import org.apache.activemq.ActiveMQMessageProducer; +import org.apache.activemq.ActiveMQSession; +import org.apache.activemq.command.ActiveMQDestination; +import org.apache.activemq.command.ActiveMQMessage; +import org.apache.activemq.transport.Transport; +import org.apache.activemq.transport.TransportFilter; + +import javax.jms.JMSException; +import javax.jms.Message; +import java.net.Socket; +import java.net.SocketAddress; + +/** + * @author HyunGil Jeong + */ +public class ActiveMQMessageProducerSendInterceptor implements AroundInterceptor { + + private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); + private final boolean isDebug = logger.isDebugEnabled(); + + private final TraceContext traceContext; + private final MethodDescriptor descriptor; + + public ActiveMQMessageProducerSendInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + this.traceContext = traceContext; + this.descriptor = descriptor; + } + + @Override + public void before(Object target, Object[] args) { + if (isDebug) { + logger.beforeInterceptor(target, args); + } + if (!validate(target, args)) { + return; + } + + Trace trace = traceContext.currentRawTraceObject(); + + if (trace == null) { + return; + } + + Message message = (Message) args[1]; + try { + if (trace.canSampled()) { + SpanEventRecorder recorder = trace.traceBlockBegin(); + recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT); + + TraceId nextId = trace.getTraceId().getNextTraceId(); + recorder.recordNextSpanId(nextId.getSpanId()); + + ActiveMQClientHeader.setTraceId(message, nextId.getTransactionId()); + ActiveMQClientHeader.setSpanId(message, nextId.getSpanId()); + ActiveMQClientHeader.setParentSpanId(message, nextId.getParentSpanId()); + ActiveMQClientHeader.setFlags(message, nextId.getFlags()); + ActiveMQClientHeader.setParentApplicationName(message, traceContext.getApplicationName()); + ActiveMQClientHeader.setParentApplicationType(message, traceContext.getServerTypeCode()); + } else { + ActiveMQClientHeader.setSampled(message, false); + } + } catch (Throwable t) { + logger.warn("BEFORE. Cause:{}", t.getMessage(), t); + } + } + + @Override + public void after(Object target, Object[] args, Object result, Throwable throwable) { + if (isDebug) { + logger.afterInterceptor(target, args); + } + if (!validate(target, args)) { + return; + } + Trace trace = traceContext.currentTraceObject(); + if (trace == null) { + return; + } + + try { + SpanEventRecorder recorder = trace.currentSpanEventRecorder(); + recorder.recordApi(descriptor); + + if (throwable == null) { + ActiveMQSession session = ((ActiveMQSessionGetter) target)._$PINPOINT$_getActiveMQSession(); + ActiveMQConnection connection = session.getConnection(); + Transport transport = getRootTransport(connection.getTransport()); + Socket socket = ((SocketGetter) transport)._$PINPOINT$_getSocket(); + SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress(); + String remoteAddress = ActiveMQClientUtils.getEndPoint(remoteSocketAddress); + // Producer's endPoint should be the socket address of where the producer is actually connected to. + recorder.recordEndPoint(remoteAddress); + recorder.recordAttribute(ActiveMQClientConstants.ACTIVEMQ_BROKER_URL, remoteAddress); + + ActiveMQDestination destination = (ActiveMQDestination) args[0]; + // This annotation indicates the uri to which the call is made + recorder.recordAttribute(AnnotationKey.MESSAGE_QUEUE_URI, destination.getQualifiedName()); + // DestinationId is used to render the virtual queue node. + // We choose the queue/topic name as the logical name of the queue node. + recorder.recordDestinationId(destination.getPhysicalName()); + } else { + recorder.recordException(throwable); + } + } catch (Throwable t) { + logger.warn("AFTER error. Cause:{}", t.getMessage(), t); + } finally { + trace.traceBlockEnd(); + } + } + + private boolean validate(Object target, Object[] args) { + if (!(target instanceof ActiveMQMessageProducer)) { + 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 < 2) { + return false; + } + if (!(args[0] instanceof ActiveMQDestination)) { + return false; + } + if (!(args[1] instanceof Message)) { + return false; + } + return true; + } + + private boolean validateTransport(ActiveMQSession session) { + if (session == null) { + return false; + } + ActiveMQConnection connection = session.getConnection(); + if (connection == null) { + return false; + } + Transport transport = getRootTransport(connection.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(); + } + return possiblyWrappedTransport; + } + +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelDequeueInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelDequeueInterceptor.java new file mode 100644 index 000000000000..ea8e501ad260 --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelDequeueInterceptor.java @@ -0,0 +1,71 @@ +/* + * 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.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin; +import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Name; +import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope; +import com.navercorp.pinpoint.bootstrap.interceptor.scope.ExecutionPolicy; +import com.navercorp.pinpoint.bootstrap.interceptor.scope.InterceptorScope; +import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants; + +/** + * @author HyunGil Jeong + */ +@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE, executionPolicy = ExecutionPolicy.INTERNAL) +public class MessageDispatchChannelDequeueInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { + + public MessageDispatchChannelDequeueInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + super(traceContext, descriptor); + } + + // These methods may be polled, producing a lot of garbage log. + // Instead, only log when the method is actually traced. + @Override + protected void logBeforeInterceptor(Object target, Object[] args) { + return; + } + + @Override + protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) { + if (isDebug) { + super.logBeforeInterceptor(target, args); + } + } + + // These methods may be polled, producing a lot of garbage log. + // Instead, only log when the method is actually traced. + @Override + protected void logAfterInterceptor(Object target, Object[] args, Object result, Throwable throwable) { + return; + } + + @Override + protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + if (isDebug) { + super.logAfterInterceptor(target, args, result, throwable); + } + recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL); + recorder.recordApi(getMethodDescriptor()); + if (throwable != null) { + recorder.recordException(throwable); + } + } +} diff --git a/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelEnqueueInterceptor.java b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelEnqueueInterceptor.java new file mode 100644 index 000000000000..524bb5232dba --- /dev/null +++ b/plugins/activemq-client/src/main/java/com/navercorp/pinpoint/plugin/activemq/client/interceptor/MessageDispatchChannelEnqueueInterceptor.java @@ -0,0 +1,50 @@ +/* + * 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.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin; +import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Scope; +import com.navercorp.pinpoint.bootstrap.interceptor.scope.ExecutionPolicy; +import com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientConstants; + +/** + * @author HyunGil Jeong + */ +@Scope(value = ActiveMQClientConstants.ACTIVEMQ_CLIENT_SCOPE, executionPolicy = ExecutionPolicy.INTERNAL) +public class MessageDispatchChannelEnqueueInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { + + public MessageDispatchChannelEnqueueInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + super(traceContext, descriptor); + } + + @Override + protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) { + + } + + @Override + protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + recorder.recordServiceType(ActiveMQClientConstants.ACTIVEMQ_CLIENT_INTERNAL); + recorder.recordApi(getMethodDescriptor()); + if (throwable != null) { + recorder.recordException(throwable); + } + } +} diff --git a/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin new file mode 100644 index 000000000000..c6b85ac938dc --- /dev/null +++ b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin @@ -0,0 +1 @@ +com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientPlugin \ No newline at end of file diff --git a/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider new file mode 100644 index 000000000000..1556809a36d9 --- /dev/null +++ b/plugins/activemq-client/src/main/resources/META-INF/services/com.navercorp.pinpoint.common.trace.TraceMetadataProvider @@ -0,0 +1 @@ +com.navercorp.pinpoint.plugin.activemq.client.ActiveMQClientTraceMetadataProvider \ No newline at end of file diff --git a/plugins/pom.xml b/plugins/pom.xml index 0afc7050b065..0ea5583f2214 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -42,6 +42,7 @@ logback dubbo cassandra + activemq-client @@ -195,6 +196,11 @@ pinpoint-dubbo-plugin ${project.version} + + com.navercorp.pinpoint + pinpoint-activemq-client-plugin + ${project.version} + diff --git a/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js b/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js index a6ed89ed4004..81fb8c85a80b 100644 --- a/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js +++ b/web/src/main/webapp/components/server-map2/jquery.ServerMap2.js @@ -31,6 +31,7 @@ "sImageDir": './images/', "sBoldKey": null, "htIcons": { + 'ACTIVEMQ_CLIENT': 'ACTIVEMQ_CLIENT.png', 'APACHE': 'APACHE.png', 'APACHE_GROUP': 'APACHE.png', 'ARCUS': 'ARCUS.png', diff --git a/web/src/main/webapp/images/icons/ACTIVEMQ_CLIENT.png b/web/src/main/webapp/images/icons/ACTIVEMQ_CLIENT.png new file mode 100644 index 000000000000..a2caf8a35620 Binary files /dev/null and b/web/src/main/webapp/images/icons/ACTIVEMQ_CLIENT.png differ diff --git a/web/src/main/webapp/images/servermap/ACTIVEMQ_CLIENT.png b/web/src/main/webapp/images/servermap/ACTIVEMQ_CLIENT.png new file mode 100644 index 000000000000..9287431fc23f Binary files /dev/null and b/web/src/main/webapp/images/servermap/ACTIVEMQ_CLIENT.png differ