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