/** * Tryies to get the span with the given {@link SpanIdent}. * * @param ident * the span identifier to search for * @return an {@link Optional} which may contain the {@link Span} with belongs to the given * {@link SpanIdent} */ private Optional<Span> getSpan(SpanIdent ident) { return spans.stream().filter(s -> s.getSpanIdent().equals(ident)).findAny(); }
/** * Returns exclusive duration of the Span contained in the given {@link InvocationTreeElement}. * Note: This method works only with {@link InvocationTreeElement} of type * {@link TreeElementType#SPAN}. * * @param element * the {@link InvocationTreeElement} to calculate the exclusive duration for * @return Returns exclusive duration. */ public static double calculateSpanExclusiveDuration(InvocationTreeElement element) { if (element == null) { return Double.NaN; } if (!element.isSpan()) { return 0D; } double childrenDuration = element.getChildren().stream().mapToDouble(InvocationTreeUtil::calculateSpanParentRelativeDuration).sum(); double exclusiveDuration = ((Span) element.getDataElement()).getDuration() - childrenDuration; return Math.max(exclusiveDuration, 0D); }
/** * Returns the full span details in form of StyledString. This method returns details if on of * these situations is matched: * * <ul> * <li>Propagation type is known for a span. * <li>Operation name exists as a tag. * <li>Method ident exists in the span. * </ul> * * @param span * {@link Span} * @param cachedDataService * Cached data service to load the method ident information from. * @return Returns {@link StyledString} if information in the span is enough or * <code>null</code> if details can not be constructed. */ public static StyledString getSpanDetailsFull(Span span, ICachedDataService cachedDataService) { return getSpanDetailsFull(span.isCaller(), span.getPropagationType(), span.getMethodIdent(), span.getTags(), cachedDataService); }
/** * Returns the short span details in form of StyledString. This method returns details if on of * these situations is matched: * * <ul> * <li>Propagation type is known for a span. * <li>Operation name exists as a tag. * <li>Method ident exists in the span. * </ul> * * @param span * {@link Span} * @param cachedDataService * Cached data service to load the method ident information from. * @return Returns {@link StyledString} if information in the span is enough or * <code>null</code> if details can not be constructed. */ public static StyledString getSpanDetailsShort(Span span, ICachedDataService cachedDataService) { return getSpanDetailsShort(span.getPropagationType(), span.getMethodIdent(), span.getTags(), cachedDataService); }
/** * Returns span image based on propagation and reference type. * * @param span * Span * @param resourceManager * resource manager * @return image */ public static Image getSpanImage(Span span, ResourceManager resourceManager) { Image propagationImage = getPropagationImage(span.getPropagationType()); if (!span.isCaller() && !References.FOLLOWS_FROM.equals(span.getReferenceType())) { return propagationImage; } else { return getOverlayedImage(propagationImage, resourceManager, 1, getReferenceImage(span.getReferenceType())); } }
/** * Returns the styled text for a specific column. * * @param data * The data object to extract the information from. * @param enumId * The enumeration ID. * @return The styled string containing the information from the data object. */ private StyledString getStyledTextForColumn(Span data, Column enumId) { switch (enumId) { case TIME: return new StyledString(NumberFormatter.formatTimeWithMillis(data.getTimeStamp())); case DURATION: return new StyledString(NumberFormatter.formatDouble(data.getDuration(), timeDecimalPlaces)); case PROPAGATION: return TextFormatter.getPropagationStyled(data.getPropagationType()); case DETAILS: return TextFormatter.getSpanDetailsShort(data, cachedDataService); case ORIGIN: return TextFormatter.getSpanOriginStyled(data, cachedDataService.getPlatformIdentForId(data.getPlatformIdent())); case TRACE_ID: return new StyledString(Long.toHexString(data.getSpanIdent().getTraceId())); default: return new StyledString("error"); } } }
if (span.isCaller()) { table.addContentRow("Client:", null, new DetailsCellContent[] { new DetailsCellContent(InspectIT.getDefault().getImage(InspectITImages.IMG_CHECKMARK), "Yes") }); new DetailsCellContent(ImageFormatter.getPropagationImage(span.getPropagationType()), TextFormatter.getPropagationStyled(span.getPropagationType()).toString()) }); if (null != span.getReferenceType()) { table.addContentRow("Reference:", null, new DetailsCellContent[] { new DetailsCellContent(ImageFormatter.getReferenceImage(span.getReferenceType()), TextFormatter.getDescriptiveReference(span)) }); table.addContentRow("Duration (ms):", null, new DetailsCellContent[] { new DetailsCellContent(NumberFormatter.formatDouble(span.getDuration(), 3)) }); if (MapUtils.isNotEmpty(span.getTags())) { List<String[]> rows = new ArrayList<>(); for (Map.Entry<String, String> entry : span.getTags().entrySet()) { rows.add(new String[] { entry.getKey(), entry.getValue() });
/** * Resolves a span element. * * @param parent * a {@link InvocationTreeElement} from type {@link TreeElementType#SPAN}. */ private void resolveSpan(InvocationTreeElement parent) { Span currentSpan = (Span) parent.getDataElement(); if (!currentSpan.isCaller()) { // server span - expect invocation sequence for (InvocationSequenceData data : invocationSequences) { if (InvocationSequenceDataHelper.hasSpanIdent(data) && data.getSpanIdent().equals(currentSpan.getSpanIdent())) { InvocationTreeElement child = createTreeElement(data, parent); resolveInvocationSequence(child); } } } for (Span span : spans) { // if span is already in the tree, skip it if (isSpanInTree(span.getSpanIdent())) { continue; } if (span.getParentSpanId() == currentSpan.getSpanIdent().getId()) { InvocationTreeElement child = createTreeElement(span, parent); resolveSpan(child); } } }
Span span = optionalSpan.get(); InvocationTreeElement child = null; if (span.isCaller()) { if ((parent.getParent() == null) || !parent.getParent().isSpan() || (((Span) parent.getParent().getDataElement()).getSpanIdent().getId() != span.getSpanIdent().getId())) { child = createTreeElement(span, parent);
} else if (type == TreeElementType.SPAN) { Span span = (Span) dataElement; builder.append(span.getSpanIdent().getId()); if (!isRoot()) { builder.append(";parent="); builder.append(span.getParentSpanId());
/** * Calculates the duration starting from this invocation sequence data element. Includes span * duration as last resource if the span ident exists on the data. * * @param data * the <code>InvocationSequenceData</code> object. * @param spanService * Span service providing the additional span information if needed. * @return the duration starting from this invocation sequence data element. */ public static double calculateDuration(InvocationSequenceData data, ISpanService spanService) { if (InvocationSequenceDataHelper.hasTimerData(data)) { return data.getTimerData().getDuration(); } else if (InvocationSequenceDataHelper.hasSQLData(data)) { return data.getSqlStatementData().getDuration(); } else if ((null != spanService) && hasSpanIdent(data)) { Span span = spanService.get(data.getSpanIdent()); if ((span != null) && !References.FOLLOWS_FROM.equals(span.getReferenceType())) { return span.getDuration(); } } if (InvocationSequenceDataHelper.isRootElementInSequence(data)) { return data.getDuration(); } return -1.0d; }
/** * Loads all the data which is required for building the tree. */ private void prepare() { if (mode == Mode.SINGLE) { invocationSequences = Arrays.asList(invocationSequence); // loading all spans related to the set invocation sequence if (spanService != null) { Stream<InvocationSequenceData> invocationStream = InvocationSequenceDataHelper.asStream(invocationSequence); spans = invocationStream.map(i -> i.getSpanIdent()).filter(Objects::nonNull).map(spanService::get).filter(Objects::nonNull).collect(Collectors.toList()); } else { spans = new ArrayList<>(); } } else { if (traceId == null) { throw new IllegalStateException("A trace id have to be specified when a span tree has to be build and the mode is not set to SINGLE."); } spans = loadSpans(); if (mode == Mode.ALL) { invocationSequences = loadInvocationSequences(); } } if (mode != Mode.ONLY_SPANS_WITH_SDK) { spans.removeIf(s -> (s.getPropagationType() == null) && !s.isRoot()); } }
/** * Returns descriptive reference for a span if one is provided. * * @param span * span * @return Reference more descriptive * @see Reference more descriptive */ public static String getDescriptiveReference(Span span) { String referenceType = span.getReferenceType(); if (References.CHILD_OF.equals(referenceType)) { return "Synchronous"; } else if (References.FOLLOWS_FROM.equals(referenceType)) { return "Non-Synchronous"; } return "N/A"; }
/** * Returns the timestamp of this element. The time stamp will be gathered from the data object. * * @return the {@link Timestamp} of this element */ private Timestamp getTimeStamp() { if (dataElement instanceof Span) { return ((Span) dataElement).getTimeStamp(); } else if (dataElement instanceof InvocationSequenceData) { return ((InvocationSequenceData) dataElement).getTimeStamp(); } return null; }
/** * {@inheritDoc} */ @Override public void doubleClick(DoubleClickEvent event) { StructuredSelection selection = (StructuredSelection) event.getSelection(); if (!selection.isEmpty()) { Object clickedObject = selection.getFirstElement(); if (clickedObject instanceof Span) { if (((Span) clickedObject).getPropagationType() == null) { // Span is SDK span return; } } else if (clickedObject instanceof SpanIdent) { return; } // open trace details view IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class); Command command = commandService.getCommand(LocateHandler.COMMAND); ExecutionEvent executionEvent = handlerService.createExecutionEvent(command, new Event()); try { command.executeWithChecks(executionEvent); } catch (Exception e) { throw new RuntimeException(e); } } }
return TextFormatter.getSpanDetailsShort(span, cachedDataService); case PROPAGATION: return TextFormatter.getPropagationStyled(span.getPropagationType()); case ORIGIN: return TextFormatter.getSpanOriginStyled(span, cachedDataService.getPlatformIdentForId(span.getPlatformIdent())); case TIME: return new StyledString(NumberFormatter.formatTimeWithMillis(span.getTimeStamp())); case DURATION: StyledString durationString = new StyledString(NumberFormatter.formatDouble(span.getDuration(), timeDecimalPlaces)); if (InvocationTreeUtil.isConsideredAsync(ite)) { durationString.append(TextFormatter.getWarningSign());
/** * Returns whether the given element is considered as asynchronous. It will return always * <code>false</code> if the type is not {@link TreeElementType#SPAN}. * * @param element * the element to check * @return <code>true</code> if the element is considered as asynchronous */ public static boolean isConsideredAsync(InvocationTreeElement element) { if (!element.isSpan()) { return false; } return References.FOLLOWS_FROM.equals(((Span) element.getDataElement()).getReferenceType()); } }
@Override public Span load(SpanIdent ident) throws Exception { // collect all from same trace as we expect them to be asked in same point of time // this way we don't do round-trips to only get one span // example: // - you ask for span id 1 and trace id 2 // - I load all spans with trace id 2, which might be few of them // - the spans not having id 1 I add to the cache manually // - i "load" the one with id 1, which will effectively add it also to the cache.. Collection<? extends Span> spans = service.getSpans(ident.getTraceId()); // add other spans from the trace to the cache and returned the one we tried to load if (CollectionUtils.isNotEmpty(spans)) { Span value = null; for (Span span : spans) { if (Objects.equals(ident, span.getSpanIdent())) { value = span; } else { cache.put(span.getSpanIdent(), span); } } // return asked one if (null != value) { return value; } } // if we can not locate, throw exception as per loading cache api throw new Exception("Span with ident " + ident + " can not be found."); } });
/** * Returns the relative duration of the span contained in the given element. If the element is * an asynchronous span, the method will return <code>0</code> because we cannot specify a * relative duration of an asynchronous span relative to its parent. * <p> * If the element is not of type {@link TreeElementType#SPAN} the sum of all direct child spans * are calculated. This means that when the child spans are not direct children in the tree but * are connected via several invocation sequence to the parent span we handle them as well. * * @param element * the element to calculate the duration * @return the relative duration of the span or the sum of all direct child spans */ private static double calculateSpanParentRelativeDuration(InvocationTreeElement element) { if (element.isSpan()) { if (InvocationTreeUtil.isConsideredAsync(element)) { return 0D; } else { return ((Span) element.getDataElement()).getDuration(); } } // This case is only necessary in full trees which do not consist of span elements only but // multiple spans are connected together over several invocation sequences. In a tree // consisting only of span elements, this case can never happen. double nestedDuration = element.getChildren().stream().mapToDouble(InvocationTreeUtil::calculateSpanParentRelativeDuration).sum(); return nestedDuration; }
/** * {@inheritDoc} */ @Override public Collection<? extends Span> getRootSpans(int limit, Date fromDate, Date toDate, ResultComparator<AbstractSpan> resultComparator) { // call service Collection<? extends Span> spans = service.getRootSpans(limit, fromDate, toDate, resultComparator); // cache results for (Span span : spans) { cache.put(span.getSpanIdent(), span); } // then return return spans; }