Compare commits
50 commits
Author | SHA1 | Date | |
---|---|---|---|
1e9ab13c97 | |||
58a53fe280 | |||
584ffc85f2 | |||
9621f913e3 | |||
25ea6c556d | |||
7a33e9cdbb | |||
6a3026de70 | |||
8269687a6e | |||
3ae16bfa25 | |||
ee96894e87 | |||
21913626ad | |||
d1acb1a0ee | |||
bc0110cc29 | |||
f591d514ec | |||
331a830c2b | |||
7d2cda5b20 | |||
ca915c4bf5 | |||
cf81524d86 | |||
35d4e29a57 | |||
ed362d84b8 | |||
fc140833d5 | |||
a0939d7729 | |||
2d23835810 | |||
5e8f8fefa8 | |||
f6e85f2c00 | |||
a5e0c2672b | |||
1cc8a39c88 | |||
8aad75a493 | |||
880df82728 | |||
2d1f9a1417 | |||
129fdb768f | |||
6d95a3b123 | |||
9d84bb8c5e | |||
0fd62decaf | |||
33e10b7a40 | |||
4d20a75f42 | |||
4b20f99856 | |||
3bbdf7015a | |||
d7b6286546 | |||
cccd9dde98 | |||
f9734649f2 | |||
e6ec3f57f8 | |||
1f38615a15 | |||
a22c070401 | |||
d5053a3862 | |||
a9e1f3d8fa | |||
5ac5d90f97 | |||
b813ed4347 | |||
6f7b048266 | |||
3081156f9f |
37 changed files with 890 additions and 256 deletions
4
.github/project.yaml
vendored
4
.github/project.yaml
vendored
|
@ -1,5 +1,5 @@
|
||||||
name: Quarkus Commons
|
name: Quarkus Commons
|
||||||
release:
|
release:
|
||||||
current-version: "1.0.2"
|
current-version: "1.0.8"
|
||||||
next-version: "1.0.3-SNAPSHOT"
|
next-version: "1.0.9-SNAPSHOT"
|
||||||
|
|
||||||
|
|
26
.github/workflows/build.yaml
vendored
26
.github/workflows/build.yaml
vendored
|
@ -4,6 +4,8 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "main"
|
- "main"
|
||||||
|
tags:
|
||||||
|
- '[0-9]+.[0-9]+.[0-9]+'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.gitattributes'
|
- '.gitattributes'
|
||||||
- '.gitignore'
|
- '.gitignore'
|
||||||
|
@ -12,6 +14,7 @@ on:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
COMMON_MAVEN_OPTS: "-e -B --fae"
|
COMMON_MAVEN_OPTS: "-e -B --fae"
|
||||||
|
@ -60,19 +63,24 @@ jobs:
|
||||||
servers: |
|
servers: |
|
||||||
[{
|
[{
|
||||||
"id": "phoenix-oss",
|
"id": "phoenix-oss",
|
||||||
"configuration": {
|
"username": "${{ vars.ORG_PACKAGE_WRITER_USERNAME }}",
|
||||||
"httpHeaders": {
|
"password": "${{ secrets.ORG_PACKAGE_WRITER_TOKEN }}"
|
||||||
"property": {
|
|
||||||
"name": "Authorization",
|
|
||||||
"value": "token ${{ secrets.ORG_PACKAGE_WRITER_TOKEN }}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
- name: Make maven wrapper executable
|
- name: Make maven wrapper executable
|
||||||
run: chmod +x mvnw
|
run: chmod +x mvnw
|
||||||
|
|
||||||
|
- name: Validate tag
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
run: |
|
||||||
|
TAG_NAME="${GITHUB_REF#refs/tags/}"
|
||||||
|
PROJECT_VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||||
|
|
||||||
|
if [[ "$PROJECT_VERSION" != "$TAG_NAME" ]]; then
|
||||||
|
echo "::error::pom.xml version '$PROJECT_VERSION' does not match tag '$TAG_NAME'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Download dependencies
|
- name: Download dependencies
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS quarkus:go-offline
|
run: ./mvnw $COMMON_MAVEN_OPTS quarkus:go-offline
|
||||||
|
|
||||||
|
@ -87,5 +95,5 @@ jobs:
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=quarkus-commons -Dsonar.projectName='quarkus-commons' -Dsonar.coverage.jacoco.xmlReportPaths=../**/target/jacoco-report/jacoco.xml
|
run: ./mvnw $COMMON_MAVEN_OPTS org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=quarkus-commons -Dsonar.projectName='quarkus-commons' -Dsonar.coverage.jacoco.xmlReportPaths=../**/target/jacoco-report/jacoco.xml
|
||||||
|
|
||||||
- name: Publish jars
|
- name: Publish jars
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS deploy -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
|
run: ./mvnw $COMMON_MAVEN_OPTS deploy -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
|
12
README.md
12
README.md
|
@ -2,12 +2,12 @@ Quarkus Commons
|
||||||
===============
|
===============
|
||||||
|
|
||||||
[](https://openjdk.org/projects/jdk/21/)
|
[](https://openjdk.org/projects/jdk/21/)
|
||||||
[](https://sonarqube.pub.production.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
||||||
[](https://sonarqube.pub.production.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
||||||
[](https://sonarqube.pub.production.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
||||||
[](https://sonarqube.pub.production.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
||||||
[](https://sonarqube.pub.production.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
||||||
[](https://sonarqube.pub.production.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
|
|
15
pom.xml
15
pom.xml
|
@ -3,11 +3,12 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>quarkus-audit-tools</module>
|
<module>quarkus-audit-tools</module>
|
||||||
|
<module>quarkus-client-logger</module>
|
||||||
<module>quarkus-clock-service</module>
|
<module>quarkus-clock-service</module>
|
||||||
<module>quarkus-json-service</module>
|
<module>quarkus-json-service</module>
|
||||||
<module>quarkus-message-digest-service</module>
|
<module>quarkus-message-digest-service</module>
|
||||||
|
@ -20,15 +21,15 @@
|
||||||
<!-- Quarkus properties -->
|
<!-- Quarkus properties -->
|
||||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||||
<quarkus.platform.version>3.23.3</quarkus.platform.version>
|
<quarkus.platform.version>3.25.0</quarkus.platform.version>
|
||||||
|
|
||||||
<!-- Plugin versions -->
|
<!-- Plugin versions -->
|
||||||
<compiler-plugin.version>3.14.0</compiler-plugin.version>
|
<compiler-plugin.version>3.14.0</compiler-plugin.version>
|
||||||
<surefire-plugin.version>3.5.3</surefire-plugin.version>
|
<surefire-plugin.version>3.5.3</surefire-plugin.version>
|
||||||
<spotless-plugin.version>2.44.4</spotless-plugin.version>
|
<spotless-plugin.version>2.46.1</spotless-plugin.version>
|
||||||
<palantir-java-format.version>2.66.0</palantir-java-format.version>
|
<palantir-java-format.version>2.72.0</palantir-java-format.version>
|
||||||
<jacoco-plugin.version>0.8.13</jacoco-plugin.version> <!-- Match with version from Quarkus BOM -->
|
<jacoco-plugin.version>0.8.13</jacoco-plugin.version> <!-- Match with version from Quarkus BOM -->
|
||||||
<jandex-plugin.version>3.3.1</jandex-plugin.version>
|
<jandex-plugin.version>3.4.0</jandex-plugin.version>
|
||||||
<release-plugin.version>3.1.1</release-plugin.version>
|
<release-plugin.version>3.1.1</release-plugin.version>
|
||||||
<source-plugin.version>3.3.1</source-plugin.version>
|
<source-plugin.version>3.3.1</source-plugin.version>
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@
|
||||||
<connection>scm:git:ssh://git@git-ssh.kvant.cloud:2222/phoenix-oss/quarkus-commons.git</connection>
|
<connection>scm:git:ssh://git@git-ssh.kvant.cloud:2222/phoenix-oss/quarkus-commons.git</connection>
|
||||||
<developerConnection>scm:git:ssh://git@git-ssh.kvant.cloud:2222/phoenix-oss/quarkus-commons.git</developerConnection>
|
<developerConnection>scm:git:ssh://git@git-ssh.kvant.cloud:2222/phoenix-oss/quarkus-commons.git</developerConnection>
|
||||||
<url>https://git.kvant.cloud/phoenix-oss/quarkus-commons.git</url>
|
<url>https://git.kvant.cloud/phoenix-oss/quarkus-commons.git</url>
|
||||||
<tag>1.0.2</tag>
|
<tag>HEAD</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -195,7 +196,7 @@
|
||||||
<tagNameFormat>@{project.version}</tagNameFormat>
|
<tagNameFormat>@{project.version}</tagNameFormat>
|
||||||
<checkModificationExcludes>mvnw</checkModificationExcludes>
|
<checkModificationExcludes>mvnw</checkModificationExcludes>
|
||||||
<scmReleaseCommitComment>chore: release @{releaseLabel}</scmReleaseCommitComment>
|
<scmReleaseCommitComment>chore: release @{releaseLabel}</scmReleaseCommitComment>
|
||||||
<scmDevelopmentCommitComment>chore: prepare for next development iteration [skip ci]</scmDevelopmentCommitComment>
|
<scmDevelopmentCommitComment>chore: prepare for next development iteration</scmDevelopmentCommitComment>
|
||||||
<autoVersionSubmodules>true</autoVersionSubmodules>
|
<autoVersionSubmodules>true</autoVersionSubmodules>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-audit-tools</artifactId>
|
<artifactId>quarkus-audit-tools</artifactId>
|
||||||
|
@ -51,38 +51,38 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<!-- <build>-->
|
<build>
|
||||||
<!-- <plugins>-->
|
<plugins>
|
||||||
<!-- <plugin>-->
|
<plugin>
|
||||||
<!-- <groupId>org.jacoco</groupId>-->
|
<groupId>org.jacoco</groupId>
|
||||||
<!-- <artifactId>jacoco-maven-plugin</artifactId>-->
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<!-- <version>${jacoco-plugin.version}</version>-->
|
<version>${jacoco-plugin.version}</version>
|
||||||
<!-- <executions>-->
|
<executions>
|
||||||
<!-- <execution>-->
|
<execution>
|
||||||
<!-- <id>jacoco-check</id>-->
|
<id>jacoco-check</id>
|
||||||
<!-- <goals>-->
|
<goals>
|
||||||
<!-- <goal>check</goal>-->
|
<goal>check</goal>
|
||||||
<!-- </goals>-->
|
</goals>
|
||||||
<!-- <phase>test</phase>-->
|
<phase>test</phase>
|
||||||
<!-- <configuration>-->
|
<configuration>
|
||||||
<!-- <dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>-->
|
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
||||||
<!-- <rules>-->
|
<rules>
|
||||||
<!-- <rule>-->
|
<rule>
|
||||||
<!-- <element>BUNDLE</element>-->
|
<element>BUNDLE</element>
|
||||||
<!-- <limits>-->
|
<limits>
|
||||||
<!-- <limit>-->
|
<limit>
|
||||||
<!-- <counter>INSTRUCTION</counter>-->
|
<counter>INSTRUCTION</counter>
|
||||||
<!-- <value>COVEREDRATIO</value>-->
|
<value>COVEREDRATIO</value>
|
||||||
<!-- <minimum>1</minimum>-->
|
<minimum>1</minimum>
|
||||||
<!-- </limit>-->
|
</limit>
|
||||||
<!-- </limits>-->
|
</limits>
|
||||||
<!-- </rule>-->
|
</rule>
|
||||||
<!-- </rules>-->
|
</rules>
|
||||||
<!-- </configuration>-->
|
</configuration>
|
||||||
<!-- </execution>-->
|
</execution>
|
||||||
<!-- </executions>-->
|
</executions>
|
||||||
<!-- </plugin>-->
|
</plugin>
|
||||||
<!-- </plugins>-->
|
</plugins>
|
||||||
<!-- </build>-->
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
package ch.phoenix.oss.quarkus.commons.audit;
|
||||||
|
|
||||||
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
package ch.phoenix.oss.quarkus.commons.audit;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -17,12 +16,36 @@ class RevisionTest {
|
||||||
var r2 = new Revision();
|
var r2 = new Revision();
|
||||||
r2.rev = 1;
|
r2.rev = 1;
|
||||||
|
|
||||||
assertThat(r1).as("Revisions should be equal").isEqualTo(r2);
|
var r3 = new Revision();
|
||||||
|
r3.rev = 2;
|
||||||
|
|
||||||
|
assertThat(r1)
|
||||||
|
.as("Revisions equality should should match expected value")
|
||||||
|
.isEqualTo(r1)
|
||||||
|
.isEqualTo(r2)
|
||||||
|
.isNotEqualTo(r3)
|
||||||
|
.isNotEqualTo(new Object());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testHashCode() {}
|
void testHashCode() {
|
||||||
|
var r1 = new Revision();
|
||||||
|
r1.rev = 123;
|
||||||
|
|
||||||
|
var r2 = new Revision();
|
||||||
|
r2.rev = 123;
|
||||||
|
|
||||||
|
var r3 = new Revision();
|
||||||
|
r3.rev = 2;
|
||||||
|
|
||||||
|
assertThat(r1.hashCode()).isEqualTo(123).isEqualTo(r2.hashCode()).isNotEqualTo(r3.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testToString() {}
|
void testToString() {
|
||||||
|
var rev = new Revision();
|
||||||
|
rev.rev = 1;
|
||||||
|
|
||||||
|
assertThat(rev).as("Revision's toString should match expected value").hasToString("Revision{rev=1}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
65
quarkus-client-logger/pom.xml
Normal file
65
quarkus-client-logger/pom.xml
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?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.9-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>quarkus-client-logger</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-config-yaml</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-rest-jackson</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>${jacoco-plugin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>jacoco-check</id>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>test</phase>
|
||||||
|
<configuration>
|
||||||
|
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
||||||
|
<rules>
|
||||||
|
<rule>
|
||||||
|
<element>BUNDLE</element>
|
||||||
|
<limits>
|
||||||
|
<limit>
|
||||||
|
<counter>INSTRUCTION</counter>
|
||||||
|
<value>COVEREDRATIO</value>
|
||||||
|
<minimum>0.92</minimum>
|
||||||
|
</limit>
|
||||||
|
</limits>
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
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,119 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import io.vertx.core.Handler;
|
||||||
|
import io.vertx.core.MultiMap;
|
||||||
|
import io.vertx.core.buffer.Buffer;
|
||||||
|
import io.vertx.core.http.HttpClientRequest;
|
||||||
|
import io.vertx.core.http.HttpClientResponse;
|
||||||
|
import jakarta.enterprise.context.Dependent;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.resteasy.reactive.client.api.ClientLogger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is based on org.jboss.resteasy.reactive.client.logging.DefaultClientLogger,
|
||||||
|
* with the only change being that headers are redacted based on the Set provided
|
||||||
|
* by the configuration.
|
||||||
|
*/
|
||||||
|
@Dependent
|
||||||
|
public class RedactingClientLogger implements ClientLogger {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(RedactingClientLogger.class);
|
||||||
|
|
||||||
|
private static final String REDACTED_VALUE = "*****";
|
||||||
|
|
||||||
|
private final Set<String> redactedHeaders;
|
||||||
|
|
||||||
|
private int bodySize;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RedactingClientLogger(RedactingClientLoggerConfiguration configuration) {
|
||||||
|
this.redactedHeaders = configuration
|
||||||
|
.headers()
|
||||||
|
.redact()
|
||||||
|
.orElse(RedactingClientLoggerConfiguration.Headers.DEFAULT_REDACTED_HEADERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBodySize(int bodySize) {
|
||||||
|
this.bodySize = bodySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logResponse(HttpClientResponse response, boolean redirect) {
|
||||||
|
if (!log.isDebugEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection Convert2Lambda
|
||||||
|
response.bodyHandler(new Handler<>() {
|
||||||
|
@Override
|
||||||
|
public void handle(Buffer body) {
|
||||||
|
log.debugf(
|
||||||
|
"%s: %s %s, Status[%d %s], Headers[%s], Body:\n%s",
|
||||||
|
redirect ? "Redirect" : "Response",
|
||||||
|
response.request().getMethod(),
|
||||||
|
response.request().absoluteURI(),
|
||||||
|
response.statusCode(),
|
||||||
|
response.statusMessage(),
|
||||||
|
asString(response.headers()),
|
||||||
|
bodyToString(body));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logRequest(HttpClientRequest request, Buffer body, boolean omitBody) {
|
||||||
|
if (!log.isDebugEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (omitBody) {
|
||||||
|
log.debugf(
|
||||||
|
"Request: %s %s Headers[%s], Body omitted",
|
||||||
|
request.getMethod(), request.absoluteURI(), asString(request.headers()));
|
||||||
|
} else if (body == null || body.length() == 0) {
|
||||||
|
log.debugf(
|
||||||
|
"Request: %s %s Headers[%s], Empty body",
|
||||||
|
request.getMethod(), request.absoluteURI(), asString(request.headers()));
|
||||||
|
} else {
|
||||||
|
log.debugf(
|
||||||
|
"Request: %s %s Headers[%s], Body:\n%s",
|
||||||
|
request.getMethod(), request.absoluteURI(), asString(request.headers()), bodyToString(body));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String bodyToString(Buffer body) {
|
||||||
|
if (body == null) {
|
||||||
|
return "";
|
||||||
|
} else if (bodySize <= 0) {
|
||||||
|
return body.toString();
|
||||||
|
} else {
|
||||||
|
String bodyAsString = body.toString();
|
||||||
|
return bodyAsString.substring(0, Math.min(bodySize, bodyAsString.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String asString(MultiMap headers) {
|
||||||
|
if (headers.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder((headers.size() * (6 + 1 + 6))
|
||||||
|
+ (headers.size() - 1)); // this is a very rough estimate of a result like 'key1=value1 key2=value2'
|
||||||
|
boolean isFirst = true;
|
||||||
|
for (Map.Entry<String, String> entry : headers) {
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
|
} else {
|
||||||
|
sb.append(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = entry.getKey();
|
||||||
|
var value = redactedHeaders.contains(key.toLowerCase()) ? REDACTED_VALUE : entry.getValue();
|
||||||
|
|
||||||
|
sb.append(key).append('=').append(value);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import io.smallrye.config.ConfigMapping;
|
||||||
|
import io.smallrye.config.WithConverter;
|
||||||
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ConfigMapping(prefix = "phoenix.client-logger")
|
||||||
|
public interface RedactingClientLoggerConfiguration {
|
||||||
|
|
||||||
|
Headers headers();
|
||||||
|
|
||||||
|
interface Headers {
|
||||||
|
|
||||||
|
Set<String> DEFAULT_REDACTED_HEADERS = Set.of(HttpHeaders.AUTHORIZATION.toLowerCase());
|
||||||
|
|
||||||
|
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import io.quarkus.test.junit.QuarkusTestProfile;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class InfoLevelProfile implements QuarkusTestProfile {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getConfigOverrides() {
|
||||||
|
return Map.of("quarkus.log.category.\"ch.phoenix.oss.quarkus.commons.client.logger\".level", "INFO");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
|
||||||
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
import io.quarkus.test.junit.TestProfile;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@QuarkusTest
|
||||||
|
@TestProfile(InfoLevelProfile.class)
|
||||||
|
class InfoLevelTest {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@RestClient
|
||||||
|
TestClient injectedClient;
|
||||||
|
|
||||||
|
TestClient builtClient = QuarkusRestClientBuilder.newBuilder()
|
||||||
|
.clientLogger(new RedactingClientLogger(() -> Optional::empty))
|
||||||
|
.baseUri(URI.create("http://localhost:8087"))
|
||||||
|
.build(TestClient.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithInjectedClient() {
|
||||||
|
injectedClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithBuiltClientAndEmptyConfig() {
|
||||||
|
builtClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postWithInjectedClient() {
|
||||||
|
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", "body");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postWithBuiltClientAndEmptyConfig() {
|
||||||
|
builtClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted", "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
|
||||||
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@QuarkusTest
|
||||||
|
class RedactingClientLoggerTest {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@RestClient
|
||||||
|
TestClient injectedClient;
|
||||||
|
|
||||||
|
TestClient builtClient = QuarkusRestClientBuilder.newBuilder()
|
||||||
|
.clientLogger(new RedactingClientLogger(() -> Optional::empty))
|
||||||
|
.baseUri(URI.create("http://localhost:8087"))
|
||||||
|
.build(TestClient.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithInjectedClient() {
|
||||||
|
injectedClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithBuiltClientAndEmptyConfig() {
|
||||||
|
builtClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postWithInjectedClient() {
|
||||||
|
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", "body");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postWithInjectedClientAndNullBody() {
|
||||||
|
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postWithBuiltClientAndEmptyConfig() {
|
||||||
|
builtClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted", "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import io.quarkus.test.junit.QuarkusTestProfile;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ScopeNoneProfile implements QuarkusTestProfile {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getConfigOverrides() {
|
||||||
|
return Map.of("quarkus.rest-client.logging.scope", "none");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
|
||||||
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
import io.quarkus.test.junit.TestProfile;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@QuarkusTest
|
||||||
|
@TestProfile(ScopeNoneProfile.class)
|
||||||
|
class ScopeNoneTest {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@RestClient
|
||||||
|
TestClient injectedClient;
|
||||||
|
|
||||||
|
TestClient builtClient = QuarkusRestClientBuilder.newBuilder()
|
||||||
|
.clientLogger(new RedactingClientLogger(() -> Optional::empty))
|
||||||
|
.baseUri(URI.create("http://localhost:8087"))
|
||||||
|
.build(TestClient.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithInjectedClient() {
|
||||||
|
injectedClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getWithBuiltClientAndEmptyConfig() {
|
||||||
|
builtClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postWithInjectedClient() {
|
||||||
|
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", "body");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void postWithBuiltClientAndEmptyConfig() {
|
||||||
|
builtClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted", "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||||
|
|
||||||
|
@SuppressWarnings("UastIncorrectHttpHeaderInspection")
|
||||||
|
@RegisterRestClient(configKey = "test")
|
||||||
|
public interface TestClient {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/")
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
String get(
|
||||||
|
@HeaderParam("Authorization") String authorization,
|
||||||
|
@HeaderParam("X-Request-ID") String requestId,
|
||||||
|
@HeaderParam("X-Something-Else") String somethingElse);
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/")
|
||||||
|
@Consumes(MediaType.TEXT_PLAIN)
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
String post(
|
||||||
|
@HeaderParam("Authorization") String authorization,
|
||||||
|
@HeaderParam("X-Request-ID") String requestId,
|
||||||
|
@HeaderParam("X-Something-Else") String somethingElse,
|
||||||
|
String body);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.client.logger;
|
||||||
|
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
public class TestResource {
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
public String get() {
|
||||||
|
return "get";
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.TEXT_PLAIN)
|
||||||
|
@Produces(MediaType.TEXT_PLAIN)
|
||||||
|
public String post(String body) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
20
quarkus-client-logger/src/test/resources/application.yaml
Normal file
20
quarkus-client-logger/src/test/resources/application.yaml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
quarkus:
|
||||||
|
http:
|
||||||
|
test-port: 8087
|
||||||
|
rest-client:
|
||||||
|
logging:
|
||||||
|
scope: request-response
|
||||||
|
body-limit: 10000
|
||||||
|
test:
|
||||||
|
url: http://localhost:${quarkus.http.test-port}
|
||||||
|
log:
|
||||||
|
category:
|
||||||
|
"ch.phoenix.oss.quarkus.commons.client.logger":
|
||||||
|
level: DEBUG
|
||||||
|
|
||||||
|
phoenix:
|
||||||
|
client-logger:
|
||||||
|
headers:
|
||||||
|
redact:
|
||||||
|
- AUTHORIZATION
|
||||||
|
- X-SOMETHING-ELSE
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-clock-service</artifactId>
|
<artifactId>quarkus-clock-service</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-json-service</artifactId>
|
<artifactId>quarkus-json-service</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-message-digest-service</artifactId>
|
<artifactId>quarkus-message-digest-service</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-random-number-generator</artifactId>
|
<artifactId>quarkus-random-number-generator</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-tracing-service</artifactId>
|
<artifactId>quarkus-tracing-service</artifactId>
|
||||||
|
@ -41,4 +41,38 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>${jacoco-plugin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>jacoco-check</id>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>test</phase>
|
||||||
|
<configuration>
|
||||||
|
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
||||||
|
<rules>
|
||||||
|
<rule>
|
||||||
|
<element>BUNDLE</element>
|
||||||
|
<limits>
|
||||||
|
<limit>
|
||||||
|
<counter>INSTRUCTION</counter>
|
||||||
|
<value>COVEREDRATIO</value>
|
||||||
|
<minimum>0.95</minimum>
|
||||||
|
</limit>
|
||||||
|
</limits>
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ch.phoenix.oss.quarkus.commons.tracing;
|
||||||
import io.smallrye.config.ConfigMapping;
|
import io.smallrye.config.ConfigMapping;
|
||||||
import io.smallrye.config.WithConverter;
|
import io.smallrye.config.WithConverter;
|
||||||
import io.smallrye.config.WithDefault;
|
import io.smallrye.config.WithDefault;
|
||||||
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -17,6 +18,14 @@ public interface TracingConfiguration {
|
||||||
|
|
||||||
interface Headers {
|
interface Headers {
|
||||||
|
|
||||||
|
Set<String> DEFAULT_REDACTED = Set.of(HttpHeaders.AUTHORIZATION.toLowerCase());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional set of headers to redact when tracing. By default, redacts
|
||||||
|
* the 'Authorization' header.
|
||||||
|
*
|
||||||
|
* @return the set of headers to be redacted
|
||||||
|
*/
|
||||||
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +40,18 @@ public interface TracingConfiguration {
|
||||||
Query query();
|
Query query();
|
||||||
|
|
||||||
interface Query {
|
interface Query {
|
||||||
|
|
||||||
|
Set<String> DEFAULT_REDACTED = Set.of("access_token", "refresh_token", "apikey");
|
||||||
|
|
||||||
@WithDefault("false")
|
@WithDefault("false")
|
||||||
boolean includeRaw();
|
boolean includeRaw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional set of query params to redact when tracing. By default, redacts
|
||||||
|
* the following params: 'access_token', 'refresh_token' and 'apikey'.
|
||||||
|
*
|
||||||
|
* @return the set of query params to be redacted
|
||||||
|
*/
|
||||||
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,13 @@ import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||||
import jakarta.ws.rs.container.ResourceInfo;
|
import jakarta.ws.rs.container.ResourceInfo;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
|
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
|
||||||
|
|
||||||
@Unremovable
|
@Unremovable
|
||||||
public class TracingRequestFilter {
|
public class TracingRequestFilter {
|
||||||
|
|
||||||
|
private static final String REDACTED_VALUE = "********";
|
||||||
|
|
||||||
private final RoutingContext routingContext;
|
private final RoutingContext routingContext;
|
||||||
private final TracingService tracingService;
|
private final TracingService tracingService;
|
||||||
private final SecurityIdentity securityIdentity;
|
private final SecurityIdentity securityIdentity;
|
||||||
|
@ -57,24 +58,38 @@ public class TracingRequestFilter {
|
||||||
TracingConstants.REQUEST_PATH_RAW, uriInfo.getAbsolutePath().getRawPath());
|
TracingConstants.REQUEST_PATH_RAW, uriInfo.getAbsolutePath().getRawPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var redactedHeaders = configuration
|
||||||
|
.requestFilter()
|
||||||
|
.headers()
|
||||||
|
.redact()
|
||||||
|
.orElse(TracingConfiguration.RequestFilterConfiguration.Headers.DEFAULT_REDACTED);
|
||||||
|
|
||||||
requestContext.getHeaders().forEach((key, value) -> {
|
requestContext.getHeaders().forEach((key, value) -> {
|
||||||
var lowerCaseKey = key.toLowerCase();
|
var lowerCaseKey = key.toLowerCase();
|
||||||
var property = TracingConstants.REQUEST_HEADERS + '.' + lowerCaseKey;
|
var property = TracingConstants.REQUEST_HEADERS + '.' + lowerCaseKey;
|
||||||
if (configuration
|
if (redactedHeaders.contains(lowerCaseKey)) {
|
||||||
.requestFilter()
|
tracingService.trace(property, REDACTED_VALUE);
|
||||||
.headers()
|
|
||||||
.redact()
|
|
||||||
.orElse(Set.of())
|
|
||||||
.contains(lowerCaseKey)) {
|
|
||||||
tracingService.trace(property, "********");
|
|
||||||
} else {
|
} else {
|
||||||
tracingService.trace(property, joinStrings(value));
|
tracingService.trace(property, joinStrings(value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
uriInfo.getQueryParameters()
|
var redactedQueryParams = configuration
|
||||||
.forEach((key, value) ->
|
.requestFilter()
|
||||||
tracingService.trace(TracingConstants.REQUEST_QUERY_PARAMS + '.' + key, joinStrings(value)));
|
.query()
|
||||||
|
.redact()
|
||||||
|
.orElse(TracingConfiguration.RequestFilterConfiguration.Query.DEFAULT_REDACTED);
|
||||||
|
|
||||||
|
uriInfo.getQueryParameters().forEach((key, value) -> {
|
||||||
|
var lowerCaseKey = key.toLowerCase();
|
||||||
|
var property = TracingConstants.REQUEST_QUERY_PARAMS + '.' + lowerCaseKey;
|
||||||
|
|
||||||
|
if (redactedQueryParams.contains(lowerCaseKey)) {
|
||||||
|
tracingService.trace(property, REDACTED_VALUE);
|
||||||
|
} else {
|
||||||
|
tracingService.trace(property, joinStrings(value));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (configuration.requestFilter().query().includeRaw()) {
|
if (configuration.requestFilter().query().includeRaw()) {
|
||||||
var rawQuery = uriInfo.getRequestUri().getRawQuery();
|
var rawQuery = uriInfo.getRequestUri().getRawQuery();
|
||||||
|
@ -87,10 +102,7 @@ public class TracingRequestFilter {
|
||||||
TracingConstants.REQUEST_CLIENT_IP,
|
TracingConstants.REQUEST_CLIENT_IP,
|
||||||
routingContext.request().connection().remoteAddress(true).hostAddress());
|
routingContext.request().connection().remoteAddress(true).hostAddress());
|
||||||
|
|
||||||
if (Log.isTraceEnabled()) {
|
Log.debugf("Incoming request: %s %s", method, uriInfo.getAbsolutePath().getRawPath());
|
||||||
Log.tracef(
|
|
||||||
"Incoming request: %s %s", method, uriInfo.getAbsolutePath().getRawPath());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String joinStrings(List<String> value) {
|
private static String joinStrings(List<String> value) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ public interface TracingService {
|
||||||
|
|
||||||
String getActor();
|
String getActor();
|
||||||
|
|
||||||
String getRequestPath();
|
String getRequestPathRaw();
|
||||||
|
|
||||||
String getRequestMethod();
|
String getRequestMethod();
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ class TracingServiceImpl implements TracingService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRequestPath() {
|
public String getRequestPathRaw() {
|
||||||
return (String) MDC.get(TracingConstants.REQUEST_PATH_RAW);
|
return (String) MDC.get(TracingConstants.REQUEST_PATH_RAW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import io.restassured.http.ContentType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@QuarkusTest
|
@QuarkusTest
|
||||||
public class ActorTest {
|
class ActorTest {
|
||||||
|
|
||||||
@InjectSpy
|
@InjectSpy
|
||||||
TracingService tracingService;
|
TracingService tracingService;
|
||||||
|
@ -34,7 +34,7 @@ public class ActorTest {
|
||||||
verify(tracingService).trace("request.route", route);
|
verify(tracingService).trace("request.route", route);
|
||||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
verify(tracingService).trace("request.headers.accept", "text/plain");
|
||||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
||||||
verify(tracingService).trace("request.headers.authorization", "Basic am9uOmRvZQ==");
|
verify(tracingService).trace("request.headers.authorization", "********");
|
||||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
||||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
||||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
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
|
||||||
|
class QueryParamTest {
|
||||||
|
|
||||||
|
@InjectSpy
|
||||||
|
TracingService tracingService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void traceQueryParams() {
|
||||||
|
var route = "/authenticated";
|
||||||
|
RestAssured.given()
|
||||||
|
.auth()
|
||||||
|
.basic("jon", "doe")
|
||||||
|
.accept(ContentType.TEXT)
|
||||||
|
.header("X-SOMETHING-ELSE", "whatever")
|
||||||
|
.queryParam("access_token", "api123")
|
||||||
|
.queryParam("refresh_token", "refresh123")
|
||||||
|
.queryParam("apikey", "apikey123")
|
||||||
|
.queryParam("grant_type", "authorization_code")
|
||||||
|
.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", "********");
|
||||||
|
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.headers.x-something-else", "whatever");
|
||||||
|
verify(tracingService).trace("request.query.params.access_token", "********");
|
||||||
|
verify(tracingService).trace("request.query.params.refresh_token", "********");
|
||||||
|
verify(tracingService).trace("request.query.params.apikey", "********");
|
||||||
|
verify(tracingService).trace("request.query.params.grant_type", "authorization_code");
|
||||||
|
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
||||||
|
verifyNoMoreInteractions(tracingService);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@QuarkusTest
|
@QuarkusTest
|
||||||
@TestProfile(Test2Profile.class)
|
@TestProfile(Test2Profile.class)
|
||||||
public class RawPathTest {
|
class RawPathTest {
|
||||||
|
|
||||||
@InjectSpy
|
@InjectSpy
|
||||||
TracingService tracingService;
|
TracingService tracingService;
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
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)
|
||||||
|
class RedactedTest {
|
||||||
|
|
||||||
|
@InjectSpy
|
||||||
|
TracingService tracingService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void traceRedactedValues() {
|
||||||
|
var route = "/authenticated";
|
||||||
|
RestAssured.given()
|
||||||
|
.auth()
|
||||||
|
.basic("jon", "doe")
|
||||||
|
.accept(ContentType.TEXT)
|
||||||
|
.header("X-SOMETHING-ELSE", "whatever")
|
||||||
|
.queryParam("access_token", "api123")
|
||||||
|
.queryParam("refresh_token", "refresh123")
|
||||||
|
.queryParam("apikey", "apikey123")
|
||||||
|
.queryParam("grant_type", "authorization_code")
|
||||||
|
.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.path.raw", route);
|
||||||
|
verify(tracingService).trace("request.headers.accept", "text/plain");
|
||||||
|
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
||||||
|
verify(tracingService).trace("request.headers.authorization", "********");
|
||||||
|
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.headers.x-something-else", "********");
|
||||||
|
verify(tracingService).trace("request.query.params.access_token", "********");
|
||||||
|
verify(tracingService).trace("request.query.params.refresh_token", "refresh123");
|
||||||
|
verify(tracingService).trace("request.query.params.apikey", "apikey123");
|
||||||
|
verify(tracingService).trace("request.query.params.grant_type", "authorization_code");
|
||||||
|
verify(tracingService)
|
||||||
|
.trace(
|
||||||
|
"request.query.raw",
|
||||||
|
"access_token=api123&refresh_token=refresh123&apikey=apikey123&grant_type=authorization_code");
|
||||||
|
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
||||||
|
verifyNoMoreInteractions(tracingService);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.ArgumentMatchers.startsWith;
|
import static org.mockito.ArgumentMatchers.startsWith;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
@ -10,7 +11,11 @@ import io.quarkus.test.junit.mockito.InjectSpy;
|
||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
@QuarkusTest
|
@QuarkusTest
|
||||||
class RoutePatternTest {
|
class RoutePatternTest {
|
||||||
|
@ -18,12 +23,46 @@ class RoutePatternTest {
|
||||||
@InjectSpy
|
@InjectSpy
|
||||||
TracingService tracingService;
|
TracingService tracingService;
|
||||||
|
|
||||||
@Test
|
static Stream<Arguments> get() {
|
||||||
void getBlankResource() {
|
return Stream.of(
|
||||||
var route = "/";
|
arguments("/", Map.of()),
|
||||||
RestAssured.given().accept(ContentType.TEXT).when().get(route).then().statusCode(200);
|
arguments("/leading-and-no-trailing", Map.of()),
|
||||||
|
arguments("/leading/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
||||||
|
arguments("/{param}/{param2}/trailing", Map.of("param", "1", "param2", "2")),
|
||||||
|
arguments("/leading-and-no-trailing/{param}", Map.of("param", "1")),
|
||||||
|
arguments("/leading-and-no-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
||||||
|
arguments("/leading-and-trailing", Map.of()),
|
||||||
|
arguments("/leading-and-trailing/{param}", Map.of("param", "1")),
|
||||||
|
arguments("/leading-and-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
||||||
|
arguments("/no-leading-and-no-trailing", Map.of()),
|
||||||
|
arguments("/no-leading-and-no-trailing/{param}", Map.of("param", "1")),
|
||||||
|
arguments("/no-leading-and-no-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
||||||
|
arguments("/no-leading-and-trailing", Map.of()),
|
||||||
|
arguments("/no-leading-and-trailing/{param}", Map.of("param", "1")),
|
||||||
|
arguments("/no-leading-and-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")));
|
||||||
|
}
|
||||||
|
|
||||||
verifyGetTracing(route, Map.of());
|
@MethodSource
|
||||||
|
@ParameterizedTest
|
||||||
|
void get(String route, Map<String, String> pathParams) {
|
||||||
|
RestAssured.given()
|
||||||
|
.accept(ContentType.TEXT)
|
||||||
|
.when()
|
||||||
|
.get(route, pathParams)
|
||||||
|
.then()
|
||||||
|
.statusCode(200);
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -45,160 +84,4 @@ class RoutePatternTest {
|
||||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
||||||
verifyNoMoreInteractions(tracingService);
|
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,98 @@
|
||||||
|
package ch.phoenix.oss.quarkus.commons.tracing;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.instrumentation.annotations.WithSpan;
|
||||||
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import org.jboss.logging.MDC;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@QuarkusTest
|
||||||
|
class TracingServiceImplTest {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
TracingService tracingService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Span span;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
MDC.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getActor() {
|
||||||
|
tracingService.trace("actor", "abc");
|
||||||
|
assertThat(tracingService.getActor())
|
||||||
|
.as("Actor should match expected value")
|
||||||
|
.isEqualTo("abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRequestPathRaw() {
|
||||||
|
tracingService.trace("request.path.raw", "/foo/bar");
|
||||||
|
assertThat(tracingService.getRequestPathRaw())
|
||||||
|
.as("Request Path Raw should match expected value")
|
||||||
|
.isEqualTo("/foo/bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRequestMethod() {
|
||||||
|
tracingService.trace("request.method", "GET");
|
||||||
|
assertThat(tracingService.getRequestMethod())
|
||||||
|
.as("Request Method should match expected value")
|
||||||
|
.isEqualTo("GET");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRequestId() {
|
||||||
|
tracingService.trace("request.headers.x-request-id", "ba458367-bfeb-46ba-87da-50b9343be8f9");
|
||||||
|
assertThat(tracingService.getRequestId())
|
||||||
|
.as("Request Id should match expected value")
|
||||||
|
.isEqualTo("ba458367-bfeb-46ba-87da-50b9343be8f9");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithSpan
|
||||||
|
void getTraceId() {
|
||||||
|
assertThat(tracingService.getTraceId())
|
||||||
|
.as("Request Trace Id should match expected value")
|
||||||
|
.isEqualTo(span.getSpanContext().getTraceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithSpan
|
||||||
|
void getSpanId() {
|
||||||
|
assertThat(tracingService.getSpanId())
|
||||||
|
.as("Request Span Id should match expected value")
|
||||||
|
.isEqualTo(span.getSpanContext().getSpanId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getClientIp() {
|
||||||
|
tracingService.trace("request.client.ip", "127.0.0.1");
|
||||||
|
assertThat(tracingService.getClientIp())
|
||||||
|
.as("Request Client Iü should match expected value")
|
||||||
|
.isEqualTo("127.0.0.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSchedulerJob() {
|
||||||
|
tracingService.trace("scheduler.job.name", "scheduler/abc");
|
||||||
|
assertThat(tracingService.getSchedulerJob())
|
||||||
|
.as("Scheduler Job Name should match expected value")
|
||||||
|
.isEqualTo("scheduler/abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void clearAll() {
|
||||||
|
tracingService.trace("aaa", "bbb");
|
||||||
|
assertThat(MDC.get("aaa")).isEqualTo("bbb");
|
||||||
|
tracingService.clearAll();
|
||||||
|
assertThat(MDC.get("aaa")).isNull();
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,14 +10,14 @@ import jakarta.ws.rs.core.MediaType;
|
||||||
public class SlashResource {
|
public class SlashResource {
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/leading/{id}/{anotherId}")
|
@Path("/leading/{param}/{param2}")
|
||||||
public String doubleLeading(int id, int anotherId) {
|
public String doubleLeading(int param, int param2) {
|
||||||
return "leading/" + id + "/" + anotherId;
|
return "leading/" + param + "/" + param2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{id}/{anotherId}/trailing/")
|
@Path("{param}/{param2}/trailing/")
|
||||||
public String doubleTrailing(int id, int anotherId) {
|
public String doubleTrailing(int param, int param2) {
|
||||||
return id + "/" + anotherId + "/trailing";
|
return param + "/" + param2 + "/trailing";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ quarkus:
|
||||||
headers:
|
headers:
|
||||||
redact:
|
redact:
|
||||||
- AUTHORIZATION
|
- AUTHORIZATION
|
||||||
|
- X-SOMETHING-ELSE
|
||||||
query:
|
query:
|
||||||
include-raw: true
|
include-raw: true
|
||||||
redact:
|
redact:
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenix.oss</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.9-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-uuid-generator</artifactId>
|
<artifactId>quarkus-uuid-generator</artifactId>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue