Support `logic-endpoint` series tag(s). (#4985)

* Support `logic-endpoint` series tag(s).

* Enhance the plugin dev doc.

* Update Java-Plugin-Development-Guide.md

fix punctuation

Co-authored-by: caoyixiong <32847163+IanCao@users.noreply.github.com>
This commit is contained in:
吴晟 Wu Sheng 2020-06-30 00:07:53 +08:00 committed by GitHub
parent 31f42343f6
commit a93f1b3c3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 644 additions and 4 deletions

View File

@ -138,7 +138,7 @@ Create ExitSpan by operation name(e.g. service name, uri) and new **ContextCarri
*/
AbstractSpan setOperationName(String endpointName);
```
Besides set operation name, tags and logs, two attributes shoule be set, which are component and layer,
Besides setting operation name, tags and logs, two attributes should be set, which are component and layer,
especially for EntrySpan and ExitSpan
SpanLayer is the catalog of span. Here are 5 values:
@ -151,6 +151,36 @@ SpanLayer is the catalog of span. Here are 5 values:
Component IDs are defined and reserved by SkyWalking project.
For component name/ID extension, please follow [Component library definition and extension](Component-library-settings.md) document.
### Special Span Tags
All tags are available in the trace view, meanwhile,
in the OAP backend analysis, some special tag or tag combination could provide other advanced features.
#### Tag key `status_code`
The value should be an integer. The response code of OAL entities is according to this.
### Tag key `db.statement` and `db.type`.
The value of `db.statement` should be a String, representing the Database statement, such as SQL, or `[No statement]/`+span#operationName if value is empty.
When exit span has this tag, OAP samples the slow statements based on `receiver-trace/default/maxSlowSQLLength`.
The threshold of slow statement is defined by following [`receiver-trace/default/slowDBAccessThreshold`](../setup/backend/slow-db-statement.md)
### Extension logic endpoint. Tag key `x-le`
Logic endpoint is a concept, which doesn't represent a real RPC call, but requires the statistic.
The value of `x-le` should be JSON format, with two options.
1. Define a separated logic endpoint. Provide its own endpoint name, latency and status. Suitable for entry and local span.
```json
{
"name": "GraphQL-service",
"latency": 100,
"status": true
}
```
2. Declare the current local span representing a logic endpoint.
```json
{
"logic-span": true
}
```
### Advanced APIs
#### Async Span APIs
There is a set of advanced APIs in Span, which work specific for async scenario. When tags, logs, attributes(including end time) of the span
@ -345,6 +375,13 @@ public class URLInstrumentation extends ClassEnhancePluginDefine {
**NOTICE**, doing bootstrap instrumentation should only happen in necessary, but mostly it effect the JRE core(rt.jar),
and could make very unexpected result or side effect.
### Plugin Test Tool
[Apache SkyWalking Agent Test Tool Suite](https://github.com/apache/skywalking-agent-test-tool)
a tremendously useful test tools suite in a wide variety of languages of Agent. Includes mock collector and validator.
The mock collector is a SkyWalking receiver, like OAP server.
You could learn how to use this tool to test the plugin in [this doc](Plugin-test.md). If you want to contribute plugins
to SkyWalking official repo, this is required.
### Contribute plugins into Apache SkyWalking repository
We are welcome everyone to contribute plugins.

View File

@ -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
}

View File

@ -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";
}

View File

@ -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);
}
/**
@ -252,6 +261,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 -> {
@ -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 {

View File

@ -0,0 +1,48 @@
/*
* 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 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;
/**
* 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();
}
@Override
public DispatcherDetectorListener getDispatcherDetectorListener() {
throw new UnsupportedOperationException("No getDispatcherDetectorListener in mock receiver.");
}
}

View File

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