Search in sources :

Example 1 with ApiNode

use of org.hypertrace.core.datamodel.shared.ApiNode in project hypertrace-ingester by hypertrace.

the class ApiTraceGraph method buildApiNodeEdges.

private void buildApiNodeEdges(StructuredTraceGraph graph) {
    // 4. connect the exit boundary and entry boundary of different api node with an edge
    for (ApiNode<Event> apiNode : apiNodeList) {
        // exit boundary events of api node
        List<Event> exitBoundaryEvents = apiNode.getExitApiBoundaryEvents();
        for (Event exitBoundaryEvent : exitBoundaryEvents) {
            List<Event> exitBoundaryEventChildren = graph.getChildrenEvents(exitBoundaryEvent);
            if (exitBoundaryEventChildren != null) {
                for (Event exitBoundaryEventChild : exitBoundaryEventChildren) {
                    // always!
                    if (EnrichedSpanUtils.isEntryApiBoundary(exitBoundaryEventChild)) {
                        // get the api node exit boundary event is connecting to
                        ApiNode<Event> destinationApiNode = entryApiBoundaryEventIdToApiNode.get(exitBoundaryEventChild.getEventId());
                        Optional<ApiNodeEventEdge> edgeBetweenApiNodes = createEdgeBetweenApiNodes(apiNode, destinationApiNode, exitBoundaryEvent, exitBoundaryEventChild);
                        edgeBetweenApiNodes.ifPresent(edge -> {
                            apiNodeEventEdgeList.add(edge);
                            apiExitBoundaryEventIdxWithOutgoingEdge.add(edge.getSrcEventIndex());
                            apiEntryBoundaryEventIdxWithIncomingEdge.add(edge.getTgtEventIndex());
                            apiNodeIdxToEdges.computeIfAbsent(apiNodeHeadEventIdToIndex.get(apiNode.getHeadEvent().getEventId()), v -> Sets.newHashSet()).add(apiNodeEventEdgeList.size() - 1);
                        });
                    } else {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Exit boundary event with eventId: {}, eventName: {}, serviceName: {}," + " can only have entry boundary event as child. Non-entry child:" + " childEventId: {}, childEventName: {}, childServiceName: {}." + " traceId for events: {}", HexUtils.getHex(exitBoundaryEvent.getEventId()), exitBoundaryEvent.getEventName(), exitBoundaryEvent.getServiceName(), HexUtils.getHex(exitBoundaryEventChild.getEventId()), exitBoundaryEventChild.getEventName(), exitBoundaryEventChild.getServiceName(), HexUtils.getHex(trace.getTraceId()));
                        }
                    }
                }
            }
        }
        // Sometimes an exit span might be missing for services like Istio, Kong.
        // Only Entry spans will be populated for these services,
        // an edge must be created between these services as well.
        // 1. get entry boundary event from an api node.
        // 2. find all the children of entry boundary event, which will be entry boundary nodes of
        // different api nodes
        // 3. Check both parent span and child belong to different service as well.
        // 4. connect the entry boundary of different api nodes with an edge.
        Optional<Event> entryBoundaryEvent = apiNode.getEntryApiBoundaryEvent();
        if (entryBoundaryEvent.isPresent()) {
            List<Event> children = graph.getChildrenEvents(entryBoundaryEvent.get());
            if (children != null) {
                for (Event child : children) {
                    // which can happen if exit span missing and both belongs to different services.
                    if (EnrichedSpanUtils.isEntryApiBoundary(child) && EnrichedSpanUtils.areBothSpansFromDifferentService(child, entryBoundaryEvent.get())) {
                        ApiNode<Event> destinationApiNode = entryApiBoundaryEventIdToApiNode.get(child);
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Edge between entry boundaries servicename: {} span: {}  to servicename: {} span: {} of trace {}", entryBoundaryEvent.get().getServiceName(), HexUtils.getHex(entryBoundaryEvent.get().getEventId()), child.getServiceName(), HexUtils.getHex(child.getEventId()), HexUtils.getHex(trace.getTraceId()));
                        }
                        Optional<ApiNodeEventEdge> edgeBetweenApiNodes = createEdgeBetweenApiNodes(apiNode, destinationApiNode, entryBoundaryEvent.get(), child);
                        edgeBetweenApiNodes.ifPresent(apiNodeEventEdgeList::add);
                    }
                }
            }
        }
    }
}
Also used : EnrichedSpanUtils(org.hypertrace.traceenricher.enrichedspan.constants.utils.EnrichedSpanUtils) AttributeValue(org.hypertrace.traceenricher.enrichedspan.constants.v1.AttributeValue) AvroBuilderCache.fastNewBuilder(org.hypertrace.core.datamodel.shared.AvroBuilderCache.fastNewBuilder) LoggerFactory(org.slf4j.LoggerFactory) HashBasedTable(com.google.common.collect.HashBasedTable) Edge(org.hypertrace.core.datamodel.Edge) StringUtils(org.apache.commons.lang3.StringUtils) ByteBuffer(java.nio.ByteBuffer) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) StructuredTraceGraph(org.hypertrace.core.datamodel.shared.StructuredTraceGraph) ApiNode(org.hypertrace.core.datamodel.shared.ApiNode) Lists(com.google.common.collect.Lists) Map(java.util.Map) EnrichedSpanConstants(org.hypertrace.traceenricher.enrichedspan.constants.EnrichedSpanConstants) HexUtils(org.hypertrace.core.datamodel.shared.HexUtils) LinkedList(java.util.LinkedList) StructuredTrace(org.hypertrace.core.datamodel.StructuredTrace) Logger(org.slf4j.Logger) ApiNodeEventEdge(org.hypertrace.core.datamodel.ApiNodeEventEdge) Event(org.hypertrace.core.datamodel.Event) Set(java.util.Set) Maps(com.google.common.collect.Maps) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) List(java.util.List) Optional(java.util.Optional) Queue(java.util.Queue) Table(com.google.common.collect.Table) Collections(java.util.Collections) ApiNodeEventEdge(org.hypertrace.core.datamodel.ApiNodeEventEdge) Event(org.hypertrace.core.datamodel.Event)

Example 2 with ApiNode

use of org.hypertrace.core.datamodel.shared.ApiNode in project hypertrace-ingester by hypertrace.

the class RawServiceViewGenerator method generateView.

@Override
List<RawServiceView> generateView(StructuredTrace structuredTrace, Map<String, Entity> entityMap, Map<ByteBuffer, Event> eventMap, Map<ByteBuffer, List<ByteBuffer>> parentToChildrenEventIds, Map<ByteBuffer, ByteBuffer> childToParentEventIds) {
    List<RawServiceView> list = new ArrayList<>();
    // Construct ApiTraceGraph and look at all the head spans within each ApiNode
    ApiTraceGraph apiTraceGraph = ViewGeneratorState.getApiTraceGraph(structuredTrace);
    List<ApiNode<Event>> apiNodes = apiTraceGraph.getApiNodeList();
    for (ApiNode<Event> apiNode : apiNodes) {
        Event event = apiNode.getHeadEvent();
        if (EnrichedSpanUtils.containsServiceId(event)) {
            RawServiceView.Builder builder = fastNewBuilder(RawServiceView.Builder.class);
            builder.setTenantId(structuredTrace.getCustomerId());
            builder.setTraceId(structuredTrace.getTraceId());
            builder.setSpanId(event.getEventId());
            builder.setParentSpanId(childToParentEventIds.get(event.getEventId()));
            builder.setServiceName(EnrichedSpanUtils.getServiceName(event));
            builder.setServiceId(EnrichedSpanUtils.getServiceId(event));
            builder.setApiName(EnrichedSpanUtils.getApiName(event));
            builder.setApiId(EnrichedSpanUtils.getApiId(event));
            builder.setApiDiscoveryState(EnrichedSpanUtils.getApiDiscoveryState(event));
            builder.setStartTimeMillis(event.getStartTimeMillis());
            builder.setEndTimeMillis(event.getEndTimeMillis());
            builder.setDurationMillis(event.getEndTimeMillis() - event.getStartTimeMillis());
            builder.setTransactionName(getTransactionName(structuredTrace));
            String spanType = EnrichedSpanUtils.getSpanType(event);
            if (spanType != null) {
                builder.setSpanKind(spanType);
            }
            Protocol protocol = EnrichedSpanUtils.getProtocol(event);
            if (protocol == null || protocol == Protocol.PROTOCOL_UNSPECIFIED) {
                /* In the view, we want to replace unknown with empty string instead
           * for better representation in the UI and easier to filter */
                builder.setProtocolName(EMPTY_STRING);
            } else {
                builder.setProtocolName(protocol.name());
            }
            builder.setStatusCode(EnrichedSpanUtils.getStatusCode(event));
            // If this is an API entry boundary span, copy the error count from the event to the view
            // because we want only API or service errors to be present in the view.
            MetricValue errorMetric = event.getMetrics().getMetricMap().get(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_ERROR_COUNT));
            if (errorMetric != null && errorMetric.getValue() > 0.0d) {
                builder.setErrorCount((int) errorMetric.getValue().doubleValue());
            }
            // Copy the exception count metric to view directly.
            MetricValue exceptionMetric = event.getMetrics().getMetricMap().get(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_EXCEPTION_COUNT));
            if (exceptionMetric != null && exceptionMetric.getValue() > 0.0d) {
                builder.setExceptionCount((int) exceptionMetric.getValue().doubleValue());
            }
            if (EnrichedSpanUtils.isEntrySpan(event)) {
                builder.setNumCalls(1);
            }
            builder.setSpaceIds(EnrichedSpanUtils.getSpaceIds(event));
            list.add(builder.build());
        }
    }
    return list;
}
Also used : MetricValue(org.hypertrace.core.datamodel.MetricValue) ArrayList(java.util.ArrayList) Event(org.hypertrace.core.datamodel.Event) ApiTraceGraph(org.hypertrace.traceenricher.trace.util.ApiTraceGraph) ApiNode(org.hypertrace.core.datamodel.shared.ApiNode) Protocol(org.hypertrace.traceenricher.enrichedspan.constants.v1.Protocol) RawServiceView(org.hypertrace.viewgenerator.api.RawServiceView)

Example 3 with ApiNode

use of org.hypertrace.core.datamodel.shared.ApiNode in project hypertrace-ingester by hypertrace.

the class ApiTraceGraph method buildApiNode.

/**
 * Traverse events starting from the {@code rootEvent} and find out all the api boundary events it
 * leads to
 */
private ApiNode<Event> buildApiNode(StructuredTraceGraph graph, Event rootEvent) {
    List<Event> events = new ArrayList<>();
    events.add(rootEvent);
    List<Event> exitApiBoundaryEvents = new ArrayList<>();
    if (EnrichedSpanUtils.isExitApiBoundary(rootEvent)) {
        exitApiBoundaryEvents.add(rootEvent);
    }
    Queue<Event> q = new LinkedList<>();
    q.add(rootEvent);
    while (!q.isEmpty()) {
        Event e = q.remove();
        // This is the main logic of filtering out events which lies in an API boundary.
        // Rest of the code is just filtering out entities and edges based on the events in API
        // boundary
        // get all the children of `e`
        // 1. if there is a child which is exit boundary, we have hit a corresponding exit boundary.
        // We should not process the children of exit boundary span
        // 2. if the child is an entry boundary, we have a new boundary. Ignore this child
        // 3. if the children of `e` is null, add `e` to the api boundary
        List<Event> children = graph.getChildrenEvents(e);
        if (children != null) {
            for (Event child : children) {
                if (EnrichedSpanUtils.isExitApiBoundary(child)) {
                    events.add(child);
                    exitApiBoundaryEvents.add(child);
                } else if (EnrichedSpanUtils.isEntryApiBoundary(child)) {
                // a new api boundary. don't do anything
                } else {
                    q.add(child);
                    // an intermediate event. intermediate events are inside API boundary
                    events.add(child);
                }
            }
        }
    }
    return new ApiNode<>(rootEvent, events, (EnrichedSpanUtils.isEntryApiBoundary(rootEvent) ? rootEvent : null), exitApiBoundaryEvents);
}
Also used : ArrayList(java.util.ArrayList) Event(org.hypertrace.core.datamodel.Event) ApiNode(org.hypertrace.core.datamodel.shared.ApiNode) LinkedList(java.util.LinkedList)

Example 4 with ApiNode

use of org.hypertrace.core.datamodel.shared.ApiNode in project hypertrace-ingester by hypertrace.

the class EndpointEnricher method enrichTrace.

/**
 * All the spans within the same API Trace graph gets the same API id as their representative span
 * i.e the entry boundary span
 */
@Override
public void enrichTrace(StructuredTrace trace) {
    List<ApiNode<Event>> apiNodes = ApiTraceGraphBuilder.buildGraph(trace).getApiNodeList();
    for (ApiNode<Event> apiNode : apiNodes) {
        Optional<Event> optionalEvent = apiNode.getEntryApiBoundaryEvent();
        if (optionalEvent.isEmpty()) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Skipping enrichment, since api node has no entry api boundary event. ApiNode exit event ids {}", apiNode.getExitApiBoundaryEvents().stream().map(event -> HexUtils.getHex(event.getEventId())).collect(Collectors.toList()));
            }
            continue;
        }
        Event entryApiBoundaryEvent = optionalEvent.get();
        Optional<String> apiId = Optional.ofNullable(EnrichedSpanUtils.getApiId(entryApiBoundaryEvent));
        Optional<String> apiPattern = Optional.ofNullable(EnrichedSpanUtils.getApiPattern(entryApiBoundaryEvent));
        Optional<String> apiName = Optional.ofNullable(EnrichedSpanUtils.getApiName(entryApiBoundaryEvent));
        if (apiId.isEmpty() || apiPattern.isEmpty() || apiName.isEmpty()) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Entry API boundary event should have non null apiId, apiPattern and apiName:" + " traceId: {}, eventId: {}, eventName: {}, serviceName: {}, apiId: {}, apiPattern: {}," + " apiName: {}", HexUtils.getHex(trace.getTraceId()), HexUtils.getHex(entryApiBoundaryEvent.getEventId()), entryApiBoundaryEvent.getEventName(), entryApiBoundaryEvent.getServiceName(), apiId, apiPattern, apiName);
            }
        } else {
            List<Event> events = apiNode.getEvents();
            for (Event event : events) {
                addEnrichedAttributeIfNotNull(event, API_ID_ATTR_NAME, apiId.get());
                addEnrichedAttributeIfNotNull(event, API_URL_PATTERN_ATTR_NAME, apiPattern.get());
                addEnrichedAttributeIfNotNull(event, API_NAME_ATTR_NAME, apiName.get());
            }
        }
    }
}
Also used : Event(org.hypertrace.core.datamodel.Event) ApiNode(org.hypertrace.core.datamodel.shared.ApiNode)

Aggregations

Event (org.hypertrace.core.datamodel.Event)4 ApiNode (org.hypertrace.core.datamodel.shared.ApiNode)4 ArrayList (java.util.ArrayList)3 LinkedList (java.util.LinkedList)2 HashBasedTable (com.google.common.collect.HashBasedTable)1 Lists (com.google.common.collect.Lists)1 Maps (com.google.common.collect.Maps)1 Sets (com.google.common.collect.Sets)1 Table (com.google.common.collect.Table)1 ByteBuffer (java.nio.ByteBuffer)1 Collections (java.util.Collections)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Map (java.util.Map)1 Optional (java.util.Optional)1 Queue (java.util.Queue)1 Set (java.util.Set)1 Collectors (java.util.stream.Collectors)1 StringUtils (org.apache.commons.lang3.StringUtils)1 ApiNodeEventEdge (org.hypertrace.core.datamodel.ApiNodeEventEdge)1