Skip to content

Commit

Permalink
add AsyncContextImpl interceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
jaehong-kim committed Apr 13, 2015
1 parent ffd321c commit 930de20
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 5 deletions.
Expand Up @@ -30,6 +30,7 @@ public interface TomcatConstants {
public static final ServiceType TOMCAT_METHOD = ServiceType.of(1011, "TOMCAT_METHOD", NORMAL_SCHEMA);
public static final String METADATA_TRACE = "trace";
public static final String METADATA_ASYNC = "async";
public static final String METADATA_ASYNC_TRACE_ID = "asyncTraceId";
public static final String ATTRIBUTE_PINPOINT_TRACE = "PINPOINT_TRACE";

}
Expand Up @@ -51,6 +51,8 @@ public void setup(ProfilerPluginSetupContext context) {
addStandardServiceEditor(context);
addTomcatConnectorEditor(context);
addWebappLoaderEditor(context);

addAsyncContextImpl(context);
}

private void addRequestEditor(ProfilerPluginSetupContext context) {
Expand Down Expand Up @@ -127,4 +129,19 @@ private void addWebappLoaderEditor(ProfilerPluginSetupContext context) {

context.addClassFileTransformer(builder.build());
}

private void addAsyncContextImpl(ProfilerPluginSetupContext context) {
ClassFileTransformerBuilder classBuilder = context.getClassFileTransformerBuilder("org.apache.catalina.core.AsyncContextImpl");
classBuilder.injectMetadata(METADATA_ASYNC_TRACE_ID);
MethodTransformerBuilder startEditor = classBuilder.editMethods(new MethodFilter() {
@Override
public boolean filter(MethodInfo method) {
final String name = method.getName();
return !(name.equals("dispatch"));
}
});
startEditor.property(IGNORE_IF_NOT_EXIST);
startEditor.injectInterceptor("com.navercorp.pinpoint.plugin.tomcat.interceptor.AsyncContextImplMethodInterceptor");
context.addClassFileTransformer(classBuilder.build());
}
}
@@ -0,0 +1,123 @@
/**
* 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.plugin.tomcat.interceptor;

import com.navercorp.pinpoint.bootstrap.MetadataAccessor;
import com.navercorp.pinpoint.bootstrap.context.AsyncTraceId;
import com.navercorp.pinpoint.bootstrap.context.Trace;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.interceptor.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.interceptor.SimpleAroundInterceptor;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.annotation.Cached;
import com.navercorp.pinpoint.bootstrap.plugin.annotation.Group;
import com.navercorp.pinpoint.bootstrap.plugin.annotation.Name;
import com.navercorp.pinpoint.common.AnnotationKey;
import com.navercorp.pinpoint.plugin.tomcat.TomcatConstants;

/**
*
* @author jaehong.kim
*
*/
@Group("TomcatAsyncScope")
public class AsyncContextImplMethodInterceptor implements SimpleAroundInterceptor, TomcatConstants {

private PLogger logger = PLoggerFactory.getLogger(this.getClass());
private boolean isDebug = logger.isDebugEnabled();

private TraceContext traceContext;
private MethodDescriptor descriptor;

private MetadataAccessor asyncTraceIdAccessor;

public AsyncContextImplMethodInterceptor(TraceContext context, @Cached MethodDescriptor descriptor, @Name(METADATA_ASYNC_TRACE_ID) MetadataAccessor asyncTraceIdAccessor) {
this.traceContext = context;
setMethodDescriptor(descriptor);

this.asyncTraceIdAccessor = asyncTraceIdAccessor;
}

@Override
public void before(Object target, Object[] args) {
if (isDebug) {
logger.beforeInterceptor(target, args);
}

if (!asyncTraceIdAccessor.isApplicable(target) || asyncTraceIdAccessor.get(target) == null) {
logger.debug("Not found asynchronous invocation metadata");
return;
}

final AsyncTraceId asyncTraceId = asyncTraceIdAccessor.get(target);
boolean async = false;
// TODO rawTrace ?
Trace trace = traceContext.currentTraceObject();
if (trace == null) {
trace = traceContext.continueAsyncTraceObject(asyncTraceId, asyncTraceId.getAsyncId(), asyncTraceId.getSpanStartTime());
if (trace == null) {
logger.warn("Failed to continue async trace. 'result is null'");
return;
}
async = true;
if(isDebug) {
logger.debug("Continue async trace {} [{}]", asyncTraceId, Thread.currentThread().getName());
}

}

logger.debug("TraceBlockBegin [{}]", Thread.currentThread().getName());
trace.traceBlockBegin();
trace.markBeforeTime();
trace.recordServiceType(TOMCAT_METHOD);
if(async) {
trace.recordAttribute(AnnotationKey.ASYNC, "");
}
}

@Override
public void after(Object target, Object[] args, Object result, Throwable throwable) {
if (isDebug) {
logger.afterInterceptor(target, args);
}

Trace trace = traceContext.currentTraceObject();
if (trace == null) {
logger.debug("Not found trace");
return;
}

try {
trace.recordApi(descriptor);
trace.recordException(throwable);
trace.markAfterTime();
} finally {
trace.traceBlockEnd();
if(trace.isAsync() && trace.isRootStack()) {
trace.traceRootBlockEnd();
traceContext.detachTraceObject();
if(isDebug) {
logger.debug("End async trace {} [{}]", trace.getTraceId(), Thread.currentThread().getName());
}
}
}
}

public void setMethodDescriptor(MethodDescriptor descriptor) {
this.descriptor = descriptor;
traceContext.cacheApi(descriptor);
}
}
Expand Up @@ -15,8 +15,11 @@
package com.navercorp.pinpoint.plugin.tomcat.interceptor;

import com.navercorp.pinpoint.bootstrap.MetadataAccessor;
import com.navercorp.pinpoint.bootstrap.context.AsyncTraceId;
import com.navercorp.pinpoint.bootstrap.context.Trace;
import com.navercorp.pinpoint.bootstrap.context.TraceContext;
import com.navercorp.pinpoint.bootstrap.instrument.MethodInfo;
import com.navercorp.pinpoint.bootstrap.interceptor.MethodDescriptor;
import com.navercorp.pinpoint.bootstrap.interceptor.SimpleAroundInterceptor;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
Expand All @@ -32,29 +35,92 @@
public class RequestStartAsyncInterceptor implements SimpleAroundInterceptor, TomcatConstants {

private PLogger logger = PLoggerFactory.getLogger(this.getClass());
private boolean isDebug = logger.isDebugEnabled();

private MethodInfo targetMethod;
private TraceContext traceContext;
private MethodDescriptor descriptor;
private MetadataAccessor asyncAccessor;
private MetadataAccessor asyncTraceIdAccessor;

public RequestStartAsyncInterceptor(TraceContext context, @Cached MethodInfo targetMethod, @Name(METADATA_ASYNC) MetadataAccessor asyncAccessor) {
this.targetMethod = targetMethod;
public RequestStartAsyncInterceptor(TraceContext context, @Cached MethodDescriptor descriptor, @Name(METADATA_ASYNC) MetadataAccessor asyncAccessor, @Name(METADATA_ASYNC_TRACE_ID) MetadataAccessor asyncTraceIdAccessor) {
this.traceContext = context;
setMethodDescriptor(descriptor);

this.asyncAccessor = asyncAccessor;
this.asyncTraceIdAccessor = asyncTraceIdAccessor;
}

@Override
public void before(Object target, Object[] args) {
if(isDebug) {
logger.beforeInterceptor(target, "", descriptor.getMethodName(), "", args);
}


final Trace trace = traceContext.currentTraceObject();
if (trace == null) {
return;
}
trace.traceBlockBegin();
trace.markBeforeTime();
}

@Override
public void after(Object target, Object[] args, Object result, Throwable throwable) {
logger.afterInterceptor(target, target.getClass().getName(), targetMethod.getName(), "", args);
if(isDebug) {
logger.afterInterceptor(target, "", descriptor.getMethodName(), "", args);
}

final Trace trace = traceContext.currentTraceObject();
if (trace == null) {
return;
}

try {
if (throwable == null && asyncAccessor.isApplicable(target)) {
if (validate(target, result, throwable)) {
asyncAccessor.set(target, Boolean.TRUE);

// make asynchronous trace-id
final AsyncTraceId asyncTraceId = trace.getAsyncTraceId();
trace.recordNextAsyncId(asyncTraceId.getAsyncId());
// result is BasicFuture
asyncTraceIdAccessor.set(result, asyncTraceId);
if(isDebug) {
logger.debug("Set asyncTraceId metadata {}", asyncTraceId);
}
}

trace.recordServiceType(TOMCAT_METHOD);
trace.recordApi(descriptor);
trace.recordException(throwable);
trace.markAfterTime();
} catch (Throwable t) {
logger.warn("Failed to after process. {}", t.getMessage(), t);
} finally {
trace.traceBlockEnd();
}
}

private boolean validate(final Object target, final Object result, final Throwable throwable) {
if (throwable != null || result == null) {
return false;
}

if (!asyncAccessor.isApplicable(target)) {
logger.debug("Invalid target object. Need metadata accessor({}).", METADATA_ASYNC);
return false;
}

if (!asyncTraceIdAccessor.isApplicable(result)) {
logger.debug("Invalid target object. Need metadata accessor({}).", METADATA_ASYNC_TRACE_ID);
return false;
}

return true;
}

public void setMethodDescriptor(MethodDescriptor descriptor) {
this.descriptor = descriptor;
traceContext.cacheApi(descriptor);
}
}

0 comments on commit 930de20

Please sign in to comment.