Skip to content

Commit

Permalink
rename package & low cardinality
Browse files Browse the repository at this point in the history
  • Loading branch information
123liuziming committed Oct 21, 2023
1 parent 95972e9 commit 982569c
Show file tree
Hide file tree
Showing 16 changed files with 347 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package v2_2
package io.opentelemetry.instrumentation.spring.gateway.v2_2

import io.opentelemetry.instrumentation.awssdk.v2_2.AbstractAws2ClientTest
import io.opentelemetry.instrumentation.test.LibraryTestTrait
Expand Down
5 changes: 5 additions & 0 deletions instrumentation/spring/spring-cloud-gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Settings for the Spring Cloud Gateway instrumentation

| System property | Type | Default | Description |
|--------------------------------------------------------------------------| ------- | ------- |---------------------------------------------------------------------------------------------|
| `otel.instrumentation.spring-cloud-gateway.experimental-span-attributes` | Boolean | `false` | Enable the capture of experimental span attributes. |
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ dependencies {
}

tasks.withType<Test>().configureEach {
// TODO run tests both with and without experimental span attributes
jvmArgs("-Dotel.instrumentation.spring-webflux.experimental-span-attributes=true")
jvmArgs("-Dotel.instrumentation.spring-cloud-gateway.experimental-span-attributes=true")

// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.v2_0;
package io.opentelemetry.javaagent.instrumentation.spring.gateway.v2_0;

import static java.util.Arrays.asList;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.v2_0;
package io.opentelemetry.javaagent.instrumentation.spring.gateway.v2_0;

import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerRouteGetter;
import org.springframework.web.server.ServerWebExchange;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.v2_0;
package io.opentelemetry.javaagent.instrumentation.spring.gateway.v2_0;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.gateway.v2_0;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.internal.StringUtils;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
import java.util.regex.Pattern;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.web.server.ServerWebExchange;

public final class ServerWebExchangeHelper {

/** Route info key. */
private static final AttributeKey<String> ROUTE_INFO_ATTRIBUTES =
AttributeKey.stringKey("ROUTE_INFO");

private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES;

static {
CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
InstrumentationConfig.get()
.getBoolean(
"otel.instrumentation.spring-cloud-gateway.experimental-span-attributes", false);
}

/* Regex for UUID */
private static final Pattern UUID_REGEX =
Pattern.compile(
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");

/* Route ID for route whose routeID is unset */
private static final String UNSET_ROUTE_ID = "UNSET_ROUTE_ID";

private static final String INVALID_RANDOM_ROUTE_ID =
"org.springframework.util.AlternativeJdkIdGenerator@";

private ServerWebExchangeHelper() {}

public static void extractAttributes(ServerWebExchange exchange, Context context) {
// Record route info
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
if (route != null && CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES) {
Span currentSpan = Span.fromContext(context);
if (currentSpan == null) {
return;
}
currentSpan.setAttribute(ROUTE_INFO_ATTRIBUTES, summarizeRoute(route));
}
}

public static String extractServerRoute(ServerWebExchange exchange) {
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
if (route != null) {
return convergeRouteId(route);
}
return null;
}

/**
* To avoid high cardinality, we ignore random UUID generated by Spring Cloud Gateway. Spring
* Cloud Gateway generate invalid random routeID, and it is fixed until 3.1.x
*
* @see <a
* href="https://github.com/spring-cloud/spring-cloud-gateway/commit/5002fe2e0a2825ef47dd667cade37b844c276cf6"/>
*/
private static String convergeRouteId(Route route) {
String routeId = route.getId();
if (StringUtils.isNullOrEmpty(routeId)) {
return UNSET_ROUTE_ID;
}
if (UUID_REGEX.matcher(routeId).matches()) {
return UNSET_ROUTE_ID;
}
if (routeId.startsWith(INVALID_RANDOM_ROUTE_ID)) {
return UNSET_ROUTE_ID;
}
return routeId;
}

private static String summarizeRoute(Route route) {
return "id='"
+ route.getId()
+ '\''
+ ", uri="
+ route.getUri()
+ ", order="
+ route.getOrder()
+ ", filterSize="
+ route.getFilters().size()
+ '}';
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.gateway.v2_0;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.semconv.SemanticAttributes;
import io.opentelemetry.testing.internal.armeria.client.WebClient;
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = {
GatewayTestApplication.class,
GatewayRouteMappingTest.ForceNettyAutoConfiguration.class
})
class GatewayRouteMappingTest {

private static final AttributeKey<String> ROUTE_INFO_ATTRIBUTES =
AttributeKey.stringKey("ROUTE_INFO");

private static final String UNSET_ROUTE_ID = "UNSET_ROUTE_ID";

@TestConfiguration
static class ForceNettyAutoConfiguration {
@Bean
NettyReactiveWebServerFactory nettyFactory() {
return new NettyReactiveWebServerFactory();
}
}

@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();

@Value("${local.server.port}")
private int port;

private WebClient client;

@BeforeEach
void beforeEach() {
client = WebClient.builder("h1c://localhost:" + port).followRedirects().build();
}

@Test
void gatewayRouteMappingTest() {
String requestBody = "gateway";
AggregatedHttpResponse response = client.post("/gateway/echo", requestBody).aggregate().join();
assertThat(response.status().code()).isEqualTo(200);
assertThat(response.contentUtf8()).isEqualTo(requestBody);
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasAttribute(equalTo(SemanticAttributes.HTTP_ROUTE, "path_route")),
span ->
span.hasAttributesSatisfying(
satisfies(ROUTE_INFO_ATTRIBUTES, s -> s.contains("id='path_route'")),
satisfies(
ROUTE_INFO_ATTRIBUTES, s -> s.contains("uri=h1c://mock.response")))));
}

@Test
void gatewayRandomUUIDRouteMappingTest() {
String requestBody = "gateway";
AggregatedHttpResponse response = client.post("/uuid/echo", requestBody).aggregate().join();
assertThat(response.status().code()).isEqualTo(200);
assertThat(response.contentUtf8()).isEqualTo(requestBody);
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasAttribute(equalTo(SemanticAttributes.HTTP_ROUTE, UNSET_ROUTE_ID)),
span ->
span.hasAttributesSatisfying(
satisfies(ROUTE_INFO_ATTRIBUTES, s -> s.contains("uri=h1c://mock.uuid")))));
}

@Test
void gatewayFakeUUIDRouteMappingTest() {
String requestBody = "gateway";
AggregatedHttpResponse response = client.post("/fake/echo", requestBody).aggregate().join();
assertThat(response.status().code()).isEqualTo(200);
assertThat(response.contentUtf8()).isEqualTo(requestBody);
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasAttribute(
equalTo(SemanticAttributes.HTTP_ROUTE, "ffffffff-ffff-ffff-ffff-ffff")),
span ->
span.hasAttributesSatisfying(
satisfies(ROUTE_INFO_ATTRIBUTES, s -> s.contains("uri=h1c://mock.fake")))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.gateway.v2_0;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class GatewayTestApplication {

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
// A simple echo gateway.
return builder
.routes()
.route(
"path_route",
r ->
r.path("/gateway/**")
.filters(GatewayTestApplication::echoFunc)
.uri("h1c://mock.response"))
// The routeID should be a random UUID.
.route(
r ->
r.path("/uuid/**").filters(GatewayTestApplication::echoFunc).uri("h1c://mock.uuid"))
// Seems like an uuid but not.
.route(
"ffffffff-ffff-ffff-ffff-ffff",
r ->
r.path("/fake/**").filters(GatewayTestApplication::echoFunc).uri("h1c://mock.fake"))
.build();
}

private static UriSpec echoFunc(GatewayFilterSpec f) {
return f.filter(
(exchange, chain) -> exchange.getResponse().writeWith(exchange.getRequest().getBody()));
}
}

This file was deleted.

Loading

0 comments on commit 982569c

Please sign in to comment.