Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce BraveRpcService #6115

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions brave/brave5/build.gradle
Original file line number Diff line number Diff line change
@@ -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')
Expand Down
1 change: 1 addition & 0 deletions brave/brave6/build.gradle
Original file line number Diff line number Diff line change
@@ -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')
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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:
* <pre>{@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)
* ...
* }</pre>
* The following tags will be available by default:
* <ul>
* <li>http.url</li>
* <li>http.host</li>
Expand All @@ -41,53 +52,25 @@
* <li>address.local</li>
* </ul>
*/
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() {}
}
Original file line number Diff line number Diff line change
@@ -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:
* <pre>{@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)
* ...
* }</pre>
* The following tags will be available by default:
* <ul>
* <li>http.url</li>
* <li>http.host</li>
* <li>http.protocol</li>
* <li>http.serfmt</li>
* <li>address.remote</li>
* <li>address.local</li>
* </ul>
*/
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() {}
}
Original file line number Diff line number Diff line change
@@ -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.
}
}
}
Loading
Loading