Search in sources :

Example 1 with ApiTraceGraph

use of org.hypertrace.traceenricher.trace.util.ApiTraceGraph in project hypertrace-ingester by hypertrace.

the class TraceStatsEnricher method enrichTrace.

@Override
public void enrichTrace(StructuredTrace trace) {
    ApiTraceGraph apiTraceGraph = ApiTraceGraphBuilder.buildGraph(trace);
    if (apiTraceGraph.getApiNodeList().isEmpty()) {
        return;
    }
    Event firstNodeHeadSpan = apiTraceGraph.getApiNodeList().get(0).getHeadEvent();
    if (firstNodeHeadSpan == null) {
        return;
    }
    addHeadSpanIdTraceAttribute(trace, firstNodeHeadSpan);
    addUniqueApiNodesCountTraceAttribute(apiTraceGraph.getApiNodeList(), trace);
}
Also used : Event(org.hypertrace.core.datamodel.Event) ApiTraceGraph(org.hypertrace.traceenricher.trace.util.ApiTraceGraph)

Example 2 with ApiTraceGraph

use of org.hypertrace.traceenricher.trace.util.ApiTraceGraph 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 ApiTraceGraph

use of org.hypertrace.traceenricher.trace.util.ApiTraceGraph in project hypertrace-ingester by hypertrace.

the class ExitCallsEnricherTest method verifyComputeApiExitInfo_HotrodTrace.

private void verifyComputeApiExitInfo_HotrodTrace(StructuredTrace trace, ExitCallsEnricher exitCallsEnricher) {
    ApiTraceGraph apiTraceGraph = new ApiTraceGraph(trace);
    // this trace has 12 api nodes
    // api edges
    // 0 -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    // backend exit
    // 1 -> to redis 13 exit calls
    // 2 -> to mysql 1 exit call
    // for events parts of api_node 0, there should 12 exit calls
    // for events parts of api_node 1, there should be 13 exit calls
    // for events parts of api_node 2, there should be 1 exit calls
    Map<Integer, Integer> apiNodeToExitCallCount = Map.of(0, 12, 1, 13, 2, 1);
    Map<Integer, Map<String, String>> apiNodeToExitServices = Map.of(0, Map.of("route", "10", "driver", "1", "customer", "1"), 1, Map.of("redis", "11", "unknown-backend", "2"), 2, Map.of("unknown-backend", "1"));
    Map<ByteBuffer, Integer> eventToApiNodeIndex = buildEventIdToApiNode(apiTraceGraph);
    trace.getEventList().forEach(e -> {
        Integer apiNodeIndex = eventToApiNodeIndex.get(e.getEventId());
        if (null != apiNodeIndex) {
            assertEquals(apiNodeToExitServices.getOrDefault(apiNodeIndex, Maps.newHashMap()), SpanAttributeUtils.getAttributeValue(e, EnrichedSpanConstants.API_CALLEE_NAME_COUNT_ATTRIBUTE).getValueMap());
            assertEquals(apiNodeToExitCallCount.getOrDefault(apiNodeIndex, 0), Integer.parseInt(SpanAttributeUtils.getStringAttribute(e, EnrichedSpanConstants.API_EXIT_CALLS_ATTRIBUTE)));
        }
    });
    Map<ByteBuffer, ApiExitCallInfo> eventToApiExitInfo = exitCallsEnricher.computeApiExitCallCount(trace);
    // verify exit call count per service per api_trace
    // this trace has 4 services
    // frontend service has 1 api_entry span and that api_node has 12 exit calls [drive: 1,
    // customer: 1, route: 10]
    List<Event> events = getApiEntryEventsForService(trace, "frontend");
    assertEquals(1, events.size());
    assertEquals(12, eventToApiExitInfo.get(events.get(0).getEventId()).getExitCallCount());
    // customer service has 1 api_entry span and that api_node has 1 exit call to mysql
    events = getApiEntryEventsForService(trace, "customer");
    assertEquals(1, events.size());
    assertEquals(1, eventToApiExitInfo.get(events.get(0).getEventId()).getExitCallCount());
    // driver service has 1 api_entry span and that api_node has 13 exit call redis
    events = getApiEntryEventsForService(trace, "driver");
    assertEquals(1, events.size());
    assertEquals(13, eventToApiExitInfo.get(events.get(0).getEventId()).getExitCallCount());
    // route service has 10 api_entry span and all of them have 0 exit calls
    events = getApiEntryEventsForService(trace, "route");
    assertEquals(10, events.size());
    events.forEach(v -> assertEquals(0, eventToApiExitInfo.get(v.getEventId()).getExitCallCount()));
}
Also used : Event(org.hypertrace.core.datamodel.Event) ApiExitCallInfo(org.hypertrace.traceenricher.enrichment.enrichers.ExitCallsEnricher.ApiExitCallInfo) ApiTraceGraph(org.hypertrace.traceenricher.trace.util.ApiTraceGraph) HashMap(java.util.HashMap) Map(java.util.Map) ByteBuffer(java.nio.ByteBuffer)

Example 4 with ApiTraceGraph

use of org.hypertrace.traceenricher.trace.util.ApiTraceGraph in project hypertrace-ingester by hypertrace.

the class ErrorsAndExceptionsEnricher method enrichTrace.

@Override
public void enrichTrace(StructuredTrace trace) {
    // TODO: There could be other cases where the client which is initiating this transaction
    // has errored out but the entry span in transaction might be fine (server responded but
    // client couldn't process it). Those cases should be handled in future.
    ApiTraceGraph apiTraceGraph = ApiTraceGraphBuilder.buildGraph(trace);
    for (ApiNode<Event> apiNode : apiTraceGraph.getApiNodeList()) {
        Optional<Event> entryEvent = apiNode.getEntryApiBoundaryEvent();
        int apiTraceErrorCount = (int) apiNode.getEvents().stream().filter(this::findIfEventHasError).count();
        if (entryEvent.isPresent()) {
            addEnrichedAttribute(entryEvent.get(), EnrichedSpanConstants.API_TRACE_ERROR_SPAN_COUNT_ATTRIBUTE, AttributeValueCreator.create(apiTraceErrorCount));
        }
    }
    // Find the earliest Event from this trace and check if that's an ENTRY type.
    Event earliestEvent = getEarliestEvent(trace);
    if (EnrichedSpanUtils.isEntrySpan(earliestEvent)) {
        LOG.debug("Found earliest Event in this trace. It is {}", earliestEvent);
        Metrics metrics = earliestEvent.getMetrics();
        if (metrics != null && metrics.getMetricMap() != null && metrics.getMetricMap().containsKey(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_ERROR_COUNT))) {
            trace.getAttributes().getAttributeMap().put(EnrichedSpanConstants.getValue(CommonAttribute.COMMON_ATTRIBUTE_TRANSACTION_HAS_ERROR), AttributeValueCreator.create(true));
        }
    }
    // Count the no. of errors and exceptions in this trace overall. These need not have caused
    // the trace to error out but it's a good metric to track anyways.
    // Trace is considered to have an error if there is an error in the entry span only.
    double exceptionCount = 0.0d;
    double errorCount = 0.0d;
    for (Event event : trace.getEventList()) {
        Map<String, MetricValue> metricMap = event.getMetrics().getMetricMap();
        if (metricMap != null && metricMap.containsKey(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_ERROR_COUNT))) {
            errorCount += metricMap.get(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_ERROR_COUNT)).getValue();
        }
        if (metricMap != null && metricMap.containsKey(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_EXCEPTION_COUNT))) {
            exceptionCount += metricMap.get(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_EXCEPTION_COUNT)).getValue();
        }
    }
    if (exceptionCount > 0.0d) {
        trace.getMetrics().getMetricMap().put(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_TOTAL_SPANS_WITH_EXCEPTIONS), MetricValueCreator.create(exceptionCount));
    }
    if (errorCount > 0.0d) {
        trace.getMetrics().getMetricMap().put(EnrichedSpanConstants.getValue(ErrorMetrics.ERROR_METRICS_TOTAL_SPANS_WITH_ERRORS), MetricValueCreator.create(errorCount));
    }
}
Also used : Metrics(org.hypertrace.core.datamodel.Metrics) ErrorMetrics(org.hypertrace.traceenricher.enrichedspan.constants.v1.ErrorMetrics) MetricValue(org.hypertrace.core.datamodel.MetricValue) Event(org.hypertrace.core.datamodel.Event) ApiTraceGraph(org.hypertrace.traceenricher.trace.util.ApiTraceGraph)

Example 5 with ApiTraceGraph

use of org.hypertrace.traceenricher.trace.util.ApiTraceGraph in project hypertrace-ingester by hypertrace.

the class ExitCallsEnricher method computeApiExitCallCount.

/**
 * An api_node represents an api call in the trace It consists of api_entry span and multiple
 * api_exit and internal spans
 *
 * <p>This method computes the count & breakup of exit calls for any given api (identified by
 * api_entry_span) This count is a composition of 2 things
 *
 * <ul>
 *   <li>1. link between api_exit_span in api_node to api_entry_span (child of api_exit_span) in
 *       another api_node
 *   <li>2. exit calls to backend from api_exit_span in api_node
 * </ul>
 */
Map<ByteBuffer, ApiExitCallInfo> computeApiExitCallCount(StructuredTrace trace) {
    ApiTraceGraph apiTraceGraph = ApiTraceGraphBuilder.buildGraph(trace);
    // event -> api exit call count for the corresponding api_node
    Map<ByteBuffer, ApiExitCallInfo> eventToExitInfo = Maps.newHashMap();
    int totalTraceExitCallCount = 0;
    for (ApiNode<Event> apiNode : apiTraceGraph.getApiNodeList()) {
        List<Event> backendExitEvents = apiTraceGraph.getExitBoundaryEventsWithNoOutboundEdgeForApiNode(apiNode);
        List<ApiNodeEventEdge> edges = apiTraceGraph.getOutboundEdgesForApiNode(apiNode);
        int totalExitCallCount = backendExitEvents.size() + edges.size();
        ApiExitCallInfo apiExitCallInfo = new ApiExitCallInfo().withExitCallCount(totalExitCallCount);
        totalTraceExitCallCount += totalExitCallCount;
        edges.forEach(edge -> apiExitCallInfo.handleApiNodeEdge(trace, edge));
        backendExitEvents.forEach(apiExitCallInfo::handleBackend);
        apiNode.getEvents().forEach(e -> eventToExitInfo.put(e.getEventId(), apiExitCallInfo));
    }
    addApiExitCallsCountTraceAttribute(trace, totalTraceExitCallCount);
    return eventToExitInfo;
}
Also used : ApiNodeEventEdge(org.hypertrace.core.datamodel.ApiNodeEventEdge) Event(org.hypertrace.core.datamodel.Event) ApiTraceGraph(org.hypertrace.traceenricher.trace.util.ApiTraceGraph) ByteBuffer(java.nio.ByteBuffer)

Aggregations

ApiTraceGraph (org.hypertrace.traceenricher.trace.util.ApiTraceGraph)8 Event (org.hypertrace.core.datamodel.Event)5 ByteBuffer (java.nio.ByteBuffer)2 ArrayList (java.util.ArrayList)2 MetricValue (org.hypertrace.core.datamodel.MetricValue)2 StructuredTrace (org.hypertrace.core.datamodel.StructuredTrace)2 ServiceCallView (org.hypertrace.viewgenerator.api.ServiceCallView)2 Test (org.junit.jupiter.api.Test)2 HashMap (java.util.HashMap)1 Map (java.util.Map)1 ApiNodeEventEdge (org.hypertrace.core.datamodel.ApiNodeEventEdge)1 Metrics (org.hypertrace.core.datamodel.Metrics)1 ApiNode (org.hypertrace.core.datamodel.shared.ApiNode)1 ErrorMetrics (org.hypertrace.traceenricher.enrichedspan.constants.v1.ErrorMetrics)1 Protocol (org.hypertrace.traceenricher.enrichedspan.constants.v1.Protocol)1 ApiExitCallInfo (org.hypertrace.traceenricher.enrichment.enrichers.ExitCallsEnricher.ApiExitCallInfo)1 RawServiceView (org.hypertrace.viewgenerator.api.RawServiceView)1 TraceState (org.hypertrace.viewgenerator.generators.ViewGeneratorState.TraceState)1