Don't start a new trace if request tracing was already started

If request tracing was started twice we would generate 2 trace IDs and
set the TRACE_ID logging tag for each of the trace IDs. This is
unneeded. Instead just use the existing trace ID if request tracing is
started multiple times.

This is useful if a custom servlet filter is used to start requesting.

Change-Id: Id5536fb9cef30b96e0e74df2d8d3127f8afdd356
Signed-off-by: Edwin Kempin <ekempin@google.com>
diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java
index 7d73303..5b8d8e1 100644
--- a/java/com/google/gerrit/server/logging/TraceContext.java
+++ b/java/com/google/gerrit/server/logging/TraceContext.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Table;
+import java.util.Optional;
 
 public class TraceContext implements AutoCloseable {
   public static TraceContext open() {
@@ -32,6 +33,10 @@
    *   <li>enables force logging
    * </ul>
    *
+   * <p>A new trace ID is only generated if request tracing was not started yet. If request tracing
+   * was already started the given {@code traceIdConsumer} is invoked with the existing trace ID and
+   * no new logging tag is set.
+   *
    * <p>No-op if {@code trace} is {@code false}.
    *
    * @param trace whether tracing should be started
@@ -45,9 +50,21 @@
       return open();
     }
 
-    RequestId traceId = new RequestId();
-    traceIdConsumer.accept(RequestId.Type.TRACE_ID.name(), traceId.toString());
-    return open().addTag(RequestId.Type.TRACE_ID, traceId).forceLogging();
+    Optional<String> traceId =
+        LoggingContext.getInstance()
+            .getTagsAsMap()
+            .get(RequestId.Type.TRACE_ID.name())
+            .stream()
+            .findAny();
+    if (traceId.isPresent()) {
+      // request tracing was already started, no need to generate a new trace ID
+      traceIdConsumer.accept(RequestId.Type.TRACE_ID.name(), traceId.get());
+      return open();
+    }
+
+    RequestId newTraceId = new RequestId();
+    traceIdConsumer.accept(RequestId.Type.TRACE_ID.name(), newTraceId.toString());
+    return open().addTag(RequestId.Type.TRACE_ID, newTraceId).forceLogging();
   }
 
   @FunctionalInterface
diff --git a/javatests/com/google/gerrit/server/logging/TraceContextTest.java b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
index cd3ad4c..7969545 100644
--- a/javatests/com/google/gerrit/server/logging/TraceContextTest.java
+++ b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
@@ -178,6 +178,24 @@
     assertThat(traceIdConsumer.traceId).isNull();
   }
 
+  @Test
+  public void onlyOneTraceId() {
+    TestTraceIdConsumer traceIdConsumer1 = new TestTraceIdConsumer();
+    try (TraceContext traceContext1 = TraceContext.newTrace(true, traceIdConsumer1)) {
+      String expectedTraceId = traceIdConsumer1.traceId;
+      assertThat(expectedTraceId).isNotNull();
+
+      TestTraceIdConsumer traceIdConsumer2 = new TestTraceIdConsumer();
+      try (TraceContext traceContext2 = TraceContext.newTrace(true, traceIdConsumer2)) {
+        assertForceLogging(true);
+        assertTags(
+            ImmutableMap.of(RequestId.Type.TRACE_ID.name(), ImmutableSet.of(expectedTraceId)));
+      }
+      assertThat(traceIdConsumer2.tagName).isEqualTo(RequestId.Type.TRACE_ID.name());
+      assertThat(traceIdConsumer2.traceId).isEqualTo(expectedTraceId);
+    }
+  }
+
   private void assertTags(ImmutableMap<String, ImmutableSet<String>> expectedTagMap) {
     SortedMap<String, SortedSet<Object>> actualTagMap =
         LoggingContext.getInstance().getTags().asMap();