1
0

use @Slf4j (+logback) for logging instead of System.out/err.println (#165)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/165
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig 2025-03-19 16:21:55 +01:00
parent eb9edf1cb1
commit 4994bac101
13 changed files with 60 additions and 39 deletions

View File

@ -66,6 +66,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5'
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0'
implementation 'org.postgresql:postgresql'
implementation 'org.liquibase:liquibase-core'
@ -76,7 +77,6 @@ dependencies {
implementation 'net.java.dev.jna:jna:5.16.0'
implementation 'org.modelmapper:modelmapper:3.2.2'
implementation 'org.iban4j:iban4j:3.2.10-RELEASE'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5'
implementation 'org.reflections:reflections:0.10.2'
compileOnly 'org.projectlombok:lombok'

View File

@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.config;
import io.micrometer.core.annotation.Timed;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.util.LinkedMultiValueMap;
@ -14,6 +15,8 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
// HOWTO add logger
@Slf4j
public class RealCasAuthenticator implements CasAuthenticator {
@Value("${hsadminng.cas.server}")
@ -32,7 +35,8 @@ public class RealCasAuthenticator implements CasAuthenticator {
? fetchServiceTicket(ticket)
: ticket;
final var userName = extractUserName(verifyServiceTicket(serviceTicket));
System.err.println("CAS-user: " + userName);
// HOWTO log some message for a certain log level (trace, debug, info, warn, error)
log.debug("CAS-user: {}", userName);
return userName;
}
@ -65,9 +69,7 @@ public class RealCasAuthenticator implements CasAuthenticator {
private String extractUserName(final Document verification) {
if (verification.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) {
System.err.println("CAS service ticket could not be validated");
System.err.println(verification);
throwBadCredentialsException("CAS service ticket could not be validated");
throwBadCredentialsException("CAS service ticket could not be verified");
}
return verification.getElementsByTagName("cas:user").item(0).getTextContent();
}

View File

@ -28,6 +28,7 @@ import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.errors.CustomErrorResponse.*;
@ControllerAdvice
// HOWTO handle exceptions to produce specific http error codes and sensible error messages
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {

View File

@ -121,10 +121,4 @@ public final class HashGenerator {
}
return withSalt(stringBuilder.toString());
}
public static void main(String[] args) {
System.out.println(
HashGenerator.using(Algorithm.LINUX_YESCRYPT).withRandomSalt().hash("my plaintext domain transfer passphrase")
);
}
}

View File

@ -125,10 +125,4 @@ public class Dns {
return Result.fromException(exception);
}
}
public static void main(String[] args) {
final var result = new Dns("example.org").fetchRecordsOfType("TXT");
System.out.println(result);
}
}

View File

@ -1261,16 +1261,12 @@ public class RbacSpec {
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
)
.findFirst()
.orElse(null);
if (mainMethod != null) {
.orElseThrow(() -> new RuntimeException("no main method in: " + c.getName() + " => cannot generate RBAC rules"));
try {
mainMethod.invoke(null, new Object[] { null });
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
}
}
public static Set<Class<? extends BaseEntity>> findRbacEntityClasses(String packageName) {

View File

@ -202,6 +202,5 @@ public class RbacViewMermaidFlowchartGenerator {
.replace("%{flowchart}", flowchart.toString())
.replace("%{case}", forCase == null ? "" : " " + forCase),
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
System.out.println("Markdown-File: " + path.toAbsolutePath());
}
}

View File

@ -49,6 +49,5 @@ public class RbacViewPostgresGenerator {
toString(),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
System.out.println(outputPath.toAbsolutePath());
}
}

View File

@ -358,9 +358,6 @@ class RolesGrantsAndPermissionsGenerator {
}
private String roleRef(final PostgresTriggerReference rootRefVar, final RbacSpec.RbacRoleDefinition roleDef) {
if (roleDef == null) {
System.out.println("null");
}
if (roleDef.getEntityAlias().isGlobal()) {
return "rbac.global_ADMIN()";
}

View File

@ -68,8 +68,10 @@ metrics:
server:
requests: true
# HOWTO set logging-levels for certain Java packages (trace, debug, info, warn, error)
logging:
level:
org:
springframework:
security: TRACE
org.springframework.security: info
# HOWTO configure logging, e.g. logging to a separate file, see:
# https://docs.spring.io/spring-boot/reference/features/logging.html

View File

@ -1,11 +1,16 @@
package net.hostsharing.hsadminng.config;
import com.github.tomakehurst.wiremock.WireMockServer;
import net.hostsharing.hsadminng.test.LogbackLogPattern;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
@ -13,15 +18,23 @@ import org.springframework.http.HttpStatus;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static net.hostsharing.hsadminng.config.HttpHeadersBuilder.headers;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"server.port=0", "hsadminng.cas.server=http://localhost:8088"})
@TestPropertySource(properties = {
"server.port=0",
"hsadminng.cas.server=http://localhost:8088",
"logging.level.root=DEBUG"
})
@ActiveProfiles({"wiremock", "realCasAuthenticator"}) // IMPORTANT: To test prod config, do NOT use test profile!
@Tag("generalIntegrationTest")
@ExtendWith(OutputCaptureExtension.class)
class CasAuthenticationFilterIntegrationTest {
@Value("${local.server.port}")
@ -37,7 +50,7 @@ class CasAuthenticationFilterIntegrationTest {
private WireMockServer wireMockServer;
@Test
public void shouldAcceptRequestWithValidCasTicket() {
public void shouldAcceptRequestWithValidCasTicket(final CapturedOutput capturedOutput) {
// given
final var username = "test-user-" + randomAlphanumeric(4);
wireMockServer.stubFor(get(urlEqualTo("/cas/p3/serviceValidate?service=" + serviceUrl + "&ticket=ST-valid"))
@ -63,6 +76,9 @@ class CasAuthenticationFilterIntegrationTest {
// then
assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(result.getBody()).isEqualTo("pong " + username + "\n");
// HOWTO assert log messages
assertThat(capturedOutput.getOut()).containsPattern(
LogbackLogPattern.of(LogLevel.DEBUG, RealCasAuthenticator.class, "CAS-user: " + username));
}
@Test

View File

@ -39,8 +39,8 @@ class RestResponseEntityExceptionHandlerUnitTest {
final var errorResponse = exceptionHandler.handleConflict(givenException, givenWebRequest);
// then
assertThat(errorResponse.getBody().getStatusCode()).isEqualTo(409);
assertThat(errorResponse.getBody().getMessage()).isEqualTo("ERROR: [409] First Line");
assertThat(errorResponse.getBody()).extracting(CustomErrorResponse::getStatusCode).isEqualTo(409);
assertThat(errorResponse.getBody()).extracting(CustomErrorResponse::getMessage).isEqualTo("ERROR: [409] First Line");
}
@Test

View File

@ -0,0 +1,21 @@
package net.hostsharing.hsadminng.test;
import ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator;
import org.springframework.boot.logging.LogLevel;
public class LogbackLogPattern {
private static final int LOGBACK_MAX_CLASSNAME_LENGTH = 36;
/**
* @return a regular expression for a log message
*/
public static CharSequence of(
final LogLevel logLevel,
final Class<?> loggingClass,
final String message) {
final var abbreviator = new TargetLengthBasedClassNameAbbreviator(LOGBACK_MAX_CLASSNAME_LENGTH);
final var shortenedClassName = abbreviator.abbreviate(loggingClass.getName());
return logLevel + " [0-9]+ .* " + shortenedClassName + " *: " + message;
}
}