You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@servicecomb.apache.org by GitBox <gi...@apache.org> on 2018/01/09 07:38:54 UTC

[GitHub] wujimin closed pull request #485: [SCB-176] Generate traceId

wujimin closed pull request #485: [SCB-176] Generate traceId
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/485
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/common/common-rest/src/main/java/io/servicecomb/common/rest/filter/tracing/TracingFilter.java b/common/common-rest/src/main/java/io/servicecomb/common/rest/filter/tracing/TracingFilter.java
new file mode 100644
index 000000000..8646c2b5b
--- /dev/null
+++ b/common/common-rest/src/main/java/io/servicecomb/common/rest/filter/tracing/TracingFilter.java
@@ -0,0 +1,76 @@
+/*
+ * 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 io.servicecomb.common.rest.filter.tracing;
+
+import org.springframework.util.StringUtils;
+
+import io.servicecomb.common.rest.filter.HttpServerFilter;
+import io.servicecomb.core.Const;
+import io.servicecomb.core.Invocation;
+import io.servicecomb.core.tracing.BraveTraceIdGenerator;
+import io.servicecomb.core.tracing.TraceIdGenerator;
+import io.servicecomb.foundation.vertx.http.HttpServletRequestEx;
+import io.servicecomb.foundation.vertx.http.HttpServletResponseEx;
+import io.servicecomb.swagger.invocation.Response;
+
+/**
+ * Ensure the invocation contains traceId
+ */
+public class TracingFilter implements HttpServerFilter {
+  private TraceIdGenerator traceIdGenerator = getTraceIdGenerator();
+
+  @Override
+  public int getOrder() {
+    return 0;
+  }
+
+  /**
+   * Ensure the invocation contains traceId
+   * @return {@code null}
+   */
+  @Override
+  public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx) {
+    if (!StringUtils.isEmpty(invocation.getContext(Const.TRACE_ID_NAME))) {
+      // if invocation context contains traceId, nothing needed to do
+      return null;
+    }
+
+    String traceId = requestEx.getHeader(Const.TRACE_ID_NAME);
+    if (!StringUtils.isEmpty(traceId)) {
+      // if request header contains traceId, move traceId into invocation context
+      invocation.addContext(Const.TRACE_ID_NAME, traceId);
+      return null;
+    }
+
+    // if traceId not found, generate a traceId
+    invocation.addContext(Const.TRACE_ID_NAME, traceIdGenerator.generateStringId());
+
+    return null;
+  }
+
+  /**
+   * nothing to do
+   */
+  @Override
+  public void beforeSendResponse(Invocation invocation, HttpServletResponseEx responseEx) {
+  }
+
+  protected TraceIdGenerator getTraceIdGenerator() {
+    return BraveTraceIdGenerator.INSTANCE;
+  }
+}
diff --git a/common/common-rest/src/main/resources/META-INF/services/io.servicecomb.common.rest.filter.HttpServerFilter b/common/common-rest/src/main/resources/META-INF/services/io.servicecomb.common.rest.filter.HttpServerFilter
new file mode 100644
index 000000000..dd2626fcb
--- /dev/null
+++ b/common/common-rest/src/main/resources/META-INF/services/io.servicecomb.common.rest.filter.HttpServerFilter
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+io.servicecomb.common.rest.filter.tracing.TracingFilter
diff --git a/common/common-rest/src/test/java/io/servicecomb/common/rest/filter/tracing/TracingFilterTest.java b/common/common-rest/src/test/java/io/servicecomb/common/rest/filter/tracing/TracingFilterTest.java
new file mode 100644
index 000000000..b7cdcbd4f
--- /dev/null
+++ b/common/common-rest/src/test/java/io/servicecomb/common/rest/filter/tracing/TracingFilterTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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 io.servicecomb.common.rest.filter.tracing;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.core.Const;
+import io.servicecomb.core.Invocation;
+import io.servicecomb.core.tracing.TraceIdGenerator;
+import io.servicecomb.foundation.vertx.http.HttpServletRequestEx;
+
+public class TracingFilterTest {
+  private static final TestTracingFilter FILTER = new TestTracingFilter();
+
+  @Test
+  public void testAfterReceiveRequestOnInvocationContainsTraceId() {
+    Invocation invocation = Mockito.mock(Invocation.class);
+    String traceId = "traceIdTest";
+    HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class);
+
+    Mockito.when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(traceId);
+
+    FILTER.afterReceiveRequest(invocation, requestEx);
+
+    Mockito.verify(requestEx, Mockito.times(0)).getHeader(Const.TRACE_ID_NAME);
+  }
+
+  @Test
+  public void testAfterReceiveRequestOnHeaderContainsTraceId() {
+    Invocation invocation = Mockito.mock(Invocation.class);
+    String traceId = "traceIdTest";
+    HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class);
+
+    Mockito.when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(null);
+    Mockito.when(requestEx.getHeader(Const.TRACE_ID_NAME)).thenReturn(traceId);
+
+    FILTER.afterReceiveRequest(invocation, requestEx);
+
+    Mockito.verify(invocation).addContext(Const.TRACE_ID_NAME, traceId);
+  }
+
+  @Test
+  public void testAfterReceiveRequestOnGenerateTraceId() {
+    Invocation invocation = Mockito.mock(Invocation.class);
+    HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class);
+
+    Mockito.when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(null);
+    Mockito.when(requestEx.getHeader(Const.TRACE_ID_NAME)).thenReturn(null);
+
+    FILTER.afterReceiveRequest(invocation, requestEx);
+
+    Mockito.verify(invocation).addContext(Const.TRACE_ID_NAME, TestTracingFilter.TRACE_ID);
+  }
+
+  static class TestTracingFilter extends TracingFilter {
+
+    static final String TRACE_ID = "" + Long.MAX_VALUE;
+
+    @Override
+    protected TraceIdGenerator getTraceIdGenerator() {
+      return () -> TRACE_ID;
+    }
+  }
+}
diff --git a/core/pom.xml b/core/pom.xml
index 573a489e6..ceee759e1 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -38,6 +38,10 @@
       <groupId>io.servicecomb</groupId>
       <artifactId>foundation-metrics</artifactId>
     </dependency>
+    <dependency>
+      <groupId>io.zipkin.brave</groupId>
+      <artifactId>brave</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.jmockit</groupId>
       <artifactId>jmockit</artifactId>
diff --git a/core/src/main/java/io/servicecomb/core/Const.java b/core/src/main/java/io/servicecomb/core/Const.java
index 63af71af4..b0c2d7be7 100644
--- a/core/src/main/java/io/servicecomb/core/Const.java
+++ b/core/src/main/java/io/servicecomb/core/Const.java
@@ -40,6 +40,8 @@ private Const() {
   public static final String TARGET_MICROSERVICE = "x-cse-target-microservice";
 
   public static final String REMOTE_ADDRESS = "x-cse-remote-address";
-  
+
   public static final String AUTH_TOKEN = "x-cse-auth-rsatoken";
+
+  public static final String TRACE_ID_NAME = "X-B3-TraceId";
 }
diff --git a/core/src/main/java/io/servicecomb/core/tracing/BraveTraceIdGenerator.java b/core/src/main/java/io/servicecomb/core/tracing/BraveTraceIdGenerator.java
new file mode 100644
index 000000000..dbacc787d
--- /dev/null
+++ b/core/src/main/java/io/servicecomb/core/tracing/BraveTraceIdGenerator.java
@@ -0,0 +1,32 @@
+/*
+ * 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 io.servicecomb.core.tracing;
+
+import brave.internal.Platform;
+
+public class BraveTraceIdGenerator implements TraceIdGenerator {
+  public static final BraveTraceIdGenerator INSTANCE = new BraveTraceIdGenerator();
+
+  @Override
+  public String generateStringId() {
+    return String.valueOf(Platform.get().nextTraceIdHigh());
+  }
+
+  private BraveTraceIdGenerator() {
+  }
+}
diff --git a/core/src/main/java/io/servicecomb/core/tracing/TraceIdGenerator.java b/core/src/main/java/io/servicecomb/core/tracing/TraceIdGenerator.java
new file mode 100644
index 000000000..43a449008
--- /dev/null
+++ b/core/src/main/java/io/servicecomb/core/tracing/TraceIdGenerator.java
@@ -0,0 +1,22 @@
+/*
+ * 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 io.servicecomb.core.tracing;
+
+public interface TraceIdGenerator {
+  String generateStringId();
+}
diff --git a/core/src/test/java/io/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java b/core/src/test/java/io/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java
new file mode 100644
index 000000000..ad2231e1e
--- /dev/null
+++ b/core/src/test/java/io/servicecomb/core/tracing/BraveTraceIdGeneratorTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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 io.servicecomb.core.tracing;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+public class BraveTraceIdGeneratorTest {
+
+  @Test
+  public void generateStringId() {
+    TraceIdGenerator traceIdGenerator = BraveTraceIdGenerator.INSTANCE;
+    assertNotEquals(traceIdGenerator.generateStringId(), traceIdGenerator.generateStringId());
+
+    String traceId = traceIdGenerator.generateStringId();
+    try {
+      Long.valueOf(traceId);
+    } catch (NumberFormatException e) {
+      fail("wrong traceId format: " + traceId);
+    }
+  }
+}
diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java
index b566b29fd..6dd2810e0 100644
--- a/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java
+++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/io/servicecomb/demo/jaxrs/server/CodeFirstJaxrs.java
@@ -38,6 +38,7 @@
 import javax.ws.rs.core.Response.Status;
 
 import io.servicecomb.common.rest.codec.RestObjectMapper;
+import io.servicecomb.core.Const;
 import io.servicecomb.demo.compute.Person;
 import io.servicecomb.demo.ignore.InputModelForTestIgnore;
 import io.servicecomb.demo.ignore.OutputModelForTestIgnore;
@@ -70,10 +71,10 @@
   public Response cseResponse(InvocationContext c1) {
     Response response = Response.createSuccess(Status.ACCEPTED, new User());
     Headers headers = response.getHeaders();
-    headers.addHeader("h1", "h1v " + c1.getContext().toString());
+    headers.addHeader("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     InvocationContext c2 = ContextUtils.getInvocationContext();
-    headers.addHeader("h2", "h2v " + c2.getContext().toString());
+    headers.addHeader("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     return response;
   }
@@ -202,4 +203,10 @@ public String testRawJsonAnnotation(@RawJsonRequestBody String jsonInput) {
     }
     return "hello " + person.get("name");
   }
+
+  @Path("/traceId")
+  @GET
+  public String getTraceId() {
+    return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME);
+  }
 }
diff --git a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java
index 4272e0ec9..17ec6af72 100644
--- a/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java
+++ b/demo/demo-pojo/pojo-client/src/main/java/io/servicecomb/demo/pojo/client/PojoClient.java
@@ -27,6 +27,7 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
+import io.servicecomb.core.Const;
 import io.servicecomb.core.CseContext;
 import io.servicecomb.core.provider.consumer.InvokerUtils;
 import io.servicecomb.demo.DemoConst;
@@ -40,11 +41,13 @@
 import io.servicecomb.foundation.common.utils.BeanUtils;
 import io.servicecomb.foundation.common.utils.Log4jUtils;
 import io.servicecomb.provider.pojo.RpcReference;
+import io.servicecomb.swagger.invocation.context.ContextUtils;
+import io.servicecomb.swagger.invocation.context.InvocationContext;
 import io.servicecomb.swagger.invocation.exception.InvocationException;
 
 @Component
 public class PojoClient {
-  private static Logger LOGGER = LoggerFactory.getLogger(PojoClient.class);
+  public static final byte buffer[] = new byte[1024];
 
   public static CodeFirstPojoClient codeFirstPojoClient;
 
@@ -58,9 +61,9 @@
 
   public static Test testFromXml;
 
-  private static SmartCare smartcare;
+  private static Logger LOGGER = LoggerFactory.getLogger(PojoClient.class);
 
-  public static final byte buffer[] = new byte[1024];
+  private static SmartCare smartcare;
 
   static {
     Arrays.fill(buffer, (byte) 1);
@@ -70,11 +73,6 @@ public static void setTestFromXml(Test testFromXml) {
     PojoClient.testFromXml = testFromXml;
   }
 
-  @Inject
-  public void setCodeFirstPojoClient(CodeFirstPojoClient codeFirstPojoClient) {
-    PojoClient.codeFirstPojoClient = codeFirstPojoClient;
-  }
-
   public static void main(String[] args) throws Exception {
     Log4jUtils.init();
     BeanUtils.init();
@@ -110,9 +108,33 @@ public static void run() throws Exception {
       testSmartCare(smartcare);
 
       testCommonInvoke(transport);
+
+      if ("rest".equals(transport)) {
+        testTraceIdOnNotSetBefore();
+      }
+
+      testTraceIdOnContextContainsTraceId();
     }
   }
 
+  /**
+   * Only in http transport, traceId will be set to invocation if null.
+   * But in highway, nothing done.
+   */
+  private static void testTraceIdOnNotSetBefore() {
+    String traceId = test.testTraceId();
+    TestMgr.checkNotEmpty(traceId);
+  }
+
+  private static void testTraceIdOnContextContainsTraceId() {
+    InvocationContext context = new InvocationContext();
+    context.addContext(Const.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE));
+    ContextUtils.setInvocationContext(context);
+    String traceId = test.testTraceId();
+    TestMgr.check(String.valueOf(Long.MIN_VALUE), traceId);
+    ContextUtils.removeInvocationContext();
+  }
+
   private static void testSmartCare(SmartCare smartCare) {
     Group group = new Group();
     group.setName("group0");
@@ -221,4 +243,9 @@ private static void testWrapParam(Test test) {
 
     TestMgr.check("User [name=nameA,  users count:1, age=100, index=0]", result);
   }
+
+  @Inject
+  public void setCodeFirstPojoClient(CodeFirstPojoClient codeFirstPojoClient) {
+    PojoClient.codeFirstPojoClient = codeFirstPojoClient;
+  }
 }
diff --git a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java b/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java
index 915467a09..8040500d3 100644
--- a/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java
+++ b/demo/demo-pojo/pojo-server/src/main/java/io/servicecomb/demo/pojo/server/TestImpl.java
@@ -20,10 +20,12 @@
 import java.util.Arrays;
 import java.util.List;
 
+import io.servicecomb.core.Const;
 import io.servicecomb.demo.server.Test;
 import io.servicecomb.demo.server.TestRequest;
 import io.servicecomb.demo.server.User;
 import io.servicecomb.provider.pojo.RpcSchema;
+import io.servicecomb.swagger.invocation.context.ContextUtils;
 import io.servicecomb.swagger.invocation.exception.InvocationException;
 
 @RpcSchema(schemaId = "server")
@@ -98,4 +100,9 @@ public String addString(String[] strArr) {
     System.out.println("addString: " + result);
     return result;
   }
+
+  @Override
+  public String testTraceId() {
+    return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME);
+  }
 }
diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java
index 2db5565f5..5c5a2745d 100644
--- a/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java
+++ b/demo/demo-schema/src/main/java/io/servicecomb/demo/CodeFirstRestTemplate.java
@@ -28,12 +28,15 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.client.RestTemplate;
 
+import io.servicecomb.core.Const;
 import io.servicecomb.core.CseContext;
 import io.servicecomb.demo.compute.Person;
 import io.servicecomb.demo.ignore.InputModelForTestIgnore;
 import io.servicecomb.demo.ignore.OutputModelForTestIgnore;
 import io.servicecomb.demo.server.User;
 import io.servicecomb.serviceregistry.RegistryUtils;
+import io.servicecomb.swagger.invocation.context.ContextUtils;
+import io.servicecomb.swagger.invocation.context.InvocationContext;
 import io.vertx.core.json.JsonObject;
 
 public class CodeFirstRestTemplate {
@@ -65,8 +68,12 @@ public void testCodeFirst(RestTemplate template, String microserviceName, String
       // TODO: highway unsupported until JAV-394 completed
       if (transport.equals("rest")) {
         testOnlyRest(template, cseUrlPrefix);
+        // only rest transport will set trace id
+        testTraceIdOnNotSetBefore(template, cseUrlPrefix);
       }
 
+      testTraceIdOnContextContainsTraceId(template, cseUrlPrefix);
+
       testRawJson(template, cseUrlPrefix);
     }
   }
@@ -123,13 +130,12 @@ protected void checkStatusCode(String microserviceName, int expectStatusCode, Ht
   private void testCseResponse(String targetMicroserviceName, RestTemplate template,
       String cseUrlPrefix) {
     String srcMicroserviceName = RegistryUtils.getMicroservice().getServiceName();
-    String context = String.format("{x-cse-src-microservice=%s}", srcMicroserviceName);
 
     ResponseEntity<User> responseEntity =
         template.exchange(cseUrlPrefix + "cseResponse", HttpMethod.GET, null, User.class);
     TestMgr.check("User [name=nameA, age=100, index=0]", responseEntity.getBody());
-    TestMgr.check("h1v " + context, responseEntity.getHeaders().getFirst("h1"));
-    TestMgr.check("h2v " + context, responseEntity.getHeaders().getFirst("h2"));
+    TestMgr.check("h1v " + srcMicroserviceName, responseEntity.getHeaders().getFirst("h1"));
+    TestMgr.check("h2v " + srcMicroserviceName, responseEntity.getHeaders().getFirst("h2"));
     checkStatusCode(targetMicroserviceName, 202, responseEntity.getStatusCode());
   }
 
@@ -227,7 +233,7 @@ protected void testCodeFirstReduce(RestTemplate template, String cseUrlPrefix) {
   protected void testModelFieldIgnore(RestTemplate template, String cseUrlPrefix) {
     InputModelForTestIgnore input = new InputModelForTestIgnore("input_id_rest", "input_id_content",
         new Person("inputSomeone"), new JsonObject("{\"InputJsonKey\" : \"InputJsonValue\"}"), () -> {
-        });
+    });
     OutputModelForTestIgnore output = template
         .postForObject(cseUrlPrefix + "ignore", input, OutputModelForTestIgnore.class);
 
@@ -249,4 +255,20 @@ protected void testRawJson(RestTemplate template, String cseUrlPrefix) {
     String output = template.postForObject(cseUrlPrefix + "rawJsonAnnotation", input, String.class);
     TestMgr.check("hello zyy", output);
   }
+
+  protected void testTraceIdOnNotSetBefore(RestTemplate template, String cseUrlPrefix) {
+    String traceIdUrl = cseUrlPrefix + "traceId";
+    String result = template.getForObject(traceIdUrl, String.class);
+    TestMgr.checkNotEmpty(result);
+  }
+
+  protected void testTraceIdOnContextContainsTraceId(RestTemplate template, String cseUrlPrefix) {
+    String traceIdUrl = cseUrlPrefix + "traceId";
+    InvocationContext invocationContext = new InvocationContext();
+    invocationContext.addContext(Const.TRACE_ID_NAME, String.valueOf(Long.MIN_VALUE));
+    ContextUtils.setInvocationContext(invocationContext);
+    String result = template.getForObject(traceIdUrl, String.class);
+    TestMgr.check(String.valueOf(Long.MIN_VALUE), result);
+    ContextUtils.removeInvocationContext();
+  }
 }
diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/TestMgr.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/TestMgr.java
index d74457ba4..16475b330 100644
--- a/demo/demo-schema/src/main/java/io/servicecomb/demo/TestMgr.java
+++ b/demo/demo-schema/src/main/java/io/servicecomb/demo/TestMgr.java
@@ -22,6 +22,7 @@
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
 
 public class TestMgr {
   private static final Logger LOGGER = LoggerFactory.getLogger(TestMgr.class);
@@ -47,6 +48,12 @@ public static void check(Object expect, Object real) {
     }
   }
 
+  public static void checkNotEmpty(String real) {
+    if (StringUtils.isEmpty(real)) {
+      errorList.add(new Error(msg + " | unexpected null result, method is " + getCaller()));
+    }
+  }
+
   public static void summary() {
     if (errorList.isEmpty()) {
       LOGGER.info("............. test finished ............");
@@ -62,4 +69,13 @@ public static void summary() {
   public static List<Throwable> errors() {
     return errorList;
   }
+
+  private static String getCaller() {
+    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+    if (stackTrace.length < 3) {
+      return null;
+    }
+    StackTraceElement stackTraceElement = stackTrace[3];
+    return stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName();
+  }
 }
diff --git a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/Test.java b/demo/demo-schema/src/main/java/io/servicecomb/demo/server/Test.java
index c1068167b..6b93798f7 100644
--- a/demo/demo-schema/src/main/java/io/servicecomb/demo/server/Test.java
+++ b/demo/demo-schema/src/main/java/io/servicecomb/demo/server/Test.java
@@ -33,4 +33,6 @@
   User splitParam(int index, User user);
 
   String addString(String[] strArr);
+
+  String testTraceId();
 }
diff --git a/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml b/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml
index 38e6fc012..493da59e5 100644
--- a/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml
+++ b/demo/demo-schema/src/main/resources/microservices/pojo/server.yaml
@@ -176,7 +176,14 @@ paths:
           description: add string
           schema:
             type: string
-            
+  /testTraceId:
+    get:
+      operationId: testTraceId
+      responses:
+        200:
+          description: get trace id
+          schema:
+            type: string
 definitions:
   Error:
     type: object
diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java
index d588c22a0..e833bf84e 100644
--- a/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java
+++ b/demo/demo-springmvc/springmvc-client/src/main/java/io/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java
@@ -160,15 +160,15 @@ private void testIntf() {
 
     ResponseEntity<Date> responseEntity = intf.responseEntity(date);
     TestMgr.check(date, responseEntity.getBody());
-    TestMgr.check("h1v {x-cse-src-microservice=" + srcName + "}", responseEntity.getHeaders().getFirst("h1"));
-    TestMgr.check("h2v {x-cse-src-microservice=" + srcName + "}", responseEntity.getHeaders().getFirst("h2"));
+    TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1"));
+    TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2"));
 
     checkStatusCode("springmvc", 202, responseEntity.getStatusCode());
 
     Response cseResponse = intf.cseResponse();
     TestMgr.check("User [name=nameA, age=100, index=0]", cseResponse.getResult());
-    TestMgr.check("h1v {x-cse-src-microservice=" + srcName + "}", cseResponse.getHeaders().getFirst("h1"));
-    TestMgr.check("h2v {x-cse-src-microservice=" + srcName + "}", cseResponse.getHeaders().getFirst("h2"));
+    TestMgr.check("h1v " + srcName, cseResponse.getHeaders().getFirst("h1"));
+    TestMgr.check("h2v " + srcName, cseResponse.getHeaders().getFirst("h2"));
   }
 
   private void testResponseEntity(String microserviceName, RestTemplate template, String cseUrlPrefix) {
@@ -184,19 +184,15 @@ private void testResponseEntity(String microserviceName, RestTemplate template,
     ResponseEntity<Date> responseEntity =
         template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.POST, httpEntity, Date.class);
     TestMgr.check(date, responseEntity.getBody());
-    TestMgr.check("h1v {contextKey=contextValue, x-cse-src-microservice=" + srcName + "}",
-        responseEntity.getHeaders().getFirst("h1"));
-    TestMgr.check("h2v {contextKey=contextValue, x-cse-src-microservice=" + srcName + "}",
-        responseEntity.getHeaders().getFirst("h2"));
+    TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1"));
+    TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2"));
     checkStatusCode(microserviceName, 202, responseEntity.getStatusCode());
 
     responseEntity =
         template.exchange(cseUrlPrefix + "responseEntity", HttpMethod.PATCH, httpEntity, Date.class);
     TestMgr.check(date, responseEntity.getBody());
-    TestMgr.check("h1v {contextKey=contextValue, x-cse-src-microservice=" + srcName + "}",
-        responseEntity.getHeaders().getFirst("h1"));
-    TestMgr.check("h2v {contextKey=contextValue, x-cse-src-microservice=" + srcName + "}",
-        responseEntity.getHeaders().getFirst("h2"));
+    TestMgr.check("h1v " + srcName, responseEntity.getHeaders().getFirst("h1"));
+    TestMgr.check("h2v " + srcName, responseEntity.getHeaders().getFirst("h2"));
     checkStatusCode(microserviceName, 202, responseEntity.getStatusCode());
   }
 
diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
index 7176abecd..85bfecae3 100644
--- a/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
+++ b/demo/demo-springmvc/springmvc-server/src/main/java/io/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java
@@ -56,6 +56,7 @@
 import com.netflix.servo.monitor.Monitor;
 
 import io.servicecomb.common.rest.codec.RestObjectMapper;
+import io.servicecomb.core.Const;
 import io.servicecomb.demo.compute.Person;
 import io.servicecomb.demo.ignore.InputModelForTestIgnore;
 import io.servicecomb.demo.ignore.OutputModelForTestIgnore;
@@ -112,10 +113,10 @@ public String fileUpload(@RequestPart(name = "file1") MultipartFile file1,
   @RequestMapping(path = "/responseEntity", method = RequestMethod.POST)
   public ResponseEntity<Date> responseEntity(InvocationContext c1, @RequestAttribute("date") Date date) {
     HttpHeaders headers = new HttpHeaders();
-    headers.add("h1", "h1v " + c1.getContext().toString());
+    headers.add("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     InvocationContext c2 = ContextUtils.getInvocationContext();
-    headers.add("h2", "h2v " + c2.getContext().toString());
+    headers.add("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     return new ResponseEntity<Date>(date, headers, HttpStatus.ACCEPTED);
   }
@@ -125,10 +126,10 @@ public String fileUpload(@RequestPart(name = "file1") MultipartFile file1,
   @RequestMapping(path = "/responseEntity", method = RequestMethod.PATCH)
   public ResponseEntity<Date> responseEntityPATCH(InvocationContext c1, @RequestAttribute("date") Date date) {
     HttpHeaders headers = new HttpHeaders();
-    headers.add("h1", "h1v " + c1.getContext().toString());
+    headers.add("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     InvocationContext c2 = ContextUtils.getInvocationContext();
-    headers.add("h2", "h2v " + c2.getContext().toString());
+    headers.add("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     return new ResponseEntity<Date>(date, headers, HttpStatus.ACCEPTED);
   }
@@ -140,10 +141,10 @@ public String fileUpload(@RequestPart(name = "file1") MultipartFile file1,
   public Response cseResponse(InvocationContext c1) {
     Response response = Response.createSuccess(Status.ACCEPTED, new User());
     Headers headers = response.getHeaders();
-    headers.addHeader("h1", "h1v " + c1.getContext().toString());
+    headers.addHeader("h1", "h1v " + c1.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     InvocationContext c2 = ContextUtils.getInvocationContext();
-    headers.addHeader("h2", "h2v " + c2.getContext().toString());
+    headers.addHeader("h2", "h2v " + c2.getContext().get(Const.SRC_MICROSERVICE).toString());
 
     return response;
   }
@@ -300,7 +301,7 @@ public OutputModelForTestIgnore testModelWithIgnoreField(@RequestBody InputModel
     return new OutputModelForTestIgnore("output_id", input.getInputId(), input.getContent(), input.getInputObject(),
         input.getInputJsonObject(), input.getInputIgnoreInterface(),
         new Person("outputSomeone"), new JsonObject("{\"OutputJsonKey\" : \"OutputJsonValue\"}"), () -> {
-        });
+    });
   }
 
   @SuppressWarnings("unchecked")
@@ -344,4 +345,9 @@ public String metricsForTest() {
       throw new InvocationException(500, "500", "JsonProcessingException", e);
     }
   }
+
+  @GetMapping(path = "/traceId")
+  public String getTraceId() {
+    return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME);
+  }
 }
diff --git a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/TestImpl.java b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/TestImpl.java
index 91b4b43fb..f3e066d01 100644
--- a/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/TestImpl.java
+++ b/integration-tests/pojo-test/src/test/java/io/servicecomb/demo/pojo/test/endpoints/TestImpl.java
@@ -20,10 +20,12 @@
 import java.util.Arrays;
 import java.util.List;
 
+import io.servicecomb.core.Const;
 import io.servicecomb.demo.server.Test;
 import io.servicecomb.demo.server.TestRequest;
 import io.servicecomb.demo.server.User;
 import io.servicecomb.provider.pojo.RpcSchema;
+import io.servicecomb.swagger.invocation.context.ContextUtils;
 import io.servicecomb.swagger.invocation.exception.InvocationException;
 
 @RpcSchema(schemaId = "server")
@@ -99,4 +101,9 @@ public String addString(String[] strArr) {
     System.out.println("addString: " + result);
     return result;
   }
+
+  @Override
+  public String testTraceId() {
+    return ContextUtils.getInvocationContext().getContext(Const.TRACE_ID_NAME);
+  }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services