@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();
    }
  }
  @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 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);
    }
  }
  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;
    }
  }
 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;
 }
  @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
  protected void doInAfterTrace(
      SpanEventRecorder recorder,
      Object target,
      Object[] args,
      Object result,
      Throwable throwable) {
    DatabaseInfo databaseInfo = databaseInfoAccessor.get(target, UnKnownDatabaseInfo.INSTANCE);

    recorder.recordServiceType(databaseInfo.getType());
    recorder.recordEndPoint(databaseInfo.getMultipleHost());
    recorder.recordDestinationId(databaseInfo.getDatabaseId());

    recorder.recordApi(methodDescriptor, args);
    recorder.recordException(throwable);
  }
 @Override
 public void doInBeforeTrace(
     SpanEventRecorder recorder, final Object target, final Object[] args) {
   if (args.length == 1) {
     recorder.recordAttribute(AnnotationKey.ARGS0, args[0]);
   }
 }
  @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);
      }
    }
  }
 @Override
 protected void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) {
   if (args != null && args.length >= 2 && args[1] != null && args[1] instanceof HttpRoute) {
     final HttpRoute route = (HttpRoute) args[1];
     final StringBuilder sb = new StringBuilder();
     if (route.getProxyHost() != null) {
       sb.append(route.getProxyHost().getHostName()).append(":");
       sb.append(route.getProxyHost().getPort());
     } else {
       sb.append(route.getTargetHost().getHostName()).append(":");
       sb.append(route.getTargetHost().getPort());
     }
     recorder.recordAttribute(AnnotationKey.HTTP_INTERNAL_DISPLAY, sb.toString());
   }
   recorder.recordApi(methodDescriptor);
   recorder.recordServiceType(HttpClient4Constants.HTTP_CLIENT_4_INTERNAL);
 }
 @Override
 protected void doInAfterTrace(
     SpanEventRecorder recorder,
     Object target,
     Object[] args,
     Object result,
     Throwable throwable) {
   recorder.recordException(throwable);
 }
  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);
  }
  @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();
    }
  }
  @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 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);
    }
  }
  @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();
    }
  }
  @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();
    }
  }