@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);
    }
  }
  @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);
    }

    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);
    }
  }
  @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();
    }
  }