@Test
  public void traceAsyncTableEndpoint() {
    final AsyncConnection connection = connectionRule.getAsyncConnection();
    final AsyncTable<?> table = connection.getTable(TEST_TABLE);
    final EchoRequestProto request = EchoRequestProto.newBuilder().setMessage("hello").build();
    final CompletableFuture<Map<byte[], String>> future = new CompletableFuture<>();
    final AsyncTable.CoprocessorCallback<EchoResponseProto> callback =
      new AsyncTable.CoprocessorCallback<EchoResponseProto>() {
        final ConcurrentMap<byte[], String> results = new ConcurrentHashMap<>();

        @Override
        public void onRegionComplete(RegionInfo region, EchoResponseProto resp) {
          if (!future.isDone()) {
            results.put(region.getRegionName(), resp.getMessage());
          }
        }

        @Override
        public void onRegionError(RegionInfo region, Throwable error) {
          if (!future.isDone()) {
            future.completeExceptionally(error);
          }
        }

        @Override
        public void onComplete() {
          if (!future.isDone()) {
            future.complete(results);
          }
        }

        @Override
        public void onError(Throwable error) {
          if (!future.isDone()) {
            future.completeExceptionally(error);
          }
        }
      };

    final Map<byte[], String> results = TraceUtil.trace(() -> {
      table.coprocessorService(TestProtobufRpcProto::newStub,
        (stub, controller, cb) -> stub.echo(controller, request, cb), callback).execute();
      try {
        return future.get();
      } catch (InterruptedException | ExecutionException e) {
        throw new RuntimeException(e);
      }
    }, testName.getMethodName());
    assertNotNull(results);
    assertTrue("coprocessor call returned no results.", MapUtils.isNotEmpty(results));
    assertThat(results.values(), everyItem(allOf(notNullValue(), equalTo("hello"))));

    final Matcher<SpanData> parentMatcher = allOf(hasName(testName.getMethodName()), hasEnded());
    waitForAndLog(parentMatcher);
    final List<SpanData> spans = otelClassRule.getSpans();

    final SpanData testSpan =
      spans.stream().filter(parentMatcher::matches).findFirst().orElseThrow(AssertionError::new);
    final Matcher<SpanData> tableOpMatcher = allOf(hasName(containsString("COPROC_EXEC")),
      hasParentSpanId(testSpan), hasStatusWithCode(StatusCode.OK));
    assertThat(spans, hasItem(tableOpMatcher));
    final SpanData tableOpSpan =
      spans.stream().filter(tableOpMatcher::matches).findFirst().orElseThrow(AssertionError::new);
    final Matcher<SpanData> rpcMatcher = allOf(hasName("hbase.pb.ClientService/ExecService"),
      hasParentSpanId(tableOpSpan), hasStatusWithCode(StatusCode.OK));
    assertThat(spans, hasItem(rpcMatcher));
  }
Test cases to verify tracing coprocessor Endpoint execution
hbase-endpoint/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorEndpointTracing.java