diff --git a/brave/brave5/build.gradle b/brave/brave5/build.gradle index 36f2b5886c2..eff6f826f3d 100644 --- a/brave/brave5/build.gradle +++ b/brave/brave5/build.gradle @@ -1,6 +1,7 @@ dependencies { api libs.brave5 api libs.brave5.instrumentation.http + optionalImplementation libs.brave5.instrumentation.rpc if (project.ext.targetJavaVersion >= 11) { testImplementation project(':thrift0.18') diff --git a/brave/brave6/build.gradle b/brave/brave6/build.gradle index cd50152bf1d..4a3fd59883c 100644 --- a/brave/brave6/build.gradle +++ b/brave/brave6/build.gradle @@ -1,6 +1,7 @@ dependencies { api libs.brave6 api libs.brave6.instrumentation.http + optionalImplementation libs.brave6.instrumentation.rpc if (project.ext.targetJavaVersion >= 11) { testImplementation project(':thrift0.18') diff --git a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaHttpServerParser.java b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaHttpServerParser.java index a43069b252c..1e05279f222 100644 --- a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaHttpServerParser.java +++ b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaHttpServerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 LINE Corporation + * Copyright 2025 LINE Corporation * * LINE Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance @@ -16,22 +16,33 @@ package com.linecorp.armeria.server.brave; -import com.linecorp.armeria.common.RpcRequest; -import com.linecorp.armeria.common.logging.RequestLog; -import com.linecorp.armeria.internal.common.brave.SpanTags; -import com.linecorp.armeria.server.ServiceRequestContext; - -import brave.SpanCustomizer; import brave.http.HttpRequestParser; -import brave.http.HttpResponse; import brave.http.HttpResponseParser; -import brave.propagation.TraceContext; /** - * Default implementation of {@link HttpRequestParser} and {@link HttpResponseParser} for servers. - * This parser adds some custom tags and overwrites the name of span if {@link RequestLog#requestContent()} - * is {@link RpcRequest}. - * The following tags become available: + * Provides Armeria's default implementation for server-side HTTP request and response parsers. + * Users may use the default parser like the following: + *
{@code
+ * Tracing tracing = ...
+ * HttpTracing httpTracing =
+ *   HttpTracing.newBuilder(tracing)
+ *              .serverRequestParser((req, ctx, span) -> {
+ *                  // apply brave's default request parser
+ *                  HttpRequestParser.DEFAULT.parse(req, ctx, span);
+ *                  // apply armeria's default request parser
+ *                  ArmeriaHttpServerParser.requestParser().parse(req, ctx, span);
+ *              })
+ *              .serverResponseParser((res, ctx, span) -> {
+ *                  // apply brave's default response parser
+ *                  HttpResponseParser.DEFAULT.parse(res, ctx, span);
+ *                  // apply armeria's default response parser
+ *                  ArmeriaHttpServerParser.responseParser().parse(res, ctx, span);
+ *              });
+ * BraveService
+ *   .newDecorator(httpTracing)
+ *   ...
+ * }
+ * The following tags will be available by default: * */ -final class ArmeriaHttpServerParser implements HttpRequestParser, HttpResponseParser { +public final class ArmeriaHttpServerParser { - private static final ArmeriaHttpServerParser INSTANCE = new ArmeriaHttpServerParser(); + private static final HttpRequestParser defaultRequestParser = ArmeriaServerParser::parseRequest; - static ArmeriaHttpServerParser get() { - return INSTANCE; - } + private static final HttpResponseParser defaultResponseParser = ArmeriaServerParser::parseResponse; - private ArmeriaHttpServerParser() { + /** + * Returns the default {@link HttpRequestParser}. + */ + public static HttpRequestParser requestParser() { + return defaultRequestParser; } - @Override - public void parse(brave.http.HttpRequest request, TraceContext context, SpanCustomizer span) { - HttpRequestParser.DEFAULT.parse(request, context, span); - - final Object unwrapped = request.unwrap(); - if (!(unwrapped instanceof ServiceRequestContext)) { - return; - } - - final ServiceRequestContext ctx = (ServiceRequestContext) unwrapped; - span.tag(SpanTags.TAG_HTTP_HOST, ctx.request().authority()) - .tag(SpanTags.TAG_HTTP_URL, ctx.request().uri().toString()) - .tag(SpanTags.TAG_HTTP_PROTOCOL, ctx.sessionProtocol().uriText()) - .tag(SpanTags.TAG_ADDRESS_REMOTE, ctx.remoteAddress().toString()) - .tag(SpanTags.TAG_ADDRESS_LOCAL, ctx.localAddress().toString()); + /** + * Returns the default {@link HttpResponseParser}. + */ + public static HttpResponseParser responseParser() { + return defaultResponseParser; } - @Override - public void parse(HttpResponse response, TraceContext context, SpanCustomizer span) { - HttpResponseParser.DEFAULT.parse(response, context, span); - - final Object res = response.unwrap(); - if (!(res instanceof ServiceRequestContext)) { - return; - } - - final ServiceRequestContext ctx = (ServiceRequestContext) res; - final RequestLog requestLog = ctx.log().ensureComplete(); - final String serFmt = ServiceRequestContextAdapter.serializationFormat(requestLog); - if (serFmt != null) { - span.tag(SpanTags.TAG_HTTP_SERIALIZATION_FORMAT, serFmt); - } - - final String name = requestLog.name(); - if (name != null) { - span.name(name); - } - } + private ArmeriaHttpServerParser() {} } diff --git a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaRpcServerParser.java b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaRpcServerParser.java new file mode 100644 index 00000000000..125252c94a3 --- /dev/null +++ b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaRpcServerParser.java @@ -0,0 +1,76 @@ +/* + * Copyright 2025 LINE Corporation + * + * LINE Corporation 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: + * + * https://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 com.linecorp.armeria.server.brave; + +import brave.rpc.RpcRequestParser; +import brave.rpc.RpcResponseParser; + +/** + * Provides Armeria's default implementation for server-side RPC request and response parsers. + * Users may use the default parser like the following: + *
{@code
+ * Tracing tracing = ...
+ * RpcTracing rpcTracing =
+ *   RpcTracing.newBuilder(tracing)
+ *              .serverRequestParser((req, ctx, span) -> {
+ *                  // apply brave's default request parser
+ *                  RpcRequestParser.DEFAULT.parse(req, ctx, span);
+ *                  // apply armeria's default request parser
+ *                  ArmeriaRpcServerParser.requestParser().parse(req, ctx, span);
+ *              })
+ *              .serverResponseParser((res, ctx, span) -> {
+ *                  // apply brave's default response parser
+ *                  RpcResponseParser.DEFAULT.parse(res, ctx, span);
+ *                  // apply armeria's default response parser
+ *                  ArmeriaRpcServerParser.responseParser().parse(res, ctx, span);
+ *              });
+ * BraveRpcService
+ *   .newDecorator(rpcTracing)
+ *   ...
+ * }
+ * The following tags will be available by default: + * + */ +public final class ArmeriaRpcServerParser { + + private static final RpcRequestParser defaultRequestParser = ArmeriaServerParser::parseRequest; + + private static final RpcResponseParser defaultResponseParser = ArmeriaServerParser::parseResponse; + + /** + * Returns the default {@link RpcRequestParser}. + */ + public static RpcRequestParser requestParser() { + return defaultRequestParser; + } + + /** + * Returns the default {@link RpcResponseParser}. + */ + public static RpcResponseParser responseParser() { + return defaultResponseParser; + } + + private ArmeriaRpcServerParser() {} +} diff --git a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaServerParser.java b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaServerParser.java new file mode 100644 index 00000000000..9048f53a660 --- /dev/null +++ b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ArmeriaServerParser.java @@ -0,0 +1,78 @@ +/* + * Copyright 2019 LINE Corporation + * + * LINE Corporation 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: + * + * https://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 com.linecorp.armeria.server.brave; + +import com.linecorp.armeria.common.logging.RequestLog; +import com.linecorp.armeria.internal.common.brave.SpanTags; +import com.linecorp.armeria.server.ServiceRequestContext; + +import brave.Request; +import brave.Response; +import brave.Span; +import brave.SpanCustomizer; +import brave.propagation.TraceContext; + +final class ArmeriaServerParser { + + private ArmeriaServerParser() { + } + + static void parseRequest(Request req, TraceContext context, SpanCustomizer span) { + final Object unwrapped = req.unwrap(); + if (!(unwrapped instanceof ServiceRequestContext)) { + return; + } + final ServiceRequestContext ctx = (ServiceRequestContext) unwrapped; + span.tag(SpanTags.TAG_HTTP_HOST, ctx.request().authority()) + .tag(SpanTags.TAG_HTTP_URL, ctx.request().uri().toString()) + .tag(SpanTags.TAG_HTTP_PROTOCOL, ctx.sessionProtocol().uriText()) + .tag(SpanTags.TAG_ADDRESS_REMOTE, ctx.remoteAddress().toString()) + .tag(SpanTags.TAG_ADDRESS_LOCAL, ctx.localAddress().toString()); + } + + static void parseResponse(Response res, TraceContext context, SpanCustomizer span) { + final Object unwrapped = res.unwrap(); + if (!(unwrapped instanceof ServiceRequestContext)) { + return; + } + final ServiceRequestContext ctx = (ServiceRequestContext) unwrapped; + final RequestLog requestLog = ctx.log().ensureComplete(); + final String serFmt = ServiceRequestContextAdapter.serializationFormat(requestLog); + if (serFmt != null) { + span.tag(SpanTags.TAG_HTTP_SERIALIZATION_FORMAT, serFmt); + } + + final String name = requestLog.name(); + if (name != null) { + span.name(name); + } + } + + static void annotateWireSpan(RequestLog log, Span span) { + span.start(log.requestStartTimeMicros()); + final Long wireReceiveTimeNanos = log.requestFirstBytesTransferredTimeNanos(); + assert wireReceiveTimeNanos != null; + SpanTags.logWireReceive(span, wireReceiveTimeNanos, log); + + final Long wireSendTimeNanos = log.responseFirstBytesTransferredTimeNanos(); + if (wireSendTimeNanos != null) { + SpanTags.logWireSend(span, wireSendTimeNanos, log); + } else { + // If the client timed-out the request, we will have never sent any response data at all. + } + } +} diff --git a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/BraveRpcService.java b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/BraveRpcService.java new file mode 100644 index 00000000000..d5c510ac6f8 --- /dev/null +++ b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/BraveRpcService.java @@ -0,0 +1,128 @@ +/* + * Copyright 2025 LINE Corporation + * + * LINE Corporation 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: + * + * https://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 com.linecorp.armeria.server.brave; + +import static com.linecorp.armeria.internal.common.brave.TraceContextUtil.ensureScopeUsesRequestContext; +import static com.linecorp.armeria.server.brave.ArmeriaServerParser.annotateWireSpan; +import static com.linecorp.armeria.server.brave.BraveService.SERVICE_REQUEST_DECORATING_SCOPE; + +import java.util.function.Function; + +import com.linecorp.armeria.common.RpcRequest; +import com.linecorp.armeria.common.RpcResponse; +import com.linecorp.armeria.common.brave.RequestContextCurrentTraceContext; +import com.linecorp.armeria.internal.common.RequestContextExtension; +import com.linecorp.armeria.server.RpcService; +import com.linecorp.armeria.server.ServiceRequestContext; +import com.linecorp.armeria.server.SimpleDecoratingRpcService; +import com.linecorp.armeria.server.TransientServiceOption; + +import brave.Span; +import brave.Tracer; +import brave.Tracer.SpanInScope; +import brave.Tracing; +import brave.rpc.RpcRequestParser; +import brave.rpc.RpcResponseParser; +import brave.rpc.RpcServerHandler; +import brave.rpc.RpcServerRequest; +import brave.rpc.RpcServerResponse; +import brave.rpc.RpcTracing; + +/** + * Decorates an {@link RpcService} to trace inbound {@link RpcRequest}s using + * Brave. + */ +public final class BraveRpcService extends SimpleDecoratingRpcService { + + private static final RpcRequestParser defaultRequestParser = (request, context, span) -> { + RpcRequestParser.DEFAULT.parse(request, context, span); + ArmeriaRpcServerParser.requestParser().parse(request, context, span); + }; + + private static final RpcResponseParser defaultResponseParser = (response, context, span) -> { + RpcResponseParser.DEFAULT.parse(response, context, span); + ArmeriaRpcServerParser.responseParser().parse(response, context, span); + }; + + /** + * Creates a new tracing {@link RpcService} decorator using the specified {@link Tracing} instance. + */ + public static Function + newDecorator(Tracing tracing) { + return newDecorator(RpcTracing.newBuilder(tracing) + .serverRequestParser(defaultRequestParser) + .serverResponseParser(defaultResponseParser) + .build()); + } + + /** + * Creates a new tracing {@link RpcService} decorator using the specified {@link RpcTracing} instance. + */ + public static Function + newDecorator(RpcTracing rpcTracing) { + ensureScopeUsesRequestContext(rpcTracing.tracing()); + return service -> new BraveRpcService(service, rpcTracing); + } + + private final Tracer tracer; + private final RpcServerHandler handler; + private final RequestContextCurrentTraceContext currentTraceContext; + + private BraveRpcService(RpcService delegate, RpcTracing rpcTracing) { + super(delegate); + final Tracing tracing = rpcTracing.tracing(); + tracer = tracing.tracer(); + handler = RpcServerHandler.create(rpcTracing); + currentTraceContext = (RequestContextCurrentTraceContext) tracing.currentTraceContext(); + } + + @Override + public RpcResponse serve(ServiceRequestContext ctx, RpcRequest req) throws Exception { + if (!ctx.config().transientServiceOptions().contains(TransientServiceOption.WITH_TRACING)) { + return unwrap().serve(ctx, req); + } + + final RpcServerRequest braveReq = ServiceRequestContextAdapter.asRpcServerRequest(ctx); + final Span span = handler.handleReceive(braveReq); + + final RequestContextExtension ctxExtension = ctx.as(RequestContextExtension.class); + if (currentTraceContext.scopeDecoratorAdded() && !span.isNoop() && ctxExtension != null) { + // Run the scope decorators when the ctx is pushed to the thread local. + ctxExtension.hook(() -> currentTraceContext.decorateScope(span.context(), + SERVICE_REQUEST_DECORATING_SCOPE)); + } + + maybeAddTagsToSpan(ctx, braveReq, span); + try (SpanInScope ignored = tracer.withSpanInScope(span)) { + return unwrap().serve(ctx, req); + } + } + + private void maybeAddTagsToSpan(ServiceRequestContext ctx, RpcServerRequest braveReq, Span span) { + if (span.isNoop()) { + // For no-op spans, nothing special to do. + return; + } + + ctx.log().whenComplete().thenAccept(log -> { + annotateWireSpan(log, span); + final RpcServerResponse braveRes = + ServiceRequestContextAdapter.asRpcServerResponse(ctx, log, braveReq); + handler.handleSend(braveRes, span); + }); + } +} diff --git a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/BraveService.java b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/BraveService.java index 2fbd4d905d3..77e0c5617a7 100644 --- a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/BraveService.java +++ b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/BraveService.java @@ -17,14 +17,16 @@ package com.linecorp.armeria.server.brave; import static com.linecorp.armeria.internal.common.brave.TraceContextUtil.ensureScopeUsesRequestContext; +import static com.linecorp.armeria.server.brave.ArmeriaServerParser.annotateWireSpan; import java.util.function.Function; +import com.google.common.annotations.VisibleForTesting; + import com.linecorp.armeria.common.HttpRequest; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.brave.RequestContextCurrentTraceContext; import com.linecorp.armeria.internal.common.RequestContextExtension; -import com.linecorp.armeria.internal.common.brave.SpanTags; import com.linecorp.armeria.server.HttpService; import com.linecorp.armeria.server.ServiceRequestContext; import com.linecorp.armeria.server.SimpleDecoratingHttpService; @@ -34,6 +36,8 @@ import brave.Tracer; import brave.Tracer.SpanInScope; import brave.Tracing; +import brave.http.HttpRequestParser; +import brave.http.HttpResponseParser; import brave.http.HttpServerHandler; import brave.http.HttpServerRequest; import brave.http.HttpServerResponse; @@ -46,7 +50,19 @@ */ public final class BraveService extends SimpleDecoratingHttpService { - private static final Scope SERVICE_REQUEST_DECORATING_SCOPE = new Scope() { + @VisibleForTesting + static final HttpRequestParser defaultRequestParser = (request, context, span) -> { + HttpRequestParser.DEFAULT.parse(request, context, span); + ArmeriaHttpServerParser.requestParser().parse(request, context, span); + }; + + @VisibleForTesting + static final HttpResponseParser defaultResponseParser = (response, context, span) -> { + HttpResponseParser.DEFAULT.parse(response, context, span); + ArmeriaHttpServerParser.responseParser().parse(response, context, span); + }; + + static final Scope SERVICE_REQUEST_DECORATING_SCOPE = new Scope() { @Override public void close() {} @@ -62,8 +78,8 @@ public String toString() { public static Function newDecorator(Tracing tracing) { return newDecorator(HttpTracing.newBuilder(tracing) - .serverRequestParser(ArmeriaHttpServerParser.get()) - .serverResponseParser(ArmeriaHttpServerParser.get()) + .serverRequestParser(defaultRequestParser) + .serverResponseParser(defaultResponseParser) .build()); } @@ -120,19 +136,7 @@ private void maybeAddTagsToSpan(ServiceRequestContext ctx, HttpServerRequest bra } ctx.log().whenComplete().thenAccept(log -> { - span.start(log.requestStartTimeMicros()); - - final Long wireReceiveTimeNanos = log.requestFirstBytesTransferredTimeNanos(); - assert wireReceiveTimeNanos != null; - SpanTags.logWireReceive(span, wireReceiveTimeNanos, log); - - final Long wireSendTimeNanos = log.responseFirstBytesTransferredTimeNanos(); - if (wireSendTimeNanos != null) { - SpanTags.logWireSend(span, wireSendTimeNanos, log); - } else { - // If the client timed-out the request, we will have never sent any response data at all. - } - + annotateWireSpan(log, span); final HttpServerResponse braveRes = ServiceRequestContextAdapter.asHttpServerResponse(log, braveReq); handler.handleSend(braveRes, span); diff --git a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ServiceRequestContextAdapter.java b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ServiceRequestContextAdapter.java index 6381712bcb4..57925cc411b 100644 --- a/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ServiceRequestContextAdapter.java +++ b/brave/brave6/src/main/java/com/linecorp/armeria/server/brave/ServiceRequestContextAdapter.java @@ -17,6 +17,8 @@ package com.linecorp.armeria.server.brave; import com.linecorp.armeria.common.RequestContext; +import com.linecorp.armeria.common.RpcRequest; +import com.linecorp.armeria.common.RpcResponse; import com.linecorp.armeria.common.SerializationFormat; import com.linecorp.armeria.common.annotation.Nullable; import com.linecorp.armeria.common.logging.RequestLog; @@ -27,6 +29,8 @@ import brave.Span; import brave.http.HttpServerHandler; +import brave.rpc.RpcServerRequest; +import brave.rpc.RpcServerResponse; /** * Wraps {@link ServiceRequestContext} in an {@link brave.http.HttpServerRequest}, for use in @@ -147,5 +151,104 @@ static String serializationFormat(RequestLog requestLog) { return serFmt == SerializationFormat.NONE ? null : serFmt.uriText(); } + static RpcServerRequest asRpcServerRequest(ServiceRequestContext ctx) { + return new ArmeriaRpcServerRequest(ctx); + } + + private static class ArmeriaRpcServerRequest extends RpcServerRequest { + + private final ServiceRequestContext ctx; + + ArmeriaRpcServerRequest(ServiceRequestContext ctx) { + this.ctx = ctx; + } + + @Override + public long startTimestamp() { + return ctx.log().ensureAvailable(RequestLogProperty.REQUEST_START_TIME).requestStartTimeMicros(); + } + + @Nullable + @Override + public String method() { + final RpcRequest rpcRequest = ctx.rpcRequest(); + if (rpcRequest == null) { + return null; + } + return rpcRequest.method(); + } + + @Nullable + @Override + public String service() { + final RpcRequest rpcRequest = ctx.rpcRequest(); + if (rpcRequest == null) { + return null; + } + return rpcRequest.serviceName(); + } + + @Nullable + @Override + protected String propagationField(String keyName) { + return ctx.request().headers().get(keyName); + } + + @Override + public Object unwrap() { + return ctx; + } + } + + static RpcServerResponse asRpcServerResponse(ServiceRequestContext ctx, RequestLog log, + RpcServerRequest braveReq) { + return new ArmeriaRpcServerResponse(ctx, log, braveReq); + } + + private static class ArmeriaRpcServerResponse extends RpcServerResponse { + + private final ServiceRequestContext ctx; + private final RequestLog log; + private final RpcServerRequest braveReq; + + ArmeriaRpcServerResponse(ServiceRequestContext ctx, RequestLog log, RpcServerRequest braveReq) { + this.ctx = ctx; + this.log = log; + this.braveReq = braveReq; + } + + @Override + public brave.rpc.RpcRequest request() { + return braveReq; + } + + @Nullable + @Override + public String errorCode() { + return null; + } + + @Nullable + @Override + public Throwable error() { + final Object content = log.responseContent(); + if (!(content instanceof RpcResponse)) { + return null; + } + final RpcResponse response = (RpcResponse) content; + return response.cause(); + } + + @Override + public long finishTimestamp() { + return SpanContextUtil.wallTimeMicros(log, log.responseEndTimeNanos()); + } + + @Override + public Object unwrap() { + return ctx; + } + } + private ServiceRequestContextAdapter() {} } diff --git a/brave/brave6/src/test/java/com/linecorp/armeria/it/brave/BraveIntegrationTest.java b/brave/brave6/src/test/java/com/linecorp/armeria/it/brave/BraveIntegrationTest.java index 1b9efe9c341..358e663b0c7 100644 --- a/brave/brave6/src/test/java/com/linecorp/armeria/it/brave/BraveIntegrationTest.java +++ b/brave/brave6/src/test/java/com/linecorp/armeria/it/brave/BraveIntegrationTest.java @@ -22,6 +22,7 @@ import static com.linecorp.armeria.common.SessionProtocol.H1C; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.awaitility.Awaitility.await; import java.time.Duration; import java.util.ArrayList; @@ -40,10 +41,12 @@ import org.apache.thrift.async.AsyncMethodCallback; import org.apache.thrift.transport.TTransportException; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; @@ -56,11 +59,14 @@ import com.linecorp.armeria.client.WebClient; import com.linecorp.armeria.client.brave.BraveClient; import com.linecorp.armeria.client.thrift.ThriftClients; +import com.linecorp.armeria.common.AggregatedHttpResponse; import com.linecorp.armeria.common.HttpRequest; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.HttpStatus; import com.linecorp.armeria.common.MediaType; import com.linecorp.armeria.common.RequestContext; +import com.linecorp.armeria.common.RpcRequest; +import com.linecorp.armeria.common.RpcResponse; import com.linecorp.armeria.common.brave.RequestContextCurrentTraceContext; import com.linecorp.armeria.common.thrift.ThriftFuture; import com.linecorp.armeria.common.util.ThreadFactories; @@ -68,8 +74,11 @@ import com.linecorp.armeria.internal.testing.GenerateNativeImageTrace; import com.linecorp.armeria.server.AbstractHttpService; import com.linecorp.armeria.server.HttpService; +import com.linecorp.armeria.server.RpcService; import com.linecorp.armeria.server.ServerBuilder; import com.linecorp.armeria.server.ServiceRequestContext; +import com.linecorp.armeria.server.SimpleDecoratingRpcService; +import com.linecorp.armeria.server.brave.BraveRpcService; import com.linecorp.armeria.server.brave.BraveService; import com.linecorp.armeria.server.thrift.THttpService; import com.linecorp.armeria.testing.junit5.server.ServerExtension; @@ -90,18 +99,10 @@ @GenerateNativeImageTrace class BraveIntegrationTest { + private static final String CLIENT_TYPE_HEADER = "x-client-type"; + private static final String TIMEOUT_HEADER = "x-timeout"; private static final SpanHandlerImpl spanHandler = new SpanHandlerImpl(); - private static TestService.Iface fooClient; - private static TestService.Iface fooClientWithoutTracing; - private static TestService.Iface timeoutClient; - private static TestService.Iface timeoutClientClientTimesOut; - private static TestService.Iface http1TimeoutClientClientTimesOut; - private static TestService.AsyncIface barClient; - private static TestService.AsyncIface quxClient; - private static TestService.Iface zipClient; - private static WebClient poolWebClient; - @RegisterExtension static ServerExtension server = new ServerExtension(true) { @Override @@ -110,33 +111,31 @@ protected void configure(ServerBuilder sb) throws Exception { // that a client cancels a request before a server receives it. sb.requestTimeout(Duration.ofSeconds(10)); - sb.service("/foo", decorate("service/foo", THttpService.of( - (AsyncIface) (name, resultHandler) -> - barClient.hello("Miss. " + name, new DelegatingCallback(resultHandler))))); - - sb.service("/bar", decorate("service/bar", THttpService.of( - (AsyncIface) (name, resultHandler) -> { - if (name.startsWith("Miss. ")) { - name = "Ms. " + name.substring(6); - } - quxClient.hello(name, new DelegatingCallback(resultHandler)); - }))); - - sb.service("/zip", decorate("service/zip", THttpService.of( - (AsyncIface) (name, resultHandler) -> { - final ThriftFuture f1 = new ThriftFuture<>(); - final ThriftFuture f2 = new ThriftFuture<>(); - quxClient.hello(name, f1); - quxClient.hello(name, f2); - CompletableFuture.allOf(f1, f2).whenCompleteAsync((aVoid, throwable) -> { - resultHandler.onComplete(f1.getNow(null) + ", and " + f2.getNow(null)); - }, RequestContext.current().eventLoop()); - }))); - - sb.service("/qux", decorate("service/qux", THttpService.of( - (AsyncIface) (name, resultHandler) -> resultHandler.onComplete("Hello, " + name + '!')))); - - sb.service("/pool", decorate("service/pool", new AbstractHttpService() { + sb.service("/foo", tHttpDecorate("service/foo", (name, resultHandler) -> + newClient("/bar").hello("Miss. " + name, new DelegatingCallback(resultHandler)))); + + sb.service("/bar", tHttpDecorate("service/bar", (name, resultHandler) -> { + if (name.startsWith("Miss. ")) { + name = "Ms. " + name.substring(6); + } + newClient("/qux").hello(name, new DelegatingCallback(resultHandler)); + })); + + sb.service("/zip", tHttpDecorate("service/zip", (name, resultHandler) -> { + final ThriftFuture f1 = new ThriftFuture<>(); + final ThriftFuture f2 = new ThriftFuture<>(); + newClient("/qux").hello(name, f1); + newClient("/qux").hello(name, f2); + CompletableFuture.allOf(f1, f2).whenCompleteAsync((aVoid, throwable) -> { + resultHandler.onComplete(f1.getNow(null) + ", and " + f2.getNow(null)); + }, RequestContext.current().eventLoop()); + })); + + sb.service("/qux", tHttpDecorate("service/qux", (name, resultHandler) -> + resultHandler.onComplete("Hello, " + name + '!'))); + + final Tracing servicePoolTracing = newTracing("service/pool"); + sb.service("/pool", httpDecorate(servicePoolTracing, new AbstractHttpService() { @Override protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req) throws Exception { @@ -151,7 +150,7 @@ protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req) countDownLatch.countDown(); countDownLatch.await(); } - final Span span = Tracing.currentTracer().nextSpan().start(); + final Span span = servicePoolTracing.tracer().nextSpan().start(); try (SpanInScope unused = Tracing.currentTracer().withSpanInScope(span)) { if (i == 1) { @@ -172,8 +171,8 @@ protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req) result -> allAsList(IntStream.range(1, 3).mapToObj( i -> executorService.submit( RequestContext.current().makeContextAware(() -> { - final ScopedSpan span = Tracing.currentTracer() - .startScopedSpan("aloha"); + final ScopedSpan span = servicePoolTracing + .tracer().startScopedSpan("aloha"); try { return null; } finally { @@ -191,64 +190,55 @@ protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req) } })); - sb.service("/timeout", decorate("service/timeout", THttpService.of( - // This service never calls the handler and will timeout. - (AsyncIface) (name, resultHandler) -> { - }))); + sb.service("/timeout", + tHttpDecorate("service/timeout", + // This service never calls the handler and will timeout. + (name, resultHandler) -> { + final ServiceRequestContext ctx = ServiceRequestContext.current(); + if (ctx.request().headers().contains(TIMEOUT_HEADER)) { + ctx.timeoutNow(); + } + })); sb.service("/http", (req, ctx) -> HttpResponse.of(HttpStatus.OK)); } }; - @BeforeEach - void setupClients() { - fooClient = ThriftClients.builder(server.httpUri()) - .path("/foo") - .decorator(BraveClient.newDecorator(newTracing("client/foo"))) - .build(TestService.Iface.class); - zipClient = ThriftClients.builder(server.httpUri()) - .path("/zip") - .decorator(BraveClient.newDecorator(newTracing("client/zip"))) - .build(TestService.Iface.class); - fooClientWithoutTracing = ThriftClients.newClient(server.httpUri() + "/foo", TestService.Iface.class); - barClient = newClient("/bar"); - quxClient = newClient("/qux"); - poolWebClient = WebClient.of(server.httpUri()); - timeoutClient = ThriftClients.builder(server.httpUri()) - .path("/timeout") - .decorator(BraveClient.newDecorator(newTracing("client/timeout"))) - .build(TestService.Iface.class); - timeoutClientClientTimesOut = - ThriftClients.builder(server.httpUri()) - .path("/timeout") - .decorator(BraveClient.newDecorator(newTracing("client/timeout"))) - .responseTimeout(Duration.ofSeconds(3)) - .build(TestService.Iface.class); - http1TimeoutClientClientTimesOut = - ThriftClients.builder(server.uri(H1C)) - .path("/timeout") - .decorator(BraveClient.newDecorator(newTracing("client/timeout"))) - .responseTimeout(Duration.ofSeconds(3)) - .build(TestService.Iface.class); + @AfterEach + void afterEach() { + assertThat(spanHandler.spans).isEmpty(); } - @AfterEach - void tearDown() { + @AfterAll + static void afterAll() throws Exception { Tracing.current().close(); } - @AfterEach - void shouldHaveNoExtraSpans() { - assertThat(spanHandler.spans).isEmpty(); + private static HttpService tHttpDecorate(String name, AsyncIface asyncIface) { + final THttpService service = + THttpService.builder() + .addService(asyncIface) + .decorate(delegate -> new HeaderBasedBraveRpcService(delegate, name)) + .build(); + return (ctx, req) -> { + final String braveServiceType = ctx.request().headers().get(CLIENT_TYPE_HEADER); + if ("http".equals(braveServiceType)) { + return BraveService.newDecorator(newTracing(name)).apply(service).serve(ctx, req); + } + return service.serve(ctx, req); + }; } - private static BraveService decorate(String name, HttpService service) { - return BraveService.newDecorator(newTracing(name)).apply(service); + private static HttpService httpDecorate(Tracing tracing, HttpService service) { + return BraveService.newDecorator(tracing).apply(service); } private static TestService.AsyncIface newClient(String path) { + final ServiceRequestContext ctx = ServiceRequestContext.current(); + final String braveServiceType = ctx.request().headers().get(CLIENT_TYPE_HEADER); return ThriftClients.builder(server.httpUri()) .path(path) + .addHeader(CLIENT_TYPE_HEADER, braveServiceType) .decorator(BraveClient.newDecorator(newTracing("client" + path))) .build(TestService.AsyncIface.class); } @@ -299,8 +289,15 @@ void testTimingAnnotations() { } } - @Test - void testServiceHasMultipleClientRequests() throws Exception { + @ParameterizedTest + @ValueSource(strings = {"http", "rpc"}) + void testServiceHasMultipleClientRequests(String type) throws Exception { + final TestService.Iface zipClient = + ThriftClients.builder(server.httpUri()) + .addHeader(CLIENT_TYPE_HEADER, type) + .path("/zip") + .decorator(BraveClient.newDecorator(newTracing("client/zip"))) + .build(TestService.Iface.class); assertThat(zipClient.hello("Lee")).isEqualTo("Hello, Lee!, and Hello, Lee!"); final MutableSpan[] spans = spanHandler.take(6); @@ -308,8 +305,15 @@ void testServiceHasMultipleClientRequests() throws Exception { assertThat(spans).allMatch(s -> s.traceId().equals(traceId)); } - @Test - void testClientInitiatedTrace() throws Exception { + @ParameterizedTest + @ValueSource(strings = {"http", "rpc"}) + void testClientInitiatedTrace(String type) throws Exception { + final TestService.Iface fooClient = + ThriftClients.builder(server.httpUri()) + .path("/foo") + .addHeader(CLIENT_TYPE_HEADER, type) + .decorator(BraveClient.newDecorator(newTracing("client/foo"))) + .build(TestService.Iface.class); assertThat(fooClient.hello("Lee")).isEqualTo("Hello, Ms. Lee!"); final MutableSpan[] spans = spanHandler.take(6); @@ -397,8 +401,13 @@ void testClientInitiatedTrace() throws Exception { assertThat(serverEndTime).isGreaterThanOrEqualTo(serverWireSendTime); } - @Test - void testServiceInitiatedTrace() throws Exception { + @ParameterizedTest + @ValueSource(strings = {"http", "rpc"}) + void testServiceInitiatedTrace(String type) throws Exception { + final TestService.Iface fooClientWithoutTracing = + ThriftClients.builder(server.httpUri() + "/foo") + .addHeader(CLIENT_TYPE_HEADER, type) + .build(TestService.Iface.class); assertThat(fooClientWithoutTracing.hello("Lee")).isEqualTo("Hello, Ms. Lee!"); final MutableSpan[] spans = spanHandler.take(5); @@ -441,7 +450,9 @@ void testServiceInitiatedTrace() throws Exception { @Test void testSpanInThreadPoolHasSameTraceId() throws Exception { - poolWebClient.get("pool").aggregate().get(); + final AggregatedHttpResponse res = server.blockingWebClient().get("pool"); + assertThat(res.contentUtf8()).isEqualTo("Lee"); + await().untilAsserted(() -> assertThat(spanHandler.spans).hasSize(5)); final MutableSpan[] spans = spanHandler.take(5); assertThat(Arrays.stream(spans).map(MutableSpan::traceId).collect(toImmutableSet())).hasSize(1); assertThat(Arrays.stream(spans).map(MutableSpan::parentId) @@ -449,29 +460,57 @@ void testSpanInThreadPoolHasSameTraceId() throws Exception { .collect(toImmutableSet())).hasSize(1); } - @Test - void testServerTimesOut() throws Exception { - assertThatThrownBy(() -> timeoutClient.hello("name")) - .isInstanceOf(TTransportException.class) - .hasCauseInstanceOf(InvalidResponseHeadersException.class); - final MutableSpan[] spans = spanHandler.take(2); - - final MutableSpan serverSpan = findSpan(spans, "service/timeout"); - final MutableSpan clientSpan = findSpan(spans, "client/timeout"); - - // Server timed out meaning it did still send a timeout response to the client and we have all - // annotations. - assertThat(serverSpan.annotations()).hasSize(2); - assertThat(clientSpan.annotations()).hasSize(2); + @ParameterizedTest + @ValueSource(strings = {"http", "rpc"}) + void testServerTimesOut(String type) throws Exception { + try (ClientFactory cf = ClientFactory.builder().build()) { + final TestService.Iface timeoutClient = + ThriftClients.builder(server.httpUri()) + .path("/timeout") + .factory(cf) + .addHeader(CLIENT_TYPE_HEADER, type) + .addHeader(TIMEOUT_HEADER, true) + .decorator(BraveClient.newDecorator(newTracing("client/timeout"))) + .build(TestService.Iface.class); + assertThatThrownBy(() -> timeoutClient.hello("name")) + .isInstanceOf(TTransportException.class) + .hasCauseInstanceOf(InvalidResponseHeadersException.class); + final MutableSpan[] spans = spanHandler.take(2); + + final MutableSpan serverSpan = findSpan(spans, "service/timeout"); + final MutableSpan clientSpan = findSpan(spans, "client/timeout"); + + // Server timed out meaning it did still send a timeout response to the client and we have all + // annotations. A separate client factory is used to guarantee that client span annotations + // always contain connection related extra annotations. + assertThat(serverSpan.annotations()).hasSize(2); + assertThat(clientSpan.annotations()).hasSize(6); + } } - @Test - void testHttp2ClientTimesOut() throws Exception { + @ParameterizedTest + @ValueSource(strings = {"http", "rpc"}) + void testHttp2ClientTimesOut(String type) throws Exception { + final TestService.Iface timeoutClientClientTimesOut = + ThriftClients.builder(server.httpUri()) + .path("/timeout") + .addHeader(CLIENT_TYPE_HEADER, type) + .decorator(BraveClient.newDecorator(newTracing("client/timeout"))) + .responseTimeout(Duration.ofSeconds(1)) + .build(TestService.Iface.class); testClientTimesOut(timeoutClientClientTimesOut); } - @Test - void testHttp1ClientTimesOut() throws Exception { + @ParameterizedTest + @ValueSource(strings = {"http", "rpc"}) + void testHttp1ClientTimesOut(String type) throws Exception { + final TestService.Iface http1TimeoutClientClientTimesOut = + ThriftClients.builder(server.uri(H1C)) + .path("/timeout") + .addHeader(CLIENT_TYPE_HEADER, type) + .decorator(BraveClient.newDecorator(newTracing("client/timeout"))) + .responseTimeout(Duration.ofSeconds(3)) + .build(TestService.Iface.class); testClientTimesOut(http1TimeoutClientClientTimesOut); } @@ -566,8 +605,8 @@ public void onError(Exception exception) { } } - private static class SpanHandlerImpl extends SpanHandler { - private final BlockingQueue spans = new LinkedBlockingQueue<>(); + static final class SpanHandlerImpl extends SpanHandler { + final BlockingQueue spans = new LinkedBlockingQueue<>(); @Override public boolean end(TraceContext context, MutableSpan span, Cause cause) { @@ -585,4 +624,26 @@ MutableSpan[] take(int numSpans) { return taken.toArray(new MutableSpan[numSpans]); } } + + private static class HeaderBasedBraveRpcService extends SimpleDecoratingRpcService { + + private final RpcService delegate; + private final String name; + + HeaderBasedBraveRpcService(RpcService delegate, String name) { + super(delegate); + this.delegate = delegate; + this.name = name; + } + + @Override + public RpcResponse serve(ServiceRequestContext ctx, + RpcRequest req) throws Exception { + final String braveServiceType = ctx.request().headers().get(CLIENT_TYPE_HEADER); + if ("rpc".equals(braveServiceType)) { + return BraveRpcService.newDecorator(newTracing(name)).apply(delegate).serve(ctx, req); + } + return delegate.serve(ctx, req); + } + } } diff --git a/brave/brave6/src/test/java/com/linecorp/armeria/server/brave/BraveRpcServiceTest.java b/brave/brave6/src/test/java/com/linecorp/armeria/server/brave/BraveRpcServiceTest.java new file mode 100644 index 00000000000..283a96d0a34 --- /dev/null +++ b/brave/brave6/src/test/java/com/linecorp/armeria/server/brave/BraveRpcServiceTest.java @@ -0,0 +1,160 @@ +/* + * Copyright 2025 LINE Corporation + * + * LINE Corporation 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: + * + * https://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 com.linecorp.armeria.server.brave; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.google.common.collect.ImmutableMap; + +import com.linecorp.armeria.client.thrift.ThriftClients; +import com.linecorp.armeria.common.brave.RequestContextCurrentTraceContext; +import com.linecorp.armeria.common.brave.TestSpanCollector; +import com.linecorp.armeria.common.logging.RequestLog; +import com.linecorp.armeria.internal.common.brave.SpanTags; +import com.linecorp.armeria.server.ServerBuilder; +import com.linecorp.armeria.server.ServiceRequestContext; +import com.linecorp.armeria.server.thrift.THttpService; +import com.linecorp.armeria.testing.junit5.server.ServerExtension; + +import brave.Tracing; +import brave.handler.MutableSpan; +import brave.rpc.RpcRequestParser; +import brave.rpc.RpcResponseParser; +import brave.rpc.RpcTags; +import brave.rpc.RpcTracing; +import testing.brave.TestService; +import testing.brave.TestService.Iface; + +class BraveRpcServiceTest { + + private static final String SAMPLE_HEADER = "x-should-sample"; + private static final TestSpanCollector spanHandler = new TestSpanCollector(); + private static final Tracing tracing = + Tracing.newBuilder() + .currentTraceContext(RequestContextCurrentTraceContext.ofDefault()) + .addSpanHandler(spanHandler) + .build(); + + @RegisterExtension + static final ServerExtension server = new ServerExtension() { + @Override + protected void configure(ServerBuilder sb) throws Exception { + final RpcTracing defaultParserTracing = + RpcTracing.newBuilder(tracing) + .serverRequestParser((request, context, span) -> { + RpcRequestParser.DEFAULT.parse(request, context, span); + ArmeriaRpcServerParser.requestParser().parse(request, context, span); + }) + .serverResponseParser((response, context, span) -> { + RpcResponseParser.DEFAULT.parse(response, context, span); + ArmeriaRpcServerParser.responseParser().parse(response, context, span); + }) + .serverSampler(req -> { + final ServiceRequestContext ctx = (ServiceRequestContext) req.unwrap(); + return ctx.request().headers().contains(SAMPLE_HEADER); + }) + .build(); + + sb.service("/default-parser", + THttpService.builder() + .decorate(BraveRpcService.newDecorator(defaultParserTracing)) + .addService((Iface) name -> "world") + .build()); + + final RpcTracing braveParserTracing = RpcTracing.newBuilder(tracing).build(); + sb.service("/brave-parser", + THttpService.builder() + .decorate(BraveRpcService.newDecorator(braveParserTracing)) + .addService((Iface) name -> "world") + .build()); + } + }; + + @AfterEach + void afterEach() { + assertThat(spanHandler.spans()).isEmpty(); + } + + @AfterAll + static void afterAll() throws Exception { + tracing.close(); + } + + @Test + void rpcSampling() throws Exception { + final TestService.Iface samplingIface = + ThriftClients.builder(server.httpUri().resolve("/default-parser")) + .addHeader(SAMPLE_HEADER, true) + .build(Iface.class); + assertThat(samplingIface.hello("/")).isEqualTo("world"); + await().untilAsserted(() -> assertThat(spanHandler.spans()).hasSize(1)); + final MutableSpan span = spanHandler.spans().take(); + assertThat(span.tag(RpcTags.SERVICE.key())).isEqualTo("testing.brave.TestService$Iface"); + assertThat(span.tag(RpcTags.METHOD.key())).isEqualTo("hello"); + } + + @Test + void rpcNoSampling() throws Exception { + final TestService.Iface iface = + ThriftClients.newClient(server.httpUri().resolve("/default-parser"), Iface.class); + assertThat(iface.hello("/")).isEqualTo("world"); + await().pollDelay(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(spanHandler.spans()).isEmpty()); + } + + @ParameterizedTest + @ValueSource(strings = {"/default-parser", "/brave-parser"}) + void parserBehavior(String path) throws Exception { + final TestService.Iface iface = + ThriftClients.builder(server.httpUri().resolve(path)) + .addHeader(SAMPLE_HEADER, true) + .build(Iface.class); + assertThat(iface.hello("/")).isEqualTo("world"); + + await().untilAsserted(() -> assertThat(spanHandler.spans()).hasSize(1)); + final MutableSpan span = spanHandler.spans().take(); + final ServiceRequestContext sctx = server.requestContextCaptor().poll(); + final RequestLog slog = sctx.log().whenComplete().join(); + + // wire annotations are recorded + assertThat(span.startTimestamp()).isEqualTo(slog.requestStartTimeMicros()); + final Map annotations = ImmutableMap.copyOf(span.annotations()); + assertThat(annotations).containsValues("wr", "ws"); + + // brave default tags are recorded + assertThat(span.tag(RpcTags.SERVICE.key())).isEqualTo("testing.brave.TestService$Iface"); + assertThat(span.tag(RpcTags.METHOD.key())).isEqualTo("hello"); + + if ("/brave-parser".equals(path)) { + // armeria default tags are not recorded + assertThat(span.tags()).doesNotContainKey(SpanTags.TAG_HTTP_SERIALIZATION_FORMAT); + } else { + // armeria default tags are recorded + assertThat(span.tag(SpanTags.TAG_HTTP_SERIALIZATION_FORMAT)).isEqualTo("tbinary"); + } + } +} diff --git a/brave/brave6/src/test/java/com/linecorp/armeria/server/brave/BraveServiceTest.java b/brave/brave6/src/test/java/com/linecorp/armeria/server/brave/BraveServiceTest.java index 505e6b7be10..989644adc75 100644 --- a/brave/brave6/src/test/java/com/linecorp/armeria/server/brave/BraveServiceTest.java +++ b/brave/brave6/src/test/java/com/linecorp/armeria/server/brave/BraveServiceTest.java @@ -215,8 +215,8 @@ private static RequestLog testServiceInvocation(SpanHandler spanHandler, .build(); final HttpTracing httpTracing = HttpTracing.newBuilder(tracing) - .serverRequestParser(ArmeriaHttpServerParser.get()) - .serverResponseParser(ArmeriaHttpServerParser.get()) + .serverRequestParser(BraveService.defaultRequestParser) + .serverResponseParser(BraveService.defaultResponseParser) .build(); final HttpRequest req = HttpRequest.of(RequestHeaders.of(HttpMethod.POST, "/hello/trustin", diff --git a/dependencies.toml b/dependencies.toml index a0b8b740fcf..83f7d008ee2 100644 --- a/dependencies.toml +++ b/dependencies.toml @@ -256,6 +256,9 @@ version.ref = "brave5" [libraries.brave5-context-slf4j] module = 'io.zipkin.brave:brave-context-slf4j' version.ref = "brave5" +[libraries.brave5-instrumentation-rpc] +module = "io.zipkin.brave:brave-instrumentation-rpc" +version.ref = "brave5" [libraries.brave5-instrumentation-http] module = "io.zipkin.brave:brave-instrumentation-http" version.ref = "brave5" @@ -272,6 +275,9 @@ javadocs = [ [libraries.brave6-context-slf4j] module = 'io.zipkin.brave:brave-context-slf4j' version.ref = "brave6" +[libraries.brave6-instrumentation-rpc] +module = "io.zipkin.brave:brave-instrumentation-rpc" +version.ref = "brave6" [libraries.brave6-instrumentation-http] module = "io.zipkin.brave:brave-instrumentation-http" version.ref = "brave6"