This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch logic-endpoint in repository https://gitbox.apache.org/repos/asf/skywalking.git
commit 2c0dc8dc9e90ea8e2e078299a3e36efd40209627 Author: Wu Sheng <[email protected]> AuthorDate: Mon Jun 29 15:55:57 2020 +0800 Support `logic-endpoint` series tag(s). --- .../oap/server/core/source/RequestType.java | 12 +- .../receiver/trace/provider/parser/SpanTags.java | 22 + .../listener/MultiScopesAnalysisListener.java | 64 ++- .../provider/parser/listener/MockReceiver.java} | 31 +- .../listener/MultiScopesAnalysisListenerTest.java | 463 +++++++++++++++++++++ 5 files changed, 584 insertions(+), 8 deletions(-) diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/RequestType.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/RequestType.java index 9c47f8c..3298da0 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/RequestType.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/source/RequestType.java @@ -18,6 +18,16 @@ package org.apache.skywalking.oap.server.core.source; +/** + * RPC request type. + */ public enum RequestType { - DATABASE, HTTP, RPC, gRPC + DATABASE, + HTTP, + RPC, + gRPC, + /** + * Logic request only. + */ + LOGIC } diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/SpanTags.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/SpanTags.java index 4f2de97..a605306 100644 --- a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/SpanTags.java +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/SpanTags.java @@ -18,10 +18,32 @@ package org.apache.skywalking.oap.server.receiver.trace.provider.parser; +/** + * Reserved keys of the span. The backend analysis the metrics according the existed tags. + */ public class SpanTags { public static final String STATUS_CODE = "status_code"; public static final String DB_STATEMENT = "db.statement"; public static final String DB_TYPE = "db.type"; + + /** + * Tag, x-le(extension logic endpoint) series tag. Value is JSON format. + * <pre> + * { + * "name": "GraphQL-service", + * "latency": 100, + * "status": true + * } + * </pre> + * + * Also, could use value to indicate this local span is representing a logic endpoint. + * <pre> + * { + * "logic-span": true + * } + * </pre> + */ + public static final String LOGIC_ENDPOINT = "x-le"; } diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MultiScopesAnalysisListener.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MultiScopesAnalysisListener.java index 2aa2923..342d264 100755 --- a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MultiScopesAnalysisListener.java +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MultiScopesAnalysisListener.java @@ -18,6 +18,8 @@ package org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; @@ -27,6 +29,7 @@ import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject; import org.apache.skywalking.apm.network.language.agent.v3.SegmentReference; import org.apache.skywalking.apm.network.language.agent.v3.SpanLayer; import org.apache.skywalking.apm.network.language.agent.v3.SpanObject; +import org.apache.skywalking.apm.network.language.agent.v3.SpanType; import org.apache.skywalking.apm.util.StringUtil; import org.apache.skywalking.oap.server.core.Const; import org.apache.skywalking.oap.server.core.CoreModule; @@ -47,6 +50,8 @@ import org.apache.skywalking.oap.server.receiver.trace.provider.DBLatencyThresho import org.apache.skywalking.oap.server.receiver.trace.provider.TraceServiceModuleConfig; import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SpanTags; +import static org.apache.skywalking.oap.server.receiver.trace.provider.parser.SpanTags.LOGIC_ENDPOINT; + /** * MultiScopesSpanListener includes the most segment to source(s) logic. * @@ -54,10 +59,12 @@ import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SpanTags; */ @Slf4j @RequiredArgsConstructor -public class MultiScopesAnalysisListener implements EntryAnalysisListener, ExitAnalysisListener { +public class MultiScopesAnalysisListener implements EntryAnalysisListener, ExitAnalysisListener, LocalAnalysisListener { private final List<SourceBuilder> entrySourceBuilders = new ArrayList<>(10); private final List<SourceBuilder> exitSourceBuilders = new ArrayList<>(10); private final List<DatabaseSlowStatement> slowDatabaseAccesses = new ArrayList<>(10); + private final List<SourceBuilder> logicEndpointBuilders = new ArrayList<>(10); + private final Gson gson = new Gson(); private final SourceReceiver sourceReceiver; private final TraceServiceModuleConfig config; private final NetworkAddressAliasCache networkAddressAliasCache; @@ -65,7 +72,7 @@ public class MultiScopesAnalysisListener implements EntryAnalysisListener, ExitA @Override public boolean containsPoint(Point point) { - return Point.Entry.equals(point) || Point.Exit.equals(point); + return Point.Entry.equals(point) || Point.Exit.equals(point) || Point.Local.equals(point); } /** @@ -132,6 +139,8 @@ public class MultiScopesAnalysisListener implements EntryAnalysisListener, ExitA setPublicAttrs(sourceBuilder, span); entrySourceBuilders.add(sourceBuilder); } + + parseLogicEndpoints(span, segmentObject); } /** @@ -253,6 +262,11 @@ public class MultiScopesAnalysisListener implements EntryAnalysisListener, ExitA } @Override + public void parseLocal(final SpanObject span, final SegmentObject segmentObject) { + parseLogicEndpoints(span, segmentObject); + } + + @Override public void build() { entrySourceBuilders.forEach(entrySourceBuilder -> { sourceReceiver.receive(entrySourceBuilder.toAll()); @@ -292,6 +306,52 @@ public class MultiScopesAnalysisListener implements EntryAnalysisListener, ExitA }); slowDatabaseAccesses.forEach(sourceReceiver::receive); + + logicEndpointBuilders.forEach(logicEndpointBuilder -> { + sourceReceiver.receive(logicEndpointBuilder.toEndpoint()); + }); + } + + /** + * Logic endpoint could be represent through an entry span or local span. It has special meaning from API + * perspective. But it is an actual RPC call. + */ + private void parseLogicEndpoints(final SpanObject span, final SegmentObject segmentObject) { + span.getTagsList().forEach(tag -> { + switch (tag.getKey()) { + case LOGIC_ENDPOINT: + final JsonObject tagValue = gson.fromJson(tag.getValue(), JsonObject.class); + final boolean isLocalSpan = SpanType.Local.equals(span.getSpanType()); + String logicEndpointName; + int latency; + boolean status; + if (isLocalSpan && tagValue.has("logic-span") && tagValue.get("logic-span").getAsBoolean()) { + logicEndpointName = span.getOperationName(); + latency = (int) (span.getEndTime() - span.getStartTime()); + status = !span.getIsError(); + } else if (tagValue.has("name") && tagValue.has("latency") && tagValue.has("status")) { + logicEndpointName = tagValue.get("name").getAsString(); + latency = tagValue.get("latency").getAsInt(); + status = tagValue.get("status").getAsBoolean(); + } else { + break; + } + SourceBuilder sourceBuilder = new SourceBuilder(namingControl); + sourceBuilder.setTimeBucket(TimeBucket.getMinuteTimeBucket(span.getStartTime())); + sourceBuilder.setDestServiceName(segmentObject.getService()); + sourceBuilder.setDestServiceInstanceName(segmentObject.getServiceInstance()); + sourceBuilder.setDestEndpointName(logicEndpointName); + sourceBuilder.setDestNodeType(NodeType.Normal); + sourceBuilder.setDetectPoint(DetectPoint.SERVER); + sourceBuilder.setLatency(latency); + sourceBuilder.setStatus(status); + sourceBuilder.setType(RequestType.LOGIC); + sourceBuilder.setResponseCode(Const.NONE); + logicEndpointBuilders.add(sourceBuilder); + default: + break; + } + }); } public static class Factory implements AnalysisListenerFactory { diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/SpanTags.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MockReceiver.java similarity index 51% copy from oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/SpanTags.java copy to oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MockReceiver.java index 4f2de97..9443744 100644 --- a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/SpanTags.java +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MockReceiver.java @@ -16,12 +16,33 @@ * */ -package org.apache.skywalking.oap.server.receiver.trace.provider.parser; +package org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener; -public class SpanTags { - public static final String STATUS_CODE = "status_code"; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import org.apache.skywalking.oap.server.core.analysis.DispatcherDetectorListener; +import org.apache.skywalking.oap.server.core.source.Source; +import org.apache.skywalking.oap.server.core.source.SourceReceiver; - public static final String DB_STATEMENT = "db.statement"; +/** + * Mock receiver for testing. + */ +public class MockReceiver implements SourceReceiver { + @Getter + private List<Source> receivedSources = new ArrayList<>(); + + @Override + public void receive(final Source source) { + receivedSources.add(source); + } + + public void clear() { + receivedSources.clear(); + } - public static final String DB_TYPE = "db.type"; + @Override + public DispatcherDetectorListener getDispatcherDetectorListener() { + throw new UnsupportedOperationException("No getDispatcherDetectorListener in mock receiver."); + } } diff --git a/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MultiScopesAnalysisListenerTest.java b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MultiScopesAnalysisListenerTest.java new file mode 100644 index 0000000..b944189 --- /dev/null +++ b/oap-server/server-receiver-plugin/skywalking-trace-receiver-plugin/src/test/java/org/apache/skywalking/oap/server/receiver/trace/provider/parser/listener/MultiScopesAnalysisListenerTest.java @@ -0,0 +1,463 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.oap.server.receiver.trace.provider.parser.listener; + +import com.google.gson.JsonObject; +import java.util.List; +import org.apache.skywalking.apm.network.common.v3.KeyStringValuePair; +import org.apache.skywalking.apm.network.language.agent.v3.RefType; +import org.apache.skywalking.apm.network.language.agent.v3.SegmentObject; +import org.apache.skywalking.apm.network.language.agent.v3.SegmentReference; +import org.apache.skywalking.apm.network.language.agent.v3.SpanLayer; +import org.apache.skywalking.apm.network.language.agent.v3.SpanObject; +import org.apache.skywalking.apm.network.language.agent.v3.SpanType; +import org.apache.skywalking.oap.server.core.Const; +import org.apache.skywalking.oap.server.core.analysis.IDManager; +import org.apache.skywalking.oap.server.core.analysis.NodeType; +import org.apache.skywalking.oap.server.core.analysis.manual.networkalias.NetworkAddressAlias; +import org.apache.skywalking.oap.server.core.cache.NetworkAddressAliasCache; +import org.apache.skywalking.oap.server.core.config.NamingControl; +import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping; +import org.apache.skywalking.oap.server.core.source.All; +import org.apache.skywalking.oap.server.core.source.DatabaseAccess; +import org.apache.skywalking.oap.server.core.source.Endpoint; +import org.apache.skywalking.oap.server.core.source.EndpointRelation; +import org.apache.skywalking.oap.server.core.source.Service; +import org.apache.skywalking.oap.server.core.source.ServiceInstance; +import org.apache.skywalking.oap.server.core.source.ServiceInstanceRelation; +import org.apache.skywalking.oap.server.core.source.ServiceMeta; +import org.apache.skywalking.oap.server.core.source.ServiceRelation; +import org.apache.skywalking.oap.server.core.source.Source; +import org.apache.skywalking.oap.server.receiver.trace.provider.TraceServiceModuleConfig; +import org.apache.skywalking.oap.server.receiver.trace.provider.UninstrumentedGatewaysConfig; +import org.apache.skywalking.oap.server.receiver.trace.provider.parser.SpanTags; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import static org.apache.skywalking.oap.server.receiver.trace.provider.parser.SpanTags.LOGIC_ENDPOINT; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +/** + * MultiScopesSpanListener includes the most segment to source(s) logic. This test covers most cases about the segment + * to sources translation. + * + * This test is a good way to study about how OAP analysis trace segment. + */ +public class MultiScopesAnalysisListenerTest { + @Mock + private static TraceServiceModuleConfig CONFIG; + @Mock + private static NetworkAddressAliasCache CACHE; + @Mock + private static NetworkAddressAliasCache CACHE2; + private static NamingControl NAMING_CONTROL = new NamingControl( + 70, + 100, + 100, + new EndpointNameGrouping() + ); + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + + when(CACHE.get(any())).thenReturn(null); + final NetworkAddressAlias networkAddressAlias = new NetworkAddressAlias(); + final String serviceId = IDManager.ServiceID.buildId("target-service", true); + final String instanceId = IDManager.ServiceInstanceID.buildId(serviceId, "target-instance"); + networkAddressAlias.setRepresentServiceId(serviceId); + networkAddressAlias.setRepresentServiceInstanceId(instanceId); + when(CACHE2.get(any())).thenReturn(networkAddressAlias); + final UninstrumentedGatewaysConfig uninstrumentedGatewaysConfig = Mockito.mock( + UninstrumentedGatewaysConfig.class); + when(uninstrumentedGatewaysConfig.isAddressConfiguredAsGateway(any())).thenReturn(false); + when(CONFIG.getUninstrumentedGatewaysConfig()).thenReturn(uninstrumentedGatewaysConfig); + } + + @Test + public void testContainsPoint() { + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + new MockReceiver(), + CONFIG, + CACHE, + NAMING_CONTROL + ); + Assert.assertTrue(listener.containsPoint(AnalysisListener.Point.Entry)); + Assert.assertTrue(listener.containsPoint(AnalysisListener.Point.Local)); + Assert.assertTrue(listener.containsPoint(AnalysisListener.Point.Exit)); + Assert.assertFalse(listener.containsPoint(AnalysisListener.Point.First)); + Assert.assertFalse(listener.containsPoint(AnalysisListener.Point.Segment)); + } + + /** + * Entry span without ref, usually the first span of the whole trace. + */ + @Test + public void testEntrySpanWithoutRef() { + final MockReceiver mockReceiver = new MockReceiver(); + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + mockReceiver, + CONFIG, + CACHE, + NAMING_CONTROL + ); + + final long startTime = System.currentTimeMillis(); + SpanObject spanObject = SpanObject.newBuilder() + .setOperationName("/springMVC") + .setStartTime(startTime) + .setEndTime(startTime + 1000L) + .setIsError(true) + .setSpanType(SpanType.Entry) + .addTags( + KeyStringValuePair.newBuilder() + .setKey(SpanTags.STATUS_CODE) + .setValue("500") + .build() + ) + .build(); + final SegmentObject segment = SegmentObject.newBuilder() + .setService("mock-service") + .setServiceInstance("mock-instance") + .addSpans(spanObject) + .build(); + listener.parseEntry(spanObject, segment); + listener.build(); + + final List<Source> receivedSources = mockReceiver.getReceivedSources(); + Assert.assertEquals(7, receivedSources.size()); + final All all = (All) receivedSources.get(0); + final Service service = (Service) receivedSources.get(1); + final ServiceInstance serviceInstance = (ServiceInstance) receivedSources.get(2); + final Endpoint endpoint = (Endpoint) receivedSources.get(3); + final ServiceRelation serviceRelation = (ServiceRelation) receivedSources.get(4); + final ServiceInstanceRelation serviceInstanceRelation = (ServiceInstanceRelation) receivedSources.get(5); + final EndpointRelation endpointRelation = (EndpointRelation) receivedSources.get(6); + Assert.assertEquals("mock-service", service.getName()); + Assert.assertEquals(500, service.getResponseCode()); + Assert.assertFalse(service.isStatus()); + Assert.assertEquals("mock-instance", serviceInstance.getName()); + Assert.assertEquals("/springMVC", endpoint.getName()); + Assert.assertEquals(Const.USER_SERVICE_NAME, serviceRelation.getSourceServiceName()); + Assert.assertEquals(service.getName(), serviceRelation.getDestServiceName()); + Assert.assertEquals(Const.USER_INSTANCE_NAME, serviceInstanceRelation.getSourceServiceInstanceName()); + Assert.assertEquals(serviceInstance.getName(), serviceInstanceRelation.getDestServiceInstanceName()); + Assert.assertEquals(Const.USER_ENDPOINT_NAME, endpointRelation.getEndpoint()); + Assert.assertEquals(endpoint.getName(), endpointRelation.getChildEndpoint()); + } + + /** + * Entry span with ref, meaning the downstream has been instrumented. + */ + @Test + public void testEntrySpanRef() { + final MockReceiver mockReceiver = new MockReceiver(); + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + mockReceiver, + CONFIG, + CACHE, + NAMING_CONTROL + ); + + final long startTime = System.currentTimeMillis(); + SpanObject spanObject = SpanObject.newBuilder() + .setOperationName("/springMVC") + .setStartTime(startTime) + .setEndTime(startTime + 1000L) + .setIsError(true) + .setSpanType(SpanType.Entry) + .setSpanLayer(SpanLayer.RPCFramework) + .addRefs( + SegmentReference.newBuilder() + .setRefType(RefType.CrossProcess) + .setParentService("downstream-service") + .setParentServiceInstance("downstream-instance") + .setParentEndpoint("downstream-endpoint") + .setNetworkAddressUsedAtPeer("127.0.0.1") + .build() + ) + .build(); + final SegmentObject segment = SegmentObject.newBuilder() + .setService("mock-service") + .setServiceInstance("mock-instance") + .addSpans(spanObject) + .build(); + listener.parseEntry(spanObject, segment); + listener.build(); + + final List<Source> receivedSources = mockReceiver.getReceivedSources(); + Assert.assertEquals(7, receivedSources.size()); + final All all = (All) receivedSources.get(0); + final Service service = (Service) receivedSources.get(1); + final ServiceInstance serviceInstance = (ServiceInstance) receivedSources.get(2); + final Endpoint endpoint = (Endpoint) receivedSources.get(3); + final ServiceRelation serviceRelation = (ServiceRelation) receivedSources.get(4); + final ServiceInstanceRelation serviceInstanceRelation = (ServiceInstanceRelation) receivedSources.get(5); + final EndpointRelation endpointRelation = (EndpointRelation) receivedSources.get(6); + Assert.assertEquals("mock-service", service.getName()); + Assert.assertEquals("mock-instance", serviceInstance.getName()); + Assert.assertEquals("/springMVC", endpoint.getName()); + Assert.assertEquals("downstream-service", serviceRelation.getSourceServiceName()); + Assert.assertEquals(service.getName(), serviceRelation.getDestServiceName()); + Assert.assertEquals("downstream-instance", serviceInstanceRelation.getSourceServiceInstanceName()); + Assert.assertEquals(serviceInstance.getName(), serviceInstanceRelation.getDestServiceInstanceName()); + Assert.assertEquals("downstream-endpoint", endpointRelation.getEndpoint()); + Assert.assertEquals(endpoint.getName(), endpointRelation.getChildEndpoint()); + } + + /** + * Entry span with ref, but as a MQ server, or uninstrumented server. + */ + @Test + public void testEntrySpanMQRef() { + final MockReceiver mockReceiver = new MockReceiver(); + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + mockReceiver, + CONFIG, + CACHE, + NAMING_CONTROL + ); + + final long startTime = System.currentTimeMillis(); + SpanObject spanObject = SpanObject.newBuilder() + .setOperationName("/springMVC") + .setStartTime(startTime) + .setEndTime(startTime + 1000L) + .setIsError(true) + .setSpanType(SpanType.Entry) + .setSpanLayer(SpanLayer.MQ) + .addRefs( + SegmentReference.newBuilder() + .setRefType(RefType.CrossProcess) + .setParentService("downstream-service") + .setParentServiceInstance("downstream-instance") + .setParentEndpoint("downstream-endpoint") + .setNetworkAddressUsedAtPeer("127.0.0.1") + .build() + ) + .build(); + final SegmentObject segment = SegmentObject.newBuilder() + .setService("mock-service") + .setServiceInstance("mock-instance") + .addSpans(spanObject) + .build(); + listener.parseEntry(spanObject, segment); + listener.build(); + + final List<Source> receivedSources = mockReceiver.getReceivedSources(); + Assert.assertEquals(7, receivedSources.size()); + final All all = (All) receivedSources.get(0); + final Service service = (Service) receivedSources.get(1); + final ServiceInstance serviceInstance = (ServiceInstance) receivedSources.get(2); + final Endpoint endpoint = (Endpoint) receivedSources.get(3); + final ServiceRelation serviceRelation = (ServiceRelation) receivedSources.get(4); + final ServiceInstanceRelation serviceInstanceRelation = (ServiceInstanceRelation) receivedSources.get(5); + final EndpointRelation endpointRelation = (EndpointRelation) receivedSources.get(6); + Assert.assertEquals("mock-service", service.getName()); + Assert.assertEquals("mock-instance", serviceInstance.getName()); + Assert.assertEquals("/springMVC", endpoint.getName()); + Assert.assertEquals("127.0.0.1", serviceRelation.getSourceServiceName()); + Assert.assertEquals(service.getName(), serviceRelation.getDestServiceName()); + Assert.assertEquals("127.0.0.1", serviceInstanceRelation.getSourceServiceInstanceName()); + Assert.assertEquals(serviceInstance.getName(), serviceInstanceRelation.getDestServiceInstanceName()); + Assert.assertEquals("downstream-endpoint", endpointRelation.getEndpoint()); + Assert.assertEquals(endpoint.getName(), endpointRelation.getChildEndpoint()); + } + + /** + * Local span analysis is triggered with logic span tag. + */ + @Test + public void testParseLocalLogicSpan() { + final MockReceiver mockReceiver = new MockReceiver(); + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + mockReceiver, + CONFIG, + CACHE, + NAMING_CONTROL + ); + + final long startTime = System.currentTimeMillis(); + final JsonObject logicSpanTagValue = new JsonObject(); + logicSpanTagValue.addProperty("logic-span", true); + SpanObject spanObject = SpanObject.newBuilder() + .setOperationName("/logic-call") + .setStartTime(startTime) + .setEndTime(startTime + 1000L) + .setIsError(false) + .setSpanType(SpanType.Local) + .addTags(KeyStringValuePair.newBuilder() + .setKey(LOGIC_ENDPOINT) + .setValue(logicSpanTagValue.toString()) + .build()) + .build(); + final SegmentObject segment = SegmentObject.newBuilder() + .setService("mock-service") + .setServiceInstance("mock-instance") + .addSpans(spanObject) + .build(); + listener.parseLocal(spanObject, segment); + listener.build(); + + final List<Source> receivedSources = mockReceiver.getReceivedSources(); + Assert.assertEquals(1, receivedSources.size()); + final Endpoint source = (Endpoint) receivedSources.get(0); + Assert.assertEquals("/logic-call", source.getName()); + + mockReceiver.clear(); + } + + /** + * Local span analysis is triggered with extension logic service tags. + */ + @Test + public void testParseSpanWithLogicEndpointTag() { + final MockReceiver mockReceiver = new MockReceiver(); + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + mockReceiver, + CONFIG, + CACHE, + NAMING_CONTROL + ); + + final long startTime = System.currentTimeMillis(); + final JsonObject logicSpanTagValue = new JsonObject(); + logicSpanTagValue.addProperty("name", "/GraphQL-service"); + logicSpanTagValue.addProperty("latency", 100); + logicSpanTagValue.addProperty("status", false); + SpanObject spanObject = SpanObject.newBuilder() + .setOperationName("/logic-call") + .setStartTime(startTime) + .setEndTime(startTime + 1000L) + .setIsError(false) + .setSpanType(SpanType.Local) + .addTags(KeyStringValuePair.newBuilder() + .setKey(LOGIC_ENDPOINT) + .setValue(logicSpanTagValue.toString()) + .build()) + .build(); + final SegmentObject segment = SegmentObject.newBuilder() + .setService("mock-service") + .setServiceInstance("mock-instance") + .addSpans(spanObject) + .build(); + listener.parseLocal(spanObject, segment); + listener.build(); + + final List<Source> receivedSources = mockReceiver.getReceivedSources(); + Assert.assertEquals(1, receivedSources.size()); + final Endpoint source = (Endpoint) receivedSources.get(0); + Assert.assertEquals("/GraphQL-service", source.getName()); + + mockReceiver.clear(); + } + + /** + * Exit span, represent calling a 3rd party system, when the alias has not been setup, including access database. + */ + @Test + public void testExitSpanWithoutAlias() { + final MockReceiver mockReceiver = new MockReceiver(); + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + mockReceiver, + CONFIG, + CACHE, + NAMING_CONTROL + ); + + final long startTime = System.currentTimeMillis(); + SpanObject spanObject = SpanObject.newBuilder() + .setOperationName("/springMVC") + .setStartTime(startTime) + .setEndTime(startTime + 1000L) + .setIsError(true) + .setSpanType(SpanType.Exit) + .setSpanLayer(SpanLayer.Database) + .setPeer("127.0.0.1:8080") + .build(); + final SegmentObject segment = SegmentObject.newBuilder() + .setService("mock-service") + .setServiceInstance("mock-instance") + .addSpans(spanObject) + .build(); + listener.parseExit(spanObject, segment); + listener.build(); + + final List<Source> receivedSources = mockReceiver.getReceivedSources(); + Assert.assertEquals(4, receivedSources.size()); + final ServiceRelation serviceRelation = (ServiceRelation) receivedSources.get(0); + final ServiceInstanceRelation serviceInstanceRelation = (ServiceInstanceRelation) receivedSources.get(1); + final ServiceMeta serviceMeta = (ServiceMeta) receivedSources.get(2); + final DatabaseAccess databaseAccess = (DatabaseAccess) receivedSources.get(3); + Assert.assertEquals("mock-service", serviceRelation.getSourceServiceName()); + Assert.assertEquals("127.0.0.1:8080", serviceRelation.getDestServiceName()); + Assert.assertEquals("mock-instance", serviceInstanceRelation.getSourceServiceInstanceName()); + Assert.assertEquals("127.0.0.1:8080", serviceInstanceRelation.getDestServiceInstanceName()); + Assert.assertEquals("127.0.0.1:8080", serviceMeta.getName()); + Assert.assertEquals(NodeType.Database, serviceMeta.getNodeType()); + Assert.assertEquals("127.0.0.1:8080", databaseAccess.getName()); + } + + /** + * Exit span, represent calling a 3rd party system, when the alias has been setup. + */ + @Test + public void testExitSpanWithAlias() { + final MockReceiver mockReceiver = new MockReceiver(); + MultiScopesAnalysisListener listener = new MultiScopesAnalysisListener( + mockReceiver, + CONFIG, + CACHE2, + NAMING_CONTROL + ); + + final long startTime = System.currentTimeMillis(); + SpanObject spanObject = SpanObject.newBuilder() + .setOperationName("/springMVC") + .setStartTime(startTime) + .setEndTime(startTime + 1000L) + .setIsError(true) + .setSpanType(SpanType.Exit) + .setSpanLayer(SpanLayer.MQ) + .setPeer("127.0.0.1:8080") + .build(); + final SegmentObject segment = SegmentObject.newBuilder() + .setService("mock-service") + .setServiceInstance("mock-instance") + .addSpans(spanObject) + .build(); + listener.parseExit(spanObject, segment); + listener.build(); + + final List<Source> receivedSources = mockReceiver.getReceivedSources(); + Assert.assertEquals(2, receivedSources.size()); + final ServiceRelation serviceRelation = (ServiceRelation) receivedSources.get(0); + final ServiceInstanceRelation serviceInstanceRelation = (ServiceInstanceRelation) receivedSources.get(1); + Assert.assertEquals("mock-service", serviceRelation.getSourceServiceName()); + Assert.assertEquals("target-service", serviceRelation.getDestServiceName()); + Assert.assertEquals("mock-instance", serviceInstanceRelation.getSourceServiceInstanceName()); + Assert.assertEquals("target-instance", serviceInstanceRelation.getDestServiceInstanceName()); + mockReceiver.clear(); + } +}
