feat(tracing): add quarkus-tracing-service module
This commit is contained in:
parent
75a778296c
commit
db0026b723
20 changed files with 867 additions and 0 deletions
44
quarkus-tracing-service/pom.xml
Normal file
44
quarkus-tracing-service/pom.xml
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>ch.phoenix.oss</groupId>
|
||||
<artifactId>quarkus-commons</artifactId>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quarkus-tracing-service</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest-jackson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-opentelemetry</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-elytron-security-properties-file</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-config-yaml</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import org.eclipse.microprofile.config.spi.Converter;
|
||||
|
||||
public class LowerCaseStringConverter implements Converter<String> {
|
||||
@Override
|
||||
public String convert(String value) throws IllegalArgumentException, NullPointerException {
|
||||
return value.toLowerCase();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import io.smallrye.config.ConfigMapping;
|
||||
import io.smallrye.config.WithConverter;
|
||||
import io.smallrye.config.WithDefault;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@ConfigMapping(prefix = "phoenix.commons.tracing")
|
||||
public interface TracingConfiguration {
|
||||
|
||||
RequestFilterConfiguration requestFilter();
|
||||
|
||||
interface RequestFilterConfiguration {
|
||||
|
||||
Headers headers();
|
||||
|
||||
interface Headers {
|
||||
|
||||
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
||||
}
|
||||
|
||||
Path path();
|
||||
|
||||
interface Path {
|
||||
|
||||
@WithDefault("false")
|
||||
boolean includeRaw();
|
||||
}
|
||||
|
||||
Query query();
|
||||
|
||||
interface Query {
|
||||
@WithDefault("false")
|
||||
boolean includeRaw();
|
||||
|
||||
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
public class TracingConstants {
|
||||
|
||||
public static final String ACTOR = "actor";
|
||||
public static final String ANONYMOUS = "anonymous";
|
||||
public static final String REQUEST_ROUTE = "request.route";
|
||||
public static final String REQUEST_METHOD = "request.method";
|
||||
public static final String REQUEST_PATH_RAW = "request.path.raw";
|
||||
public static final String REQUEST_PATH_PARAMS = "request.path.params";
|
||||
public static final String REQUEST_QUERY_RAW = "request.query.raw";
|
||||
public static final String REQUEST_QUERY_PARAMS = "request.query.params";
|
||||
public static final String REQUEST_HEADERS = "request.headers";
|
||||
public static final String REQUEST_CLIENT_IP = "request.client.ip";
|
||||
public static final String SCHEDULER_JOB_NAME = "scheduler.job.name";
|
||||
|
||||
private TracingConstants() {}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import io.quarkus.arc.Unremovable;
|
||||
import io.quarkus.logging.Log;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ResourceInfo;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
|
||||
|
||||
@Unremovable
|
||||
public class TracingRequestFilter {
|
||||
|
||||
private final RoutingContext routingContext;
|
||||
private final TracingService tracingService;
|
||||
private final SecurityIdentity securityIdentity;
|
||||
private final TracingConfiguration configuration;
|
||||
|
||||
@Inject
|
||||
public TracingRequestFilter(
|
||||
RoutingContext routingContext,
|
||||
TracingService tracingService,
|
||||
SecurityIdentity securityIdentity,
|
||||
TracingConfiguration configuration) {
|
||||
this.routingContext = routingContext;
|
||||
this.tracingService = tracingService;
|
||||
this.securityIdentity = securityIdentity;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@ServerRequestFilter
|
||||
public void filter(ContainerRequestContext requestContext, ResourceInfo resourceInfo) {
|
||||
if (securityIdentity.isAnonymous()) {
|
||||
tracingService.trace(TracingConstants.ACTOR, TracingConstants.ANONYMOUS);
|
||||
} else {
|
||||
tracingService.trace(
|
||||
TracingConstants.ACTOR, securityIdentity.getPrincipal().getName());
|
||||
}
|
||||
|
||||
var method = requestContext.getMethod();
|
||||
tracingService.trace(TracingConstants.REQUEST_METHOD, method);
|
||||
|
||||
var routePattern = getRoutePattern(resourceInfo);
|
||||
tracingService.trace(TracingConstants.REQUEST_ROUTE, routePattern);
|
||||
|
||||
var uriInfo = requestContext.getUriInfo();
|
||||
uriInfo.getPathParameters()
|
||||
.forEach((key, value) ->
|
||||
tracingService.trace(TracingConstants.REQUEST_PATH_PARAMS + '.' + key, joinStrings(value)));
|
||||
|
||||
if (configuration.requestFilter().path().includeRaw()) {
|
||||
tracingService.trace(
|
||||
TracingConstants.REQUEST_PATH_RAW, uriInfo.getAbsolutePath().getRawPath());
|
||||
}
|
||||
|
||||
requestContext.getHeaders().forEach((key, value) -> {
|
||||
var lowerCaseKey = key.toLowerCase();
|
||||
var property = TracingConstants.REQUEST_HEADERS + '.' + lowerCaseKey;
|
||||
if (configuration
|
||||
.requestFilter()
|
||||
.headers()
|
||||
.redact()
|
||||
.orElse(Set.of())
|
||||
.contains(lowerCaseKey)) {
|
||||
tracingService.trace(property, "********");
|
||||
} else {
|
||||
tracingService.trace(property, joinStrings(value));
|
||||
}
|
||||
});
|
||||
|
||||
uriInfo.getQueryParameters()
|
||||
.forEach((key, value) ->
|
||||
tracingService.trace(TracingConstants.REQUEST_QUERY_PARAMS + '.' + key, joinStrings(value)));
|
||||
|
||||
if (configuration.requestFilter().query().includeRaw()) {
|
||||
var rawQuery = uriInfo.getRequestUri().getRawQuery();
|
||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
||||
tracingService.trace(TracingConstants.REQUEST_QUERY_RAW, rawQuery);
|
||||
}
|
||||
}
|
||||
|
||||
tracingService.trace(
|
||||
TracingConstants.REQUEST_CLIENT_IP,
|
||||
routingContext.request().connection().remoteAddress(true).hostAddress());
|
||||
|
||||
if (Log.isTraceEnabled()) {
|
||||
Log.tracef(
|
||||
"Incoming request: %s %s", method, uriInfo.getAbsolutePath().getRawPath());
|
||||
}
|
||||
}
|
||||
|
||||
private static String joinStrings(List<String> value) {
|
||||
return String.join(", ", value);
|
||||
}
|
||||
|
||||
private String getRoutePattern(ResourceInfo resourceInfo) {
|
||||
String classPath = getPathValue(resourceInfo.getResourceClass().getAnnotation(Path.class));
|
||||
String methodPath = getPathValue(resourceInfo.getResourceMethod().getAnnotation(Path.class));
|
||||
|
||||
if (!classPath.isEmpty()) {
|
||||
if (methodPath.isEmpty()) {
|
||||
return "/" + classPath;
|
||||
} else {
|
||||
return "/" + classPath + "/" + methodPath;
|
||||
}
|
||||
} else {
|
||||
if (methodPath.isEmpty()) {
|
||||
return "/";
|
||||
} else {
|
||||
return "/" + methodPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPathValue(Path path) {
|
||||
if (path == null) {
|
||||
return "";
|
||||
}
|
||||
return trimSlashes(path.value());
|
||||
}
|
||||
|
||||
private static String trimSlashes(String segment) {
|
||||
if (segment.isEmpty() || "/".equals(segment)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Assuming that it's not possible for a segment to contain //,
|
||||
// thus it's possible to avoid extra ifs and whiles, as well
|
||||
// as the use of regexes
|
||||
int start = (segment.charAt(0) == '/') ? 1 : 0;
|
||||
int end = (segment.charAt(segment.length() - 1) == '/') ? segment.length() - 1 : segment.length();
|
||||
|
||||
return segment.substring(start, end);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
public interface TracingService {
|
||||
|
||||
void clearAll();
|
||||
|
||||
void trace(String key, Object value);
|
||||
|
||||
String getActor();
|
||||
|
||||
String getRequestPath();
|
||||
|
||||
String getRequestMethod();
|
||||
|
||||
String getRequestId();
|
||||
|
||||
String getTraceId();
|
||||
|
||||
String getSpanId();
|
||||
|
||||
String getClientIp();
|
||||
|
||||
String getSchedulerJob();
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import io.opentelemetry.api.trace.Span;
|
||||
import io.quarkus.arc.DefaultBean;
|
||||
import io.quarkus.logging.Log;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import org.jboss.logging.MDC;
|
||||
|
||||
@DefaultBean
|
||||
@ApplicationScoped
|
||||
class TracingServiceImpl implements TracingService {
|
||||
|
||||
private final Span span;
|
||||
|
||||
TracingServiceImpl(Span span) {
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAll() {
|
||||
MDC.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(final String key, final Object value) {
|
||||
Log.infof("tracing key=%s value=%s", key, value);
|
||||
MDC.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActor() {
|
||||
return (String) MDC.get(TracingConstants.ACTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestPath() {
|
||||
return (String) MDC.get(TracingConstants.REQUEST_PATH_RAW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestMethod() {
|
||||
return (String) MDC.get(TracingConstants.REQUEST_METHOD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestId() {
|
||||
return (String) MDC.get(TracingConstants.REQUEST_HEADERS + ".x-request-id");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTraceId() {
|
||||
return span.getSpanContext().getTraceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpanId() {
|
||||
return span.getSpanContext().getSpanId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientIp() {
|
||||
return (String) MDC.get(TracingConstants.REQUEST_CLIENT_IP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchedulerJob() {
|
||||
return (String) MDC.get(TracingConstants.SCHEDULER_JOB_NAME);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
public class ActorTest {
|
||||
|
||||
@InjectSpy
|
||||
TracingService tracingService;
|
||||
|
||||
@Test
|
||||
void getAuthenticated() {
|
||||
var route = "/authenticated";
|
||||
RestAssured.given()
|
||||
.auth()
|
||||
.basic("jon", "doe")
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verify(tracingService).trace("actor", "jon");
|
||||
verify(tracingService).trace("request.method", "GET");
|
||||
verify(tracingService).trace("request.route", route);
|
||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
||||
verify(tracingService).trace("request.headers.authorization", "Basic am9uOmRvZQ==");
|
||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
||||
verifyNoMoreInteractions(tracingService);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.TestProfile;
|
||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@TestProfile(Test2Profile.class)
|
||||
public class RawPathTest {
|
||||
|
||||
@InjectSpy
|
||||
TracingService tracingService;
|
||||
|
||||
@Test
|
||||
void getWithRawPath() {
|
||||
var route = "/no-leading-and-trailing/{param}/{param2}";
|
||||
RestAssured.given()
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route, 1, 2)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verify(tracingService).trace("actor", "anonymous");
|
||||
verify(tracingService).trace("request.method", "GET");
|
||||
verify(tracingService).trace("request.route", route);
|
||||
verify(tracingService).trace("request.path.params.param", "1");
|
||||
verify(tracingService).trace("request.path.params.param2", "2");
|
||||
verify(tracingService).trace("request.path.raw", "/no-leading-and-trailing/1/2");
|
||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
||||
verifyNoMoreInteractions(tracingService);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
class RoutePatternTest {
|
||||
|
||||
@InjectSpy
|
||||
TracingService tracingService;
|
||||
|
||||
@Test
|
||||
void getBlankResource() {
|
||||
var route = "/";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void postBlankResource() {
|
||||
var route = "/";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().post(route).then().statusCode(200);
|
||||
|
||||
verify(tracingService).trace("actor", "anonymous");
|
||||
verify(tracingService).trace("request.method", "POST");
|
||||
verify(tracingService).trace("request.route", route);
|
||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
||||
verify(tracingService).trace("request.headers.content-length", "0");
|
||||
verify(tracingService)
|
||||
.trace("request.headers.content-type", "application/x-www-form-urlencoded; charset=ISO-8859-1");
|
||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
||||
verifyNoMoreInteractions(tracingService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLeadingResource() {
|
||||
var route = "/leading/{id}/{anotherId}";
|
||||
RestAssured.given()
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route, 1, 2)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("id", "1", "anotherId", "2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTrailingResource() {
|
||||
var route = "/{id}/{anotherId}/trailing";
|
||||
RestAssured.given()
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route, 1, 2)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("id", "1", "anotherId", "2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLeadingAndNoTrailingResource() {
|
||||
var route = "/leading-and-no-trailing";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLeadingAndNoTrailingWithSingleParamResource() {
|
||||
var route = "/leading-and-no-trailing/{param}";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route, 1).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLeadingAndNoTrailingWithMultiParamResource() {
|
||||
var route = "/leading-and-no-trailing/{param}/{param2}";
|
||||
RestAssured.given()
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route, 1, 2)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1", "param2", "2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLeadingAndTrailingResource() {
|
||||
var route = "/leading-and-trailing";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLeadingAndTrailingWithSingleParamResource() {
|
||||
var route = "/leading-and-trailing/{param}";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route, 1).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLeadingAndTrailingWithMultiParamResource() {
|
||||
var route = "/leading-and-trailing/{param}/{param2}";
|
||||
RestAssured.given()
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route, 1, 2)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1", "param2", "2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoLeadingAndNoTrailingResource() {
|
||||
var route = "/no-leading-and-no-trailing";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void geNoLeadingAndNoTrailingWithSingleParamResource() {
|
||||
var route = "/no-leading-and-no-trailing/{param}";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route, 1).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoLeadingAndNoTrailingWithMultiParamResource() {
|
||||
var route = "/no-leading-and-no-trailing/{param}/{param2}";
|
||||
RestAssured.given()
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route, 1, 2)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1", "param2", "2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoLeadingAndTrailingResource() {
|
||||
var route = "/no-leading-and-trailing";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoLeadingAndTrailingWithSingleParamResource() {
|
||||
var route = "/no-leading-and-trailing/{param}";
|
||||
RestAssured.given().accept(ContentType.TEXT).when().get(route, 1).then().statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNoLeadingAndTrailingWithMultiParamResource() {
|
||||
var route = "/no-leading-and-trailing/{param}/{param2}";
|
||||
RestAssured.given()
|
||||
.accept(ContentType.TEXT)
|
||||
.when()
|
||||
.get(route, 1, 2)
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
verifyGetTracing(route, Map.of("param", "1", "param2", "2"));
|
||||
}
|
||||
|
||||
private void verifyGetTracing(String route, Map<String, String> pathParams) {
|
||||
verify(tracingService).trace("actor", "anonymous");
|
||||
verify(tracingService).trace("request.method", "GET");
|
||||
verify(tracingService).trace("request.route", route);
|
||||
pathParams.forEach((key, value) -> verify(tracingService).trace("request.path.params." + key, value));
|
||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
||||
verifyNoMoreInteractions(tracingService);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||
|
||||
import io.quarkus.test.junit.QuarkusTestProfile;
|
||||
|
||||
public class Test2Profile implements QuarkusTestProfile {
|
||||
|
||||
@Override
|
||||
public String getConfigProfile() {
|
||||
return "test2";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
||||
|
||||
import io.quarkus.security.Authenticated;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
|
||||
@Authenticated
|
||||
@Path("authenticated")
|
||||
public class AuthenticatedResource {
|
||||
|
||||
@GET
|
||||
public String get() {
|
||||
return "Hello user";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Path("")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public class BlankResource {
|
||||
|
||||
@GET
|
||||
@PermitAll
|
||||
public String get() {
|
||||
return "get";
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("")
|
||||
public String post() {
|
||||
return "post";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Path("/leading-and-no-trailing")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public class LeadingAndNoTrailingResource {
|
||||
|
||||
@GET
|
||||
@Path("")
|
||||
public String root() {
|
||||
return "root";
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{param}")
|
||||
public String singleParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{param}/{param2}")
|
||||
public String multiParam(String param, String param2) {
|
||||
return param + "/" + param2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Path("/leading-and-trailing/")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public class LeadingAndTrailingResource {
|
||||
|
||||
@GET
|
||||
@Path("/")
|
||||
public String root() {
|
||||
return "root";
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{param}/")
|
||||
public String singleParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{param}/{param2}/")
|
||||
public String multiParam(String param, String param2) {
|
||||
return param + "/" + param2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Path("no-leading-and-no-trailing")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public class NoLeadingAndNoTrailingResource {
|
||||
|
||||
@GET
|
||||
@Path("")
|
||||
public String root() {
|
||||
return "root";
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{param}")
|
||||
public String singleParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{param}/{param2}")
|
||||
public String multiParam(String param, String param2) {
|
||||
return param + "/" + param2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Path("no-leading-and-trailing/")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public class NoLeadingAndTrailingResource {
|
||||
|
||||
@GET
|
||||
@Path("/")
|
||||
public String root() {
|
||||
return "root";
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{param}/")
|
||||
public String singleParam(String param) {
|
||||
return param;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{param}/{param2}/")
|
||||
public String multiParam(String param, String param2) {
|
||||
return param + "/" + param2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
||||
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
@Path("/")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public class SlashResource {
|
||||
|
||||
@GET
|
||||
@Path("/leading/{id}/{anotherId}")
|
||||
public String doubleLeading(int id, int anotherId) {
|
||||
return "leading/" + id + "/" + anotherId;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/{anotherId}/trailing/")
|
||||
public String doubleTrailing(int id, int anotherId) {
|
||||
return id + "/" + anotherId + "/trailing";
|
||||
}
|
||||
}
|
38
quarkus-tracing-service/src/test/resources/application.yaml
Normal file
38
quarkus-tracing-service/src/test/resources/application.yaml
Normal file
|
@ -0,0 +1,38 @@
|
|||
quarkus:
|
||||
http:
|
||||
test-port: 0
|
||||
log:
|
||||
category:
|
||||
"org.apache.http.wire":
|
||||
level: DEBUG # set to DEBUG when RestAssured logs are necessary to understand test failures
|
||||
otel:
|
||||
traces:
|
||||
exporter: none
|
||||
security:
|
||||
users:
|
||||
embedded:
|
||||
enabled: true
|
||||
plain-text: true
|
||||
users:
|
||||
jon: doe
|
||||
|
||||
"%test2":
|
||||
quarkus:
|
||||
log:
|
||||
min-level: TRACE
|
||||
category:
|
||||
"ch.phoenix.oss.quarkus.commons.tracing":
|
||||
level: TRACE
|
||||
phoenix:
|
||||
commons:
|
||||
tracing:
|
||||
request-filter:
|
||||
path:
|
||||
include-raw: true
|
||||
headers:
|
||||
redact:
|
||||
- AUTHORIZATION
|
||||
query:
|
||||
include-raw: true
|
||||
redact:
|
||||
- ACCESS_TOKEN
|
Loading…
Add table
Add a link
Reference in a new issue