/** @author harebox */
@TargetConstructor("net.sf.ehcache.Element")
public class FrontCacheGetFutureConstructInterceptor implements AroundInterceptor, ArcusConstants {

  // TODO This should be extracted from FrontCacheMemcachedClient.
  private static final String DEFAULT_FRONTCACHE_NAME = "front";

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

  @Override
  public void before(Object target, Object[] args) {
    // do nothing
  }

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

    try {
      ((CacheNameAccessor) target)._$PINPOINT$_setCacheName(DEFAULT_FRONTCACHE_NAME);

      if (args[0] instanceof Element) {
        Element element = (Element) args[0];
        ((CacheKeyAccessor) target)._$PINPOINT$_setCacheKey(element.getObjectKey());
      }
    } catch (Exception e) {
      logger.error("failed to add metadata: {}", e);
    }
  }
}
Esempio n. 2
0
/**
 * @author netspider
 * @author emeroad
 */
@TargetMethod(
    name = "addOp",
    paramTypes = {"java.lang.String", "net.spy.memcached.ops.Operation"})
public class AddOpInterceptor implements AroundInterceptor {

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

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

    String serviceCode = ((ServiceCodeAccessor) target)._$PINPOINT$_getServiceCode();
    ((ServiceCodeAccessor) args[1])._$PINPOINT$_setServiceCode(serviceCode);
  }

  @Override
  public void after(Object target, Object[] args, Object result, Throwable throwable) {
    if (isDebug) {
      logger.afterInterceptor(target, args, result, throwable);
    }
  }
}
Esempio n. 3
0
  public void stop(boolean staticResourceCleanup) {
    synchronized (this) {
      if (this.agentStatus == AgentStatus.RUNNING) {
        changeStatus(AgentStatus.STOPPED);
      } else {
        logger.warn("Cannot stop agent. Current status = [{}]", this.agentStatus);
        return;
      }
    }
    logger.info("Stopping {} Agent.", ProductInfo.NAME);

    this.agentInfoSender.stop();
    this.agentStatMonitor.stop();

    // Need to process stop
    this.spanDataSender.stop();
    this.statDataSender.stop();

    closeTcpDataSender();
    // for testcase
    if (staticResourceCleanup) {
      PLoggerFactory.unregister(this.binder);
      this.interceptorRegistryBinder.unbind();
    }
  }
Esempio n. 4
0
  private TestClassLoader getTestClassLoader() {
    PLoggerFactory.initialize(new Slf4jLoggerBinder());

    ProfilerConfig profilerConfig = new ProfilerConfig();
    profilerConfig.setApplicationServerType(ServiceType.TEST_STAND_ALONE.getName());
    DefaultAgent agent = MockAgent.of(profilerConfig);

    return new TestClassLoader(agent);
  }
Esempio n. 5
0
/** @author emeroad */
public class GroupedInterceptor implements AroundInterceptor {
  private final PLogger logger = PLoggerFactory.getLogger(getClass());
  private final boolean debugEnabled = logger.isDebugEnabled();

  private final BeforeInterceptor before;
  private final AfterInterceptor after;
  private final InterceptorGroup group;
  private final ExecutionPolicy policy;

  public GroupedInterceptor(
      BeforeInterceptor before,
      AfterInterceptor after,
      InterceptorGroup group,
      ExecutionPolicy policy) {
    this.before = before;
    this.after = after;
    this.group = group;
    this.policy = policy;
  }

  @Override
  public void before(Object target, Object[] args) {
    InterceptorGroupInvocation transaction = group.getCurrentInvocation();

    if (transaction.tryEnter(policy)) {
      if (before != null) {
        before.before(target, args);
      }
    } else {
      if (debugEnabled) {
        logger.debug(
            "tryBefore() returns false: interceptorGroupTransaction: {}, executionPoint: {}. Skip interceptor {}",
            new Object[] {transaction, policy, before == null ? null : before.getClass()});
      }
    }
  }

  @Override
  public void after(Object target, Object[] args, Object result, Throwable throwable) {
    InterceptorGroupInvocation transaction = group.getCurrentInvocation();

    if (transaction.canLeave(policy)) {
      if (after != null) {
        after.after(target, args, result, throwable);
      }
      transaction.leave(policy);
    } else {
      if (debugEnabled) {
        logger.debug(
            "tryAfter() returns false: interceptorGroupTransaction: {}, executionPoint: {}. Skip interceptor {}",
            new Object[] {transaction, policy, after == null ? null : after.getClass()});
      }
    }
  }
}
/** @author Jongho Moon */
public class BeanMethodTransformer implements PinpointClassFileTransformer {
  private static final int REQUIRED_ACCESS_FLAG = Modifier.PUBLIC;
  private static final int REJECTED_ACCESS_FLAG =
      Modifier.ABSTRACT | Modifier.NATIVE | Modifier.STATIC;
  private static final MethodFilter METHOD_FILTER =
      MethodFilters.modifier(REQUIRED_ACCESS_FLAG, REJECTED_ACCESS_FLAG);

  private final PLogger logger = PLoggerFactory.getLogger(getClass());

  /* (non-Javadoc)
   * @see com.navercorp.pinpoint.bootstrap.plugin.transformer.PinpointClassFileTransformer#transform(com.navercorp.pinpoint.bootstrap.plugin.PinpointInstrument, java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[])
   */
  @Override
  public byte[] transform(
      Instrumentor instrumentContext,
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws InstrumentException {
    if (logger.isInfoEnabled()) {
      logger.info("Modify {}", className);
    }

    try {
      InstrumentClass target =
          instrumentContext.getInstrumentClass(loader, className, classfileBuffer);

      if (!target.isInterceptable()) {
        return null;
      }

      List<InstrumentMethod> methodList = target.getDeclaredMethods(METHOD_FILTER);
      for (InstrumentMethod method : methodList) {
        if (logger.isTraceEnabled()) {
          logger.trace(
              "### c={}, m={}, params={}",
              new Object[] {
                className, method.getName(), Arrays.toString(method.getParameterTypes())
              });
        }

        method.addInterceptor(
            BasicMethodInterceptor.class.getName(), va(SpringBeansConstants.SERVICE_TYPE));
      }

      return target.toBytecode();
    } catch (Exception e) {
      logger.warn("modify fail. Cause:{}", e.getMessage(), e);
      return null;
    }
  }
}
Esempio n. 7
0
 private void bindPLoggerFactory(PLoggerBinder binder) {
   final String binderClassName = binder.getClass().getName();
   PLogger pLogger = binder.getLogger(binder.getClass().getName());
   pLogger.info(
       "PLoggerFactory.initialize() bind:{} cl:{}",
       binderClassName,
       binder.getClass().getClassLoader());
   // Set binder to static LoggerFactory
   // Should we unset binder at shutdown hook or stop()?
   PLoggerFactory.initialize(binder);
 }
/**
 * This interceptor retrieves the method name called by the client and stores it for other
 * interceptors in the chain to use.
 *
 * <p><tt>TBaseAsyncProcessorProcessInterceptor</tt> ->
 * <b><tt>TProtocolReadMessageBeginInterceptor</tt></b> ->
 * <tt>TProtocolReadFieldBeginInterceptor</tt> <-> <tt>TProtocolReadTTypeInterceptor</tt> ->
 * <tt>TProtocolReadMessageEndInterceptor</tt>
 *
 * <p>Based on Thrift 0.8.0+
 *
 * @author HyunGil Jeong
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageBeginInterceptor
 *     TProtocolReadMessageBeginInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadFieldBeginInterceptor
 *     TProtocolReadFieldBeginInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadTTypeInterceptor
 *     TProtocolReadTTypeInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageEndInterceptor
 *     TProtocolReadMessageEndInterceptor
 */
@Scope(value = THRIFT_SERVER_SCOPE, executionPolicy = ExecutionPolicy.INTERNAL)
public class TProtocolReadMessageBeginInterceptor implements AroundInterceptor {

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

  private final InterceptorScope scope;

  public TProtocolReadMessageBeginInterceptor(@Name(THRIFT_SERVER_SCOPE) InterceptorScope scope) {
    this.scope = scope;
  }

  @Override
  public void before(Object target, Object[] args) {
    // Do nothing
  }

  @Override
  public void after(Object target, Object[] args, Object result, Throwable throwable) {
    if (isDebug) {
      logger.afterInterceptor(target, args, result, throwable);
    }
    if (!validate(target)) {
      return;
    }
    final boolean shouldTrace =
        ((AsyncMarkerFlagFieldAccessor) target)._$PINPOINT$_getAsyncMarkerFlag();
    if (shouldTrace) {
      String methodName = ThriftConstants.UNKNOWN_METHOD_NAME;
      if (result instanceof TMessage) {
        TMessage message = (TMessage) result;
        methodName = message.name;
      }
      ThriftClientCallContext clientCallContext = new ThriftClientCallContext(methodName);
      InterceptorScopeInvocation currentTransaction = this.scope.getCurrentInvocation();
      currentTransaction.setAttachment(clientCallContext);
    }
  }

  private boolean validate(Object target) {
    if (!(target instanceof AsyncMarkerFlagFieldAccessor)) {
      if (isDebug) {
        logger.debug(
            "Invalid target object. Need field accessor({}).",
            AsyncMarkerFlagFieldAccessor.class.getName());
      }
      return false;
    }
    return true;
  }
}
/**
 * @see
 *     JacksonPlugin#intercept_ObjectMapper(com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext)
 * @author Sungkook Kim
 */
public class WriteValueAsStringInterceptor implements AroundInterceptor {
  private final PLogger logger = PLoggerFactory.getLogger(getClass());
  private final boolean isDebug = logger.isDebugEnabled();

  private final MethodDescriptor descriptor;
  private final TraceContext traceContext;

  public WriteValueAsStringInterceptor(TraceContext traceContext, MethodDescriptor descriptor) {
    this.descriptor = descriptor;
    this.traceContext = traceContext;
  }

  @Override
  public void before(Object target, Object[] args) {
    if (isDebug) {
      logger.beforeInterceptor(target, args);
    }
    Trace trace = traceContext.currentTraceObject();
    if (trace == null) {
      return;
    }

    SpanEventRecorder recorder = trace.traceBlockBegin();
    recorder.recordServiceType(JacksonConstants.SERVICE_TYPE);
  }

  @Override
  public void after(Object target, Object result, Throwable throwable, Object[] args) {
    if (isDebug) {
      logger.afterInterceptor(target, args);
    }
    Trace trace = traceContext.currentTraceObject();
    if (trace == null) {
      return;
    }

    try {
      SpanEventRecorder recorder = trace.currentSpanEventRecorder();
      recorder.recordApi(descriptor);
      recorder.recordException(throwable);

      if (result != null) {
        recorder.recordAttribute(
            JacksonConstants.ANNOTATION_KEY_LENGTH_VALUE, ((String) result).length());
      }
    } finally {
      trace.traceBlockEnd();
    }
  }
}
/**
 * @author harebox
 * @author emeroad
 */
public class FutureSetOperationInterceptor implements AroundInterceptor, ArcusConstants {

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

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

    ((OperationAccessor) target)._$PINPOINT$_setOperation((Operation) args[0]);
  }

  @Override
  public void after(Object target, Object result, Throwable throwable, Object[] args) {
    if (isDebug) {
      logger.afterInterceptor(target, args);
    }
  }
}
/** @author jaehong.kim */
public class UserIncludeMethodInterceptor implements AroundInterceptor {
  // scope name, must be unique.
  private static final String SCOPE_NAME = "##USER_INCLUDE";
  private static final UserIncludeMethodDescriptor USER_INCLUDE_METHOD_DESCRIPTOR =
      new UserIncludeMethodDescriptor();

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

  private TraceContext traceContext;
  private MethodDescriptor descriptor;

  public UserIncludeMethodInterceptor(
      TraceContext traceContext, MethodDescriptor methodDescriptor) {
    this.traceContext = traceContext;
    this.descriptor = methodDescriptor;

    traceContext.cacheApi(USER_INCLUDE_METHOD_DESCRIPTOR);
  }

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

    Trace trace = traceContext.currentRawTraceObject();
    if (trace == null) {
      // create user include trace for standalone entry point.
      trace = createUserIncludeTrace();
      if (trace == null) {
        return;
      }
    }

    // check user include trace.
    if (!isUserIncludeTrace(trace)) {
      return;
    }

    // entry scope(default & disable trace).
    entryUserIncludeTraceScope(trace);

    // check sampled.
    if (!trace.canSampled()) {
      // skip
      return;
    }

    trace.traceBlockBegin();
  }

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

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

    // check user include trace.
    if (!isUserIncludeTrace(trace)) {
      return;
    }

    // leave scope(default & disable trace).
    if (!leaveUserIncludeTraceScope(trace)) {
      logger.warn(
          "Failed to leave scope of user include trace. trace={}, sampled={}",
          trace,
          trace.canSampled());
      // delete unstable trace.
      deleteUserIncludeTrace(trace);
      return;
    }

    // check sampled.
    if (!trace.canSampled()) {
      if (isUserIncludeTraceDestination(trace)) {
        deleteUserIncludeTrace(trace);
      }
      return;
    }

    try {
      final SpanEventRecorder recorder = trace.currentSpanEventRecorder();
      recorder.recordApi(descriptor);
      recorder.recordServiceType(ServiceType.INTERNAL_METHOD);
      recorder.recordException(throwable);
    } finally {
      trace.traceBlockEnd();
      if (isUserIncludeTraceDestination(trace)) {
        deleteUserIncludeTrace(trace);
      }
    }
  }

  private Trace createUserIncludeTrace() {
    final Trace trace = traceContext.newTraceObject();
    if (isDebug) {
      logger.debug("New user include trace {} and sampled {}", trace, trace.canSampled());
    }
    // add user scope.
    TraceScope oldScope = trace.addScope(SCOPE_NAME);
    if (oldScope != null) {
      // delete corrupted trace.
      logger.warn("Duplicated user include trace scope={}.", oldScope.getName());
      deleteUserIncludeTrace(trace);
      return null;
    }

    if (trace.canSampled()) {
      // record root span.
      final SpanRecorder recorder = trace.getSpanRecorder();
      recorder.recordServiceType(ServiceType.STAND_ALONE);
      recorder.recordApi(USER_INCLUDE_METHOD_DESCRIPTOR);
    }
    return trace;
  }

  private void deleteUserIncludeTrace(final Trace trace) {
    if (isDebug) {
      logger.debug("Delete user include trace={}, sampled={}", trace, trace.canSampled());
    }
    traceContext.removeTraceObject();
    trace.close();
  }

  private void entryUserIncludeTraceScope(final Trace trace) {
    final TraceScope scope = trace.getScope(SCOPE_NAME);
    if (scope != null) {
      scope.tryEnter();
    }
  }

  private boolean leaveUserIncludeTraceScope(final Trace trace) {
    final TraceScope scope = trace.getScope(SCOPE_NAME);
    if (scope != null) {
      if (scope.canLeave()) {
        scope.leave();
      } else {
        return false;
      }
    }
    return true;
  }

  private boolean isUserIncludeTrace(final Trace trace) {
    final TraceScope scope = trace.getScope(SCOPE_NAME);
    return scope != null;
  }

  private boolean isUserIncludeTraceDestination(final Trace trace) {
    final TraceScope scope = trace.getScope(SCOPE_NAME);
    return scope != null && !scope.isActive();
  }
}
/** @author jaehong.kim */
@Group(OkHttpConstants.SEND_REQUEST_SCOPE)
public class HttpEngineSendRequestMethodInterceptor
    implements SimpleAroundInterceptor, OkHttpConstants {
  private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
  private final boolean isDebug = logger.isDebugEnabled();

  private TraceContext traceContext;
  private MethodDescriptor methodDescriptor;
  private InterceptorGroup interceptorGroup;

  private final boolean cookie;
  private final DumpType cookieDumpType;
  private final SimpleSampler cookieSampler;
  private final boolean statusCode;

  public HttpEngineSendRequestMethodInterceptor(
      TraceContext traceContext,
      MethodDescriptor methodDescriptor,
      InterceptorGroup interceptorGroup,
      OkHttpPluginConfig config) {
    this.traceContext = traceContext;
    this.methodDescriptor = methodDescriptor;
    this.interceptorGroup = interceptorGroup;

    this.cookie = config.isCookie();
    this.cookieDumpType = config.getCookieDumpType();
    if (cookie) {
      cookieSampler = SimpleSamplerFactory.createSampler(cookie, config.getCookieSamplingRate());
    } else {
      this.cookieSampler = null;
    }

    statusCode = config.isStatusCode();
  }

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

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

    if (!validate(target)) {
      return;
    }

    if (!trace.canSampled()) {
      return;
    }

    SpanEventRecorder recorder = trace.traceBlockBegin();
    try {
      final TraceId nextId = trace.getTraceId().getNextTraceId();
      recorder.recordNextSpanId(nextId.getSpanId());
      recorder.recordServiceType(OK_HTTP_CLIENT);

      InterceptorGroupInvocation invocation = interceptorGroup.getCurrentInvocation();
      if (invocation != null) {
        invocation.getOrCreateAttachment(
            new AttachmentFactory() {
              @Override
              public Object createAttachment() {
                return nextId;
              }
            });
      }
    } catch (Throwable t) {
      logger.warn("Failed to BEFORE process. {}", t.getMessage(), t);
    }
  }

  private boolean validate(Object target) {
    if (!(target instanceof UserRequestGetter)) {
      logger.debug("Invalid target object. Need field accessor({}).", FIELD_USER_REQUEST);
      return false;
    }

    if (!(target instanceof UserResponseGetter)) {
      logger.debug("Invalid target object. Need field accessor({}).", FIELD_USER_RESPONSE);
      return false;
    }

    if (!(target instanceof ConnectionGetter)) {
      logger.debug("Invalid target object. Need field accessor({}).", FIELD_CONNECTION);
      return false;
    }

    return true;
  }

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

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

    if (!validate(target)) {
      return;
    }

    try {
      SpanEventRecorder recorder = trace.currentSpanEventRecorder();
      recorder.recordApi(methodDescriptor);
      recorder.recordException(throwable);

      Request request = ((UserRequestGetter) target)._$PINPOINT$_getUserRequest();
      if (request != null) {
        recorder.recordAttribute(AnnotationKey.HTTP_URL, request.httpUrl().toString());
        recorder.recordDestinationId(request.httpUrl().host() + ":" + request.httpUrl().port());
        recordRequest(trace, request, throwable);
      }

      // clear attachment.
      InterceptorGroupInvocation invocation = interceptorGroup.getCurrentInvocation();
      if (invocation != null && invocation.getAttachment() != null) {
        invocation.removeAttachment();
      }
    } finally {
      trace.traceBlockEnd();
    }
  }

  private void recordRequest(Trace trace, Request request, Throwable throwable) {
    final boolean isException = InterceptorUtils.isThrowable(throwable);
    if (cookie) {
      if (DumpType.ALWAYS == cookieDumpType) {
        recordCookie(request, trace);
      } else if (DumpType.EXCEPTION == cookieDumpType && isException) {
        recordCookie(request, trace);
      }
    }
  }

  private void recordCookie(Request request, Trace trace) {
    for (String cookie : request.headers("Cookie")) {
      if (cookieSampler.isSampling()) {
        final SpanEventRecorder recorder = trace.currentSpanEventRecorder();
        recorder.recordAttribute(AnnotationKey.HTTP_COOKIE, StringUtils.drop(cookie, 1024));
      }

      return;
    }
  }
}
/** @author jaehong.kim */
@Group(value = HttpClient4Constants.HTTP_CLIENT4_SCOPE, executionPolicy = ExecutionPolicy.ALWAYS)
public class HttpRequestExecutorDoSendRequestAndDoReceiveResponseMethodInterceptor
    implements AroundInterceptor {

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

  private final TraceContext traceContext;
  private final MethodDescriptor methodDescriptor;
  private final InterceptorGroup interceptorGroup;

  public HttpRequestExecutorDoSendRequestAndDoReceiveResponseMethodInterceptor(
      TraceContext traceContext,
      MethodDescriptor methodDescriptor,
      InterceptorGroup interceptorGroup) {
    this.traceContext = traceContext;
    this.methodDescriptor = methodDescriptor;
    this.interceptorGroup = interceptorGroup;
  }

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

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

    InterceptorGroupInvocation invocation = interceptorGroup.getCurrentInvocation();
    if (invocation != null && invocation.getAttachment() != null) {
      HttpCallContext callContext = (HttpCallContext) invocation.getAttachment();
      if (methodDescriptor.getMethodName().equals("doSendRequest")) {
        callContext.setWriteBeginTime(System.currentTimeMillis());
      } else {
        callContext.setReadBeginTime(System.currentTimeMillis());
      }
      logger.debug("Set call context {}", callContext);
    }
  }

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

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

    InterceptorGroupInvocation invocation = interceptorGroup.getCurrentInvocation();
    if (invocation != null && invocation.getAttachment() != null) {
      HttpCallContext callContext = (HttpCallContext) invocation.getAttachment();
      if (methodDescriptor.getMethodName().equals("doSendRequest")) {
        callContext.setWriteEndTime(System.currentTimeMillis());
        callContext.setWriteFail(throwable != null);
      } else {
        callContext.setReadEndTime(System.currentTimeMillis());
        callContext.setReadFail(throwable != null);
      }
      logger.debug("Set call context {}", callContext);
    }
  }
}
/** @author HyunGil Jeong */
@Group(value = THRIFT_CLIENT_SCOPE, executionPoint = ExecutionPolicy.BOUNDARY)
public class TAsyncClientManagerCallInterceptor
    implements SimpleAroundInterceptor, ThriftConstants {

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

  private final TraceContext traceContext;
  private final MethodDescriptor descriptor;
  private final InterceptorGroup group;
  private final MetadataAccessor asyncTraceIdAccessor;
  private final MetadataAccessor asyncNextSpanIdAccessor;
  private final MetadataAccessor asyncCallRemoteAddressAccessor;
  private final MetadataAccessor nonblockingSocketAddressAccessor;

  public TAsyncClientManagerCallInterceptor(
      TraceContext traceContext,
      MethodDescriptor descriptor,
      @Name(THRIFT_CLIENT_SCOPE) InterceptorGroup group,
      @Name(METADATA_ASYNC_TRACE_ID) MetadataAccessor asyncTraceIdAccessor,
      @Name(METADATA_ASYNC_NEXT_SPAN_ID) MetadataAccessor asyncNextSpanIdAccessor,
      @Name(METADATA_ASYNC_CALL_REMOTE_ADDRESS) MetadataAccessor asyncCallRemoteAddressAccessor,
      @Name(METADATA_NONBLOCKING_SOCKET_ADDRESS)
          MetadataAccessor nonblockingSocketAddressAccessor) {
    this.traceContext = traceContext;
    this.descriptor = descriptor;
    this.group = group;
    this.asyncTraceIdAccessor = asyncTraceIdAccessor;
    this.asyncNextSpanIdAccessor = asyncNextSpanIdAccessor;
    this.asyncCallRemoteAddressAccessor = asyncCallRemoteAddressAccessor;
    this.nonblockingSocketAddressAccessor = nonblockingSocketAddressAccessor;
  }

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

    if (!validate(target, args)) {
      return;
    }

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

    try {
      ThriftRequestProperty parentTraceInfo = new ThriftRequestProperty();
      final boolean shouldSample = trace.canSampled();
      if (!shouldSample) {
        if (isDebug) {
          logger.debug("set Sampling flag=false");
        }
        parentTraceInfo.setShouldSample(shouldSample);
      } else {
        SpanEventRecorder recorder = trace.traceBlockBegin();
        Object asyncMethodCallObj = args[0];
        // inject async trace info to AsyncMethodCall object
        final AsyncTraceId asyncTraceId = injectAsyncTraceId(asyncMethodCallObj, trace);

        recorder.recordServiceType(THRIFT_CLIENT_INTERNAL);

        // retrieve connection information
        String remoteAddress = getRemoteAddress(asyncMethodCallObj);

        final TraceId nextId = asyncTraceId.getNextTraceId();

        // Inject nextSpanId as the actual sending of data will be handled asynchronously.
        final long nextSpanId = nextId.getSpanId();
        parentTraceInfo.setSpanId(nextSpanId);

        parentTraceInfo.setTraceId(nextId.getTransactionId());
        parentTraceInfo.setParentSpanId(nextId.getParentSpanId());

        parentTraceInfo.setFlags(nextId.getFlags());
        parentTraceInfo.setParentApplicationName(this.traceContext.getApplicationName());
        parentTraceInfo.setParentApplicationType(this.traceContext.getServerTypeCode());
        parentTraceInfo.setAcceptorHost(remoteAddress);

        this.asyncCallRemoteAddressAccessor.set(asyncMethodCallObj, remoteAddress);
        this.asyncNextSpanIdAccessor.set(asyncMethodCallObj, nextSpanId);
      }
      InterceptorGroupInvocation currentTransaction = this.group.getCurrentInvocation();
      currentTransaction.setAttachment(parentTraceInfo);
    } catch (Throwable t) {
      logger.warn("before error. Caused:{}", t.getMessage(), t);
    }
  }

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

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

    try {
      SpanEventRecorder recorder = trace.currentSpanEventRecorder();
      recorder.recordApi(this.descriptor);
      recorder.recordException(throwable);
    } catch (Throwable t) {
      logger.warn("after error. Caused:{}", t.getMessage(), t);
    } finally {
      trace.traceBlockEnd();
    }
  }

  private boolean validate(final Object target, final Object[] args) {
    if (args.length != 1) {
      return false;
    }

    Object asyncMethodCallObj = args[0];
    if (asyncMethodCallObj == null) {
      if (isDebug) {
        logger.debug("Metadata injection target object is null.");
      }
      return false;
    }

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

    if (!this.asyncNextSpanIdAccessor.isApplicable(asyncMethodCallObj)) {
      if (isDebug) {
        logger.debug(
            "Invalid target object. Need metadata accessor({})", METADATA_ASYNC_NEXT_SPAN_ID);
      }
      return false;
    }

    if (!this.asyncCallRemoteAddressAccessor.isApplicable(asyncMethodCallObj)) {
      if (isDebug) {
        logger.debug(
            "Invalid target object. Need metadata accessor({})",
            METADATA_ASYNC_CALL_REMOTE_ADDRESS);
      }
      return false;
    }

    return true;
  }

  private AsyncTraceId injectAsyncTraceId(final Object asyncMethodCallObj, final Trace trace) {
    final AsyncTraceId asyncTraceId = trace.getAsyncTraceId();
    SpanEventRecorder recorder = trace.currentSpanEventRecorder();
    recorder.recordNextAsyncId(asyncTraceId.getAsyncId());
    this.asyncTraceIdAccessor.set(asyncMethodCallObj, asyncTraceId);
    if (isDebug) {
      logger.debug("Set asyncTraceId metadata {}", asyncTraceId);
    }
    return asyncTraceId;
  }

  private String getRemoteAddress(Object asyncMethodCallObj) {
    if (!this.nonblockingSocketAddressAccessor.isApplicable(asyncMethodCallObj)) {
      if (isDebug) {
        logger.debug(
            "Invalid TAsyncMethodCall object. Need metadata accessor({})",
            METADATA_NONBLOCKING_SOCKET_ADDRESS);
      }
      return UNKNOWN_ADDRESS;
    }
    Object socketAddress = this.nonblockingSocketAddressAccessor.get(asyncMethodCallObj);
    if (socketAddress instanceof SocketAddress) {
      return ThriftUtils.getHostPort((SocketAddress) socketAddress);
    }
    return UNKNOWN_ADDRESS;
  }
}
Esempio n. 15
0
 @BeforeClass
 public static void before() {
   PLoggerFactory.initialize(new Slf4jLoggerBinder());
 }
/**
 * Entry/exit point for tracing synchronous processors for Thrift services.
 *
 * <p>Because trace objects cannot be created until the message is read, this interceptor merely
 * sends trace objects created by other interceptors in the tracing pipeline:
 *
 * <ol>
 *   <li>
 *       <p>{@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.server.ProcessFunctionProcessInterceptor
 *       ProcessFunctionProcessInterceptor} marks the start of a trace, and sets up the environment
 *       for trace data to be injected.
 *   <li>
 *       <p>{@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadFieldBeginInterceptor
 *       TProtocolReadFieldBeginInterceptor}, {@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadTTypeInterceptor
 *       TProtocolReadTTypeInterceptor} reads the header fields and injects the parent trace object
 *       (if any).
 *   <li>
 *       <p>{@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageEndInterceptor
 *       TProtocolReadMessageEndInterceptor} creates the actual root trace object.
 * </ol>
 *
 * <p><b><tt>TBaseProcessorProcessInterceptor</tt></b> -> <tt>ProcessFunctionProcessInterceptor</tt>
 * -> <tt>TProtocolReadFieldBeginInterceptor</tt> <-> <tt>TProtocolReadTTypeInterceptor</tt> ->
 * <tt>TProtocolReadMessageEndInterceptor</tt>
 *
 * <p>Based on Thrift 0.8.0+
 *
 * @author HyunGil Jeong
 * @see com.navercorp.pinpoint.plugin.thrift.interceptor.server.ProcessFunctionProcessInterceptor
 *     ProcessFunctionProcessInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadFieldBeginInterceptor
 *     TProtocolReadFieldBeginInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadTTypeInterceptor
 *     TProtocolReadTTypeInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageEndInterceptor
 *     TProtocolReadMessageEndInterceptor
 */
@Group(value = THRIFT_SERVER_SCOPE, executionPoint = ExecutionPolicy.BOUNDARY)
public class TBaseProcessorProcessInterceptor implements SimpleAroundInterceptor, ThriftConstants {

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

  private final TraceContext traceContext;
  private final MethodDescriptor descriptor;
  private final InterceptorGroup group;
  private final MetadataAccessor socketAccessor;

  public TBaseProcessorProcessInterceptor(
      TraceContext traceContext,
      MethodDescriptor descriptor,
      @Name(THRIFT_SERVER_SCOPE) InterceptorGroup group,
      @Name(METADATA_SOCKET) MetadataAccessor socketAccessor) {
    this.traceContext = traceContext;
    this.descriptor = descriptor;
    this.group = group;
    this.socketAccessor = socketAccessor;
  }

  @Override
  public void before(Object target, Object[] args) {
    // Do nothing
  }

  @Override
  public void after(Object target, Object[] args, Object result, Throwable throwable) {
    final Trace trace = this.traceContext.currentRawTraceObject();
    if (trace == null) {
      return;
    }
    // logging here as some Thrift servers depend on TTransportException being thrown for normal
    // operations.
    // log only when current transaction is being traced.
    if (isDebug) {
      logger.afterInterceptor(target, args, result, throwable);
    }
    this.traceContext.removeTraceObject();
    if (trace.canSampled()) {
      try {
        processTraceObject(trace, target, args, throwable);
      } catch (Throwable t) {
        logger.warn("Error processing trace object. Cause:{}", t.getMessage(), t);
      } finally {
        trace.close();
      }
    }
  }

  private void processTraceObject(
      final Trace trace, Object target, Object[] args, Throwable throwable) {
    // end spanEvent
    try {
      SpanEventRecorder recorder = trace.currentSpanEventRecorder();
      // TODO Might need a way to collect and record method arguments
      // trace.recordAttribute(...);
      recorder.recordException(throwable);
      recorder.recordApi(this.descriptor);
    } catch (Throwable t) {
      logger.warn("Error processing trace object. Cause:{}", t.getMessage(), t);
    } finally {
      trace.traceBlockEnd();
    }

    // end root span
    SpanRecorder recorder = trace.getSpanRecorder();
    String methodUri = getMethodUri(target);
    recorder.recordRpcName(methodUri);
    // retrieve connection information
    String localIpPort = UNKNOWN_ADDRESS;
    String remoteAddress = UNKNOWN_ADDRESS;
    if (args.length == 2 && args[0] instanceof TProtocol) {
      TProtocol inputProtocol = (TProtocol) args[0];
      if (this.socketAccessor.isApplicable(inputProtocol.getTransport())) {
        Socket socket = this.socketAccessor.get(inputProtocol.getTransport());
        if (socket != null) {
          localIpPort = ThriftUtils.getHostPort(socket.getLocalSocketAddress());
          remoteAddress = ThriftUtils.getHost(socket.getRemoteSocketAddress());
        }
      }
    }
    if (localIpPort != UNKNOWN_ADDRESS) {
      recorder.recordEndPoint(localIpPort);
    }
    if (remoteAddress != UNKNOWN_ADDRESS) {
      recorder.recordRemoteAddress(remoteAddress);
    }
  }

  private String getMethodUri(Object target) {
    String methodUri = UNKNOWN_METHOD_URI;
    InterceptorGroupInvocation currentTransaction = this.group.getCurrentInvocation();
    Object attachment = currentTransaction.getAttachment();
    if (attachment instanceof ThriftClientCallContext && target instanceof TBaseProcessor) {
      ThriftClientCallContext clientCallContext = (ThriftClientCallContext) attachment;
      String methodName = clientCallContext.getMethodName();
      methodUri = ThriftUtils.getProcessorNameAsUri((TBaseProcessor<?>) target);
      StringBuilder sb = new StringBuilder(methodUri);
      if (!methodUri.endsWith("/")) {
        sb.append("/");
      }
      sb.append(methodName);
      methodUri = sb.toString();
    }
    return methodUri;
  }
}
/**
 * Entry/exit point for tracing asynchronous processors for Thrift services.
 *
 * <p>Because trace objects cannot be created until the message is read, this interceptor works in
 * tandem with other interceptors in the tracing pipeline. The actual processing of input messages
 * is not off-loaded to <tt>AsyncProcessFunction</tt> (unlike synchronous processors where
 * <tt>ProcessFunction</tt> does most of the work).
 *
 * <ol>
 *   <li>
 *       <p>{@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageBeginInterceptor
 *       TProtocolReadMessageBeginInterceptor} retrieves the method name called by the client.
 *   <li>
 *       <p>{@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadFieldBeginInterceptor
 *       TProtocolReadFieldBeginInterceptor}, {@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadTTypeInterceptor
 *       TProtocolReadTTypeInterceptor} reads the header fields and injects the parent trace object
 *       (if any).
 *   <li>
 *       <p>{@link
 *       com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageEndInterceptor
 *       TProtocolReadMessageEndInterceptor} creates the actual root trace object.
 * </ol>
 *
 * <p><b><tt>TBaseAsyncProcessorProcessInterceptor</tt></b> ->
 * <tt>TProtocolReadMessageBeginInterceptor</tt> -> <tt>TProtocolReadFieldBeginInterceptor</tt> <->
 * <tt>TProtocolReadTTypeInterceptor</tt> -> <tt>TProtocolReadMessageEndInterceptor</tt>
 *
 * <p>Based on Thrift 0.9.1+
 *
 * @author HyunGil Jeong
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageBeginInterceptor
 *     TProtocolReadMessageBeginInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadFieldBeginInterceptor
 *     TProtocolReadFieldBeginInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadTTypeInterceptor
 *     TProtocolReadTTypeInterceptor
 * @see
 *     com.navercorp.pinpoint.plugin.thrift.interceptor.tprotocol.server.TProtocolReadMessageEndInterceptor
 *     TProtocolReadMessageEndInterceptor
 */
@Scope(value = THRIFT_SERVER_SCOPE, executionPolicy = ExecutionPolicy.BOUNDARY)
public class TBaseAsyncProcessorProcessInterceptor implements AroundInterceptor {

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

  private final TraceContext traceContext;
  private final MethodDescriptor descriptor;
  private final InterceptorScope scope;

  public TBaseAsyncProcessorProcessInterceptor(
      TraceContext traceContext,
      MethodDescriptor descriptor,
      @Name(THRIFT_SERVER_SCOPE) InterceptorScope scope) {
    this.traceContext = traceContext;
    this.descriptor = descriptor;
    this.scope = scope;
  }

  @Override
  public void before(Object target, Object[] args) {
    if (isDebug) {
      logger.beforeInterceptor(target, args);
    }
    // process(final AsyncFrameBuffer fb)
    if (args.length != 1) {
      return;
    }
    // Set server markers
    if (args[0] instanceof AsyncFrameBuffer) {
      AsyncFrameBuffer frameBuffer = (AsyncFrameBuffer) args[0];
      attachMarkersToInputProtocol(frameBuffer.getInputProtocol(), true);
    }
  }

  @Override
  public void after(Object target, Object[] args, Object result, Throwable throwable) {
    if (isDebug) {
      logger.afterInterceptor(target, args, result, throwable);
    }
    // Unset server markers
    if (args[0] instanceof AsyncFrameBuffer) {
      AsyncFrameBuffer frameBuffer = (AsyncFrameBuffer) args[0];
      attachMarkersToInputProtocol(frameBuffer.getInputProtocol(), false);
    }
    final Trace trace = this.traceContext.currentRawTraceObject();
    if (trace == null) {
      return;
    }
    this.traceContext.removeTraceObject();
    if (trace.canSampled()) {
      try {
        processTraceObject(trace, target, args, throwable);
      } catch (Throwable t) {
        logger.warn("Error processing trace object. Cause:{}", t.getMessage(), t);
      } finally {
        trace.close();
      }
    }
  }

  private boolean validateInputProtocol(Object iprot) {
    if (iprot instanceof TProtocol) {
      if (!(iprot instanceof ServerMarkerFlagFieldAccessor)) {
        if (isDebug) {
          logger.debug(
              "Invalid target object. Need field accessor({}).",
              ServerMarkerFlagFieldAccessor.class.getName());
        }
        return false;
      }
      if (!(iprot instanceof AsyncMarkerFlagFieldAccessor)) {
        if (isDebug) {
          logger.debug(
              "Invalid target object. Need field accessor({}).",
              AsyncMarkerFlagFieldAccessor.class.getName());
        }
        return false;
      }
      return true;
    }
    return false;
  }

  private void attachMarkersToInputProtocol(TProtocol iprot, boolean flag) {
    if (validateInputProtocol(iprot)) {
      ((ServerMarkerFlagFieldAccessor) iprot)._$PINPOINT$_setServerMarkerFlag(flag);
      ((AsyncMarkerFlagFieldAccessor) iprot)._$PINPOINT$_setAsyncMarkerFlag(flag);
    }
  }

  private void processTraceObject(
      final Trace trace, Object target, Object[] args, Throwable throwable) {
    // end spanEvent
    try {
      // TODO Might need a way to collect and record method arguments
      // trace.recordAttribute(...);
      SpanEventRecorder recorder = trace.currentSpanEventRecorder();
      recorder.recordException(throwable);
      recorder.recordApi(this.descriptor);
    } catch (Throwable t) {
      logger.warn("Error processing trace object. Cause:{}", t.getMessage(), t);
    } finally {
      trace.traceBlockEnd();
    }

    // end root span
    SpanRecorder recorder = trace.getSpanRecorder();
    String methodUri = getMethodUri(target);
    recorder.recordRpcName(methodUri);
  }

  private String getMethodUri(Object target) {
    String methodUri = ThriftConstants.UNKNOWN_METHOD_URI;
    InterceptorScopeInvocation currentTransaction = this.scope.getCurrentInvocation();
    Object attachment = currentTransaction.getAttachment();
    if (attachment instanceof ThriftClientCallContext && target instanceof TBaseAsyncProcessor) {
      ThriftClientCallContext clientCallContext = (ThriftClientCallContext) attachment;
      String methodName = clientCallContext.getMethodName();
      methodUri = ThriftUtils.getAsyncProcessorNameAsUri((TBaseAsyncProcessor<?>) target);
      StringBuilder sb = new StringBuilder(methodUri);
      if (!methodUri.endsWith("/")) {
        sb.append("/");
      }
      sb.append(methodName);
      methodUri = sb.toString();
    }
    return methodUri;
  }
}
Esempio n. 18
0
/** @author jaehong.kim */
public class UserPlugin implements ProfilerPlugin, UserConstants {
  private final PLogger logger = PLoggerFactory.getLogger(this.getClass());

  @Override
  public void setup(ProfilerPluginSetupContext context) {
    final UserPluginConfig config = new UserPluginConfig(context.getConfig());

    // add user include methods
    for (String fullQualifiedMethodName : config.getIncludeList()) {
      try {
        addUserIncludeClass(context, fullQualifiedMethodName);
        if (logger.isDebugEnabled()) {
          logger.debug("Add user include class interceptor {}", fullQualifiedMethodName);
        }
      } catch (Exception e) {
        logger.warn("Failed to add user include class(" + fullQualifiedMethodName + ").", e);
      }
    }
  }

  private void addUserIncludeClass(
      ProfilerPluginSetupContext context, final String fullQualifiedMethodName) {
    final String className = toClassName(fullQualifiedMethodName);
    final String methodName = toMethodName(fullQualifiedMethodName);

    context.addClassFileTransformer(
        className,
        new PinpointClassFileTransformer() {

          @Override
          public byte[] transform(
              PinpointInstrument instrumentContext,
              ClassLoader classLoader,
              String className,
              Class<?> classBeingRedefined,
              ProtectionDomain protectionDomain,
              byte[] classfileBuffer)
              throws InstrumentException {
            InstrumentClass target =
                instrumentContext.getInstrumentClass(classLoader, className, classfileBuffer);

            for (InstrumentMethod method :
                target.getDeclaredMethods(MethodFilters.name(methodName))) {
              try {
                method.addInterceptor(
                    "com.navercorp.pinpoint.plugin.user.interceptor.UserIncludeMethodInterceptor");
              } catch (Exception e) {
                if (logger.isWarnEnabled()) {
                  logger.warn("Unsupported method " + method, e);
                }
              }
            }

            return target.toBytecode();
          }
        });
  }

  String toClassName(String fullQualifiedMethodName) {
    final int classEndPosition = fullQualifiedMethodName.lastIndexOf(".");
    if (classEndPosition <= 0) {
      throw new IllegalArgumentException(
          "invalid full qualified method name(" + fullQualifiedMethodName + "). not found method");
    }

    return fullQualifiedMethodName.substring(0, classEndPosition);
  }

  String toMethodName(String fullQualifiedMethodName) {
    final int methodBeginPosition = fullQualifiedMethodName.lastIndexOf(".");
    if (methodBeginPosition <= 0 || methodBeginPosition + 1 >= fullQualifiedMethodName.length()) {
      throw new IllegalArgumentException(
          "invalid full qualified method name(" + fullQualifiedMethodName + "). not found method");
    }

    return fullQualifiedMethodName.substring(methodBeginPosition + 1);
  }
}
Esempio n. 19
0
/** @author jaehong.kim */
public class OkHttpPlugin implements ProfilerPlugin {
  private final PLogger logger = PLoggerFactory.getLogger(this.getClass());

  @Override
  public void setup(ProfilerPluginSetupContext context) {
    final OkHttpPluginConfig config = new OkHttpPluginConfig(context.getConfig());
    logger.debug("[OkHttp] Initialized config={}", config);

    logger.debug("[OkHttp] Add Call class.");
    addCall(context, config);
    logger.debug("[OkHttp] Add Dispatcher class.");
    addDispatcher(context, config);
    logger.debug("[OkHttp] Add AsyncCall class.");
    addAsyncCall(context, config);
    addHttpEngine(context, config);
    addRequestBuilder(context, config);
  }

  private void addCall(ProfilerPluginSetupContext context, final OkHttpPluginConfig config) {
    context.addClassFileTransformer(
        "com.squareup.okhttp.Call",
        new TransformCallback() {

          @Override
          public byte[] doInTransform(
              Instrumentor instrumentContext,
              ClassLoader loader,
              String className,
              Class<?> classBeingRedefined,
              ProtectionDomain protectionDomain,
              byte[] classfileBuffer)
              throws InstrumentException {
            InstrumentClass target =
                instrumentContext.getInstrumentClass(loader, className, classfileBuffer);

            for (InstrumentMethod method :
                target.getDeclaredMethods(MethodFilters.name("execute", "enqueue", "cancel"))) {
              method.addInterceptor(
                  "com.navercorp.pinpoint.plugin.okhttp.interceptor.CallMethodInterceptor");
            }

            return target.toBytecode();
          }
        });
  }

  private void addDispatcher(ProfilerPluginSetupContext context, final OkHttpPluginConfig config) {
    context.addClassFileTransformer(
        "com.squareup.okhttp.Dispatcher",
        new TransformCallback() {

          @Override
          public byte[] doInTransform(
              Instrumentor instrumentContext,
              ClassLoader loader,
              String className,
              Class<?> classBeingRedefined,
              ProtectionDomain protectionDomain,
              byte[] classfileBuffer)
              throws InstrumentException {
            InstrumentClass target =
                instrumentContext.getInstrumentClass(loader, className, classfileBuffer);

            for (InstrumentMethod method :
                target.getDeclaredMethods(MethodFilters.name("execute", "cancel"))) {
              logger.debug("[OkHttp] Add Dispatcher.execute | cancel interceptor.");
              method.addInterceptor(
                  OkHttpConstants.BASIC_METHOD_INTERCEPTOR,
                  va(OkHttpConstants.OK_HTTP_CLIENT_INTERNAL));
            }
            InstrumentMethod enqueueMethod =
                target.getDeclaredMethod("enqueue", "com.squareup.okhttp.Call$AsyncCall");
            if (enqueueMethod != null) {
              logger.debug("[OkHttp] Add Dispatcher.enqueue interceptor.");
              enqueueMethod.addInterceptor(
                  "com.navercorp.pinpoint.plugin.okhttp.interceptor.DispatcherEnqueueMethodInterceptor");
            }

            return target.toBytecode();
          }
        });
  }

  private void addAsyncCall(ProfilerPluginSetupContext context, final OkHttpPluginConfig config) {
    context.addClassFileTransformer(
        "com.squareup.okhttp.Call$AsyncCall",
        new TransformCallback() {

          @Override
          public byte[] doInTransform(
              Instrumentor instrumentContext,
              ClassLoader loader,
              String className,
              Class<?> classBeingRedefined,
              ProtectionDomain protectionDomain,
              byte[] classfileBuffer)
              throws InstrumentException {
            InstrumentClass target =
                instrumentContext.getInstrumentClass(loader, className, classfileBuffer);
            target.addField(AsyncTraceIdAccessor.class.getName());

            InstrumentMethod executeMethod = target.getDeclaredMethod("execute");
            if (executeMethod != null) {
              logger.debug("[OkHttp] Add AsyncCall.execute interceptor.");
              executeMethod.addInterceptor(
                  "com.navercorp.pinpoint.plugin.okhttp.interceptor.AsyncCallExecuteMethodInterceptor");
            }

            return target.toBytecode();
          }
        });
  }

  private void addHttpEngine(
      final ProfilerPluginSetupContext context, final OkHttpPluginConfig config) {
    context.addClassFileTransformer(
        "com.squareup.okhttp.internal.http.HttpEngine",
        new TransformCallback() {

          @Override
          public byte[] doInTransform(
              Instrumentor instrumentContext,
              ClassLoader loader,
              String className,
              Class<?> classBeingRedefined,
              ProtectionDomain protectionDomain,
              byte[] classfileBuffer)
              throws InstrumentException {
            InstrumentClass target =
                instrumentContext.getInstrumentClass(loader, className, classfileBuffer);
            target.addGetter(UserRequestGetter.class.getName(), OkHttpConstants.FIELD_USER_REQUEST);
            target.addGetter(
                UserResponseGetter.class.getName(), OkHttpConstants.FIELD_USER_RESPONSE);
            target.addGetter(ConnectionGetter.class.getName(), OkHttpConstants.FIELD_CONNECTION);

            InstrumentMethod sendRequestMethod = target.getDeclaredMethod("sendRequest");
            if (sendRequestMethod != null) {
              logger.debug("[OkHttp] Add HttpEngine.sendRequest interceptor.");
              final ObjectRecipe objectRecipe =
                  ObjectRecipe.byConstructor(
                      "com.navercorp.pinpoint.plugin.okhttp.OkHttpPluginConfig",
                      context.getConfig());
              sendRequestMethod.addInterceptor(
                  "com.navercorp.pinpoint.plugin.okhttp.interceptor.HttpEngineSendRequestMethodInterceptor",
                  va(objectRecipe));
            }

            InstrumentMethod connectMethod = target.getDeclaredMethod("connect");
            if (connectMethod != null) {
              logger.debug("[OkHttp] Add HttpEngine.connect interceptor.");
              connectMethod.addInterceptor(
                  "com.navercorp.pinpoint.plugin.okhttp.interceptor.HttpEngineConnectMethodInterceptor");
            }

            InstrumentMethod readResponseMethod = target.getDeclaredMethod("readResponse");
            if (readResponseMethod != null) {
              logger.debug("[OkHttp] Add HttpEngine.connect interceptor.");
              readResponseMethod.addInterceptor(
                  "com.navercorp.pinpoint.plugin.okhttp.interceptor.HttpEngineReadResponseMethodInterceptor",
                  va(config.isStatusCode()));
            }

            return target.toBytecode();
          }
        });
  }

  private void addRequestBuilder(
      ProfilerPluginSetupContext context, final OkHttpPluginConfig config) {
    context.addClassFileTransformer(
        "com.squareup.okhttp.Request$Builder",
        new TransformCallback() {

          @Override
          public byte[] doInTransform(
              Instrumentor instrumentContext,
              ClassLoader loader,
              String className,
              Class<?> classBeingRedefined,
              ProtectionDomain protectionDomain,
              byte[] classfileBuffer)
              throws InstrumentException {
            InstrumentClass target =
                instrumentContext.getInstrumentClass(loader, className, classfileBuffer);
            target.addGetter(HttpUrlGetter.class.getName(), OkHttpConstants.FIELD_HTTP_URL);

            InstrumentMethod buildMethod = target.getDeclaredMethod("build");
            if (buildMethod != null) {
              logger.debug("[OkHttp] Add Request.Builder.build interceptor.");
              buildMethod.addInterceptor(
                  "com.navercorp.pinpoint.plugin.okhttp.interceptor.RequestBuilderBuildMethodInterceptor");
            }

            return target.toBytecode();
          }
        });
  }
}
/** @author jaehong.kim */
@Group(value = HttpClientConstants.EXECUTE_ASYNC_SCOPE, executionPolicy = ExecutionPolicy.ALWAYS)
public class HttpRequestExecuteAsyncMethodInterceptor implements AroundInterceptor {
  private final PLogger logger = PLoggerFactory.getLogger(this.getClass());
  private final boolean isDebug = logger.isDebugEnabled();

  private TraceContext traceContext;
  private MethodDescriptor methodDescriptor;
  private InterceptorGroup interceptorGroup;

  public HttpRequestExecuteAsyncMethodInterceptor(
      TraceContext traceContext,
      MethodDescriptor methodDescriptor,
      InterceptorGroup interceptorGroup) {
    this.traceContext = traceContext;
    this.methodDescriptor = methodDescriptor;
    this.interceptorGroup = interceptorGroup;
  }

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

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

    SpanEventRecorder recorder = trace.traceBlockBegin();
    try {
      // set asynchronous trace
      final AsyncTraceId asyncTraceId = trace.getAsyncTraceId();
      recorder.recordNextAsyncId(asyncTraceId.getAsyncId());

      // set async id.
      InterceptorGroupInvocation transaction = interceptorGroup.getCurrentInvocation();
      if (transaction != null) {
        transaction.setAttachment(asyncTraceId);
        if (isDebug) {
          logger.debug("Set asyncTraceId metadata {}", asyncTraceId);
        }
      }
    } catch (Throwable t) {
      logger.warn("Failed to before process. {}", t.getMessage(), t);
    }
  }

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

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

    try {
      SpanEventRecorder recorder = trace.currentSpanEventRecorder();
      recorder.recordApi(methodDescriptor);
      recorder.recordServiceType(HttpClientConstants.HTTP_CLIENT_INTERNAL);
      recorder.recordException(throwable);

      // remove async id.
      InterceptorGroupInvocation transaction = interceptorGroup.getCurrentInvocation();
      if (transaction != null) {
        // clear
        transaction.removeAttachment();
      }
    } finally {
      trace.traceBlockEnd();
    }
  }
}