TelemetryDataUtil.java 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Copyright The OpenTelemetry Authors
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package io.opentelemetry.instrumentation.testing.util;
  6. import static java.util.stream.Collectors.toList;
  7. import static org.assertj.core.api.Assertions.assertThat;
  8. import io.opentelemetry.api.trace.SpanId;
  9. import io.opentelemetry.api.trace.SpanKind;
  10. import io.opentelemetry.sdk.trace.data.SpanData;
  11. import java.util.ArrayList;
  12. import java.util.Arrays;
  13. import java.util.Comparator;
  14. import java.util.HashMap;
  15. import java.util.List;
  16. import java.util.Map;
  17. import java.util.concurrent.TimeUnit;
  18. import java.util.concurrent.TimeoutException;
  19. import java.util.function.Supplier;
  20. import java.util.stream.Collectors;
  21. public final class TelemetryDataUtil {
  22. public static Comparator<List<SpanData>> orderByRootSpanKind(SpanKind... spanKinds) {
  23. List<SpanKind> list = Arrays.asList(spanKinds);
  24. return Comparator.comparing(span -> list.indexOf(span.get(0).getKind()));
  25. }
  26. public static Comparator<List<SpanData>> orderByRootSpanName(String... names) {
  27. List<String> list = Arrays.asList(names);
  28. return Comparator.comparing(span -> list.indexOf(span.get(0).getName()));
  29. }
  30. public static List<List<SpanData>> groupTraces(List<SpanData> spans) {
  31. List<List<SpanData>> traces =
  32. new ArrayList<>(
  33. spans.stream().collect(Collectors.groupingBy(SpanData::getTraceId)).values());
  34. sortTraces(traces);
  35. for (int i = 0; i < traces.size(); i++) {
  36. List<SpanData> trace = traces.get(i);
  37. traces.set(i, sort(trace));
  38. }
  39. return traces;
  40. }
  41. public static List<List<SpanData>> waitForTraces(
  42. Supplier<List<SpanData>> supplier, int number, boolean verifyScopeVersion)
  43. throws InterruptedException, TimeoutException {
  44. return waitForTraces(supplier, number, 20, TimeUnit.SECONDS, verifyScopeVersion);
  45. }
  46. public static List<List<SpanData>> waitForTraces(
  47. Supplier<List<SpanData>> supplier,
  48. int number,
  49. long timeout,
  50. TimeUnit unit,
  51. boolean verifyScopeVersion)
  52. throws InterruptedException, TimeoutException {
  53. long startTime = System.nanoTime();
  54. List<List<SpanData>> allTraces = groupTraces(supplier.get());
  55. List<List<SpanData>> completeTraces =
  56. allTraces.stream().filter(TelemetryDataUtil::isCompleted).collect(toList());
  57. while (completeTraces.size() < number && elapsedSeconds(startTime) < unit.toSeconds(timeout)) {
  58. allTraces = groupTraces(supplier.get());
  59. completeTraces = allTraces.stream().filter(TelemetryDataUtil::isCompleted).collect(toList());
  60. Thread.sleep(10);
  61. }
  62. if (completeTraces.size() < number) {
  63. throw new TimeoutException(
  64. "Timeout waiting for "
  65. + number
  66. + " completed trace(s), found "
  67. + completeTraces.size()
  68. + " completed trace(s) and "
  69. + allTraces.size()
  70. + " total trace(s): "
  71. + allTraces);
  72. }
  73. if (verifyScopeVersion) {
  74. // TODO (trask) is there a better location for this assertion?
  75. for (List<SpanData> trace : completeTraces) {
  76. for (SpanData span : trace) {
  77. if (!span.getInstrumentationScopeInfo().getName().equals("test")) {
  78. assertThat(span.getInstrumentationScopeInfo().getVersion())
  79. .as(
  80. "Instrumentation version was empty; make sure that the instrumentation name matches the gradle module name")
  81. .isNotNull();
  82. }
  83. }
  84. }
  85. }
  86. return completeTraces;
  87. }
  88. private static long elapsedSeconds(long startTime) {
  89. return TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime);
  90. }
  91. // must be called under tracesLock
  92. private static void sortTraces(List<List<SpanData>> traces) {
  93. traces.sort(Comparator.comparingLong(TelemetryDataUtil::getMinSpanOrder));
  94. }
  95. private static long getMinSpanOrder(List<SpanData> spans) {
  96. return spans.stream().mapToLong(SpanData::getStartEpochNanos).min().orElse(0);
  97. }
  98. @SuppressWarnings("UnstableApiUsage")
  99. private static List<SpanData> sort(List<SpanData> trace) {
  100. Map<String, Node> lookup = new HashMap<>();
  101. for (SpanData span : trace) {
  102. lookup.put(span.getSpanId(), new Node(span));
  103. }
  104. for (Node node : lookup.values()) {
  105. String parentSpanId = node.span.getParentSpanId();
  106. if (SpanId.isValid(parentSpanId)) {
  107. Node parentNode = lookup.get(parentSpanId);
  108. if (parentNode != null) {
  109. parentNode.childNodes.add(node);
  110. node.root = false;
  111. }
  112. }
  113. }
  114. List<Node> rootNodes = new ArrayList<>();
  115. for (Node node : lookup.values()) {
  116. sortOneLevel(node.childNodes);
  117. if (node.root) {
  118. rootNodes.add(node);
  119. }
  120. }
  121. sortOneLevel(rootNodes);
  122. List<Node> orderedNodes = new ArrayList<>();
  123. for (Node rootNode : rootNodes) {
  124. traversePreOrder(rootNode, orderedNodes);
  125. }
  126. List<SpanData> orderedSpans = new ArrayList<>();
  127. for (Node node : orderedNodes) {
  128. orderedSpans.add(node.span);
  129. }
  130. return orderedSpans;
  131. }
  132. private static void sortOneLevel(List<Node> nodes) {
  133. nodes.sort(Comparator.comparingLong(node -> node.span.getStartEpochNanos()));
  134. }
  135. private static void traversePreOrder(Node node, List<Node> accumulator) {
  136. accumulator.add(node);
  137. for (Node child : node.childNodes) {
  138. traversePreOrder(child, accumulator);
  139. }
  140. }
  141. // trace is completed if root span is present
  142. private static boolean isCompleted(List<SpanData> trace) {
  143. for (SpanData span : trace) {
  144. if (!SpanId.isValid(span.getParentSpanId())) {
  145. return true;
  146. }
  147. if (span.getParentSpanId().equals("0000000000000456")) {
  148. // this is a special parent id that some tests use
  149. return true;
  150. }
  151. }
  152. return false;
  153. }
  154. private static class Node {
  155. private final SpanData span;
  156. private final List<Node> childNodes = new ArrayList<>();
  157. private boolean root = true;
  158. private Node(SpanData span) {
  159. this.span = span;
  160. }
  161. }
  162. private TelemetryDataUtil() {}
  163. }