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);
}
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;
}
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()));
}
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));
}
}
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;
}
Aggregations