From 2dd9876f7bb2606be4da8ed231a10682fe324b8a Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Fri, 13 Mar 2026 10:18:46 +0100 Subject: [PATCH 1/7] Log 404 responses at DEBUG instead of WARN to reduce log noise --- .../DSpaceApiExceptionControllerAdvice.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index 4833cb938317..f6fbb8d02ba8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -283,11 +283,9 @@ private void sendErrorResponseFromException(final HttpServletRequest request, if (statusCodesLoggedAsErrors.contains(statusCode)) { log.error("{} (status:{})", message, statusCode, ex); } else { - // Log the error as a single-line WARN StackTraceElement[] trace = ex.getStackTrace(); String location = trace.length <= 0 ? "unknown" : trace[0].toString(); - log.warn("{} (status:{} exception: {} at: {})", - message, statusCode, ex.getClass().getName(), location); + logClientError(statusCode, message, ex.getClass().getName(), location); } response.sendError(statusCode, message); @@ -322,7 +320,6 @@ private void sendErrorResponse(final HttpServletRequest request, // Log the full error and status code log.error("{} (status:{})", message, statusCode, ex); } else if (HttpStatus.valueOf(statusCode).is4xxClientError()) { - // Log the error as a single-line WARN String location; String exceptionMessage; if (null == ex) { @@ -333,14 +330,27 @@ private void sendErrorResponse(final HttpServletRequest request, StackTraceElement[] trace = ex.getStackTrace(); location = trace.length <= 0 ? "unknown" : trace[0].toString(); } - log.warn("{} (status:{} exception: {} at: {})", message, statusCode, - exceptionMessage, location); + logClientError(statusCode, message, exceptionMessage, location); } //Exception properties will be set by org.springframework.boot.web.support.ErrorPageFilter response.sendError(statusCode, message); } + /** + * Log a 4xx client error. 404 NOT_FOUND is logged at DEBUG level (normal REST response), + * all other 4xx errors are logged at WARN level. + */ + private void logClientError(int statusCode, String message, String exceptionMessage, String location) { + if (statusCode == HttpServletResponse.SC_NOT_FOUND) { + log.debug("{} (status:{} exception: {} at: {})", message, statusCode, + exceptionMessage, location); + } else { + log.warn("{} (status:{} exception: {} at: {})", message, statusCode, + exceptionMessage, location); + } + } + /** * Get set of status codes that should be treated as errors. * From df1aa9ed86d04bd5d270c0edf9a8b1777b0524a2 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Fri, 13 Mar 2026 11:07:05 +0100 Subject: [PATCH 2/7] Log 404 responses at DEBUG instead of WARN (configurable via logging.server.debug-404) --- .../DSpaceApiExceptionControllerAdvice.java | 27 ++++++++++++++++--- dspace/config/dspace.cfg | 4 +++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index f6fbb8d02ba8..99ea1268dd51 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -69,6 +69,18 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH /** Configuration parameter for ERROR treatment. */ private static final String P_LOG_AS_ERROR = "logging.server.include-stacktrace-for-httpcode"; + /** + * Configuration property name controlling whether 404 NOT_FOUND responses + * are logged at DEBUG level instead of WARN. + */ + private static final String P_LOG_NOT_FOUND_AS_DEBUG = "logging.server.debug-404"; + + /** + * Default value for {@link #P_LOG_NOT_FOUND_AS_DEBUG}. When {@code true}, + * 404 NOT_FOUND responses are logged at DEBUG level. + */ + private static final boolean LOG_NOT_FOUND_AS_DEBUG_DEFAULT = true; + @Inject private ConfigurationService configurationService; @@ -338,11 +350,12 @@ private void sendErrorResponse(final HttpServletRequest request, } /** - * Log a 4xx client error. 404 NOT_FOUND is logged at DEBUG level (normal REST response), - * all other 4xx errors are logged at WARN level. + * Log a 4xx client error. By default, 404 NOT_FOUND is logged at DEBUG level + * (normal REST response), all other 4xx errors are logged at WARN level. + * This behavior for 404 can be overridden via {@link #P_LOG_NOT_FOUND_AS_DEBUG}. */ private void logClientError(int statusCode, String message, String exceptionMessage, String location) { - if (statusCode == HttpServletResponse.SC_NOT_FOUND) { + if (statusCode == HttpServletResponse.SC_NOT_FOUND && isNotFoundLoggedAsDebug()) { log.debug("{} (status:{} exception: {} at: {})", message, statusCode, exceptionMessage, location); } else { @@ -351,6 +364,13 @@ private void logClientError(int statusCode, String message, String exceptionMess } } + private boolean isNotFoundLoggedAsDebug() { + return configurationService.getBooleanProperty( + P_LOG_NOT_FOUND_AS_DEBUG, + LOG_NOT_FOUND_AS_DEBUG_DEFAULT + ); + } + /** * Get set of status codes that should be treated as errors. * @@ -365,7 +385,6 @@ private Set getStatusCodesLoggedAsErrors() { statusCodesLoggedAsErrors.add(Integer.valueOf(code)); } catch (NumberFormatException e) { log.warn("Non-integer HTTP status code {} in {}", code, P_LOG_AS_ERROR); - // And continue } } return statusCodesLoggedAsErrors; diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 8330d7ee3cf1..7941e7656a12 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -244,6 +244,10 @@ logging.server.include-query-string = false # repeating the assignment logging.server.include-stacktrace-for-httpcode = 422 logging.server.max-payload-length = 10000 +# When true, 404 NOT_FOUND responses are logged at DEBUG level instead of WARN. +# This prevents flooding production logs with expected 404s (e.g. missing config properties). +# Set to false if you want to see 404 responses in WARN logs. +logging.server.debug-404 = true ##### DOI registration agency credentials ###### # To mint DOIs you have to use a DOI registration agency like DataCite. Several From 4a86919853c8feda8ddf872d30870e1faf099fcc Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Fri, 13 Mar 2026 12:08:53 +0100 Subject: [PATCH 3/7] Skip stack trace extraction for suppressed 404 debug logs --- .../DSpaceApiExceptionControllerAdvice.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index 99ea1268dd51..6b9338a36ea8 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -294,7 +294,7 @@ private void sendErrorResponseFromException(final HttpServletRequest request, String message = ex.getMessage(); if (statusCodesLoggedAsErrors.contains(statusCode)) { log.error("{} (status:{})", message, statusCode, ex); - } else { + } else if (!isNotFoundSuppressed(statusCode)) { StackTraceElement[] trace = ex.getStackTrace(); String location = trace.length <= 0 ? "unknown" : trace[0].toString(); logClientError(statusCode, message, ex.getClass().getName(), location); @@ -331,7 +331,7 @@ private void sendErrorResponse(final HttpServletRequest request, if (HttpStatus.valueOf(statusCode).is5xxServerError() || LOG_AS_ERROR.contains(statusCode)) { // Log the full error and status code log.error("{} (status:{})", message, statusCode, ex); - } else if (HttpStatus.valueOf(statusCode).is4xxClientError()) { + } else if (HttpStatus.valueOf(statusCode).is4xxClientError() && !isNotFoundSuppressed(statusCode)) { String location; String exceptionMessage; if (null == ex) { @@ -371,6 +371,16 @@ private boolean isNotFoundLoggedAsDebug() { ); } + /** + * Check if a 404 response would be suppressed (debug-404 enabled but DEBUG logging off). + * Used to skip unnecessary stack trace extraction on frequent 404s in production. + */ + private boolean isNotFoundSuppressed(int statusCode) { + return statusCode == HttpServletResponse.SC_NOT_FOUND + && isNotFoundLoggedAsDebug() + && !log.isDebugEnabled(); + } + /** * Get set of status codes that should be treated as errors. * From 04425b72abb71ea095346661c3f0821b80eb3460 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Fri, 13 Mar 2026 13:50:19 +0100 Subject: [PATCH 4/7] Replace custom debug-404 property with dedicated Log4j2 logger (org.dspace.app.rest.NotFound) --- .../DSpaceApiExceptionControllerAdvice.java | 50 ++++++------------- dspace/config/dspace.cfg | 4 -- dspace/config/log4j2-cli.xml | 7 +++ dspace/config/log4j2-container.xml | 7 +++ dspace/config/log4j2.xml | 7 +++ 5 files changed, 35 insertions(+), 40 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index 6b9338a36ea8..271d650ad8a2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -61,6 +61,13 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionHandler { private static final Logger log = LogManager.getLogger(); + /** + * Dedicated logger for 404 NOT_FOUND responses. Defaults to DEBUG level so that + * expected 404s (e.g. missing config properties) don't flood production logs. + * Level is controllable via standard Log4j2 configuration. + */ + private static final Logger notFoundLog = LogManager.getLogger("org.dspace.app.rest.NotFound"); + /** * Default collection of HTTP error codes to log as ERROR with full stack trace. */ @@ -69,18 +76,6 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH /** Configuration parameter for ERROR treatment. */ private static final String P_LOG_AS_ERROR = "logging.server.include-stacktrace-for-httpcode"; - /** - * Configuration property name controlling whether 404 NOT_FOUND responses - * are logged at DEBUG level instead of WARN. - */ - private static final String P_LOG_NOT_FOUND_AS_DEBUG = "logging.server.debug-404"; - - /** - * Default value for {@link #P_LOG_NOT_FOUND_AS_DEBUG}. When {@code true}, - * 404 NOT_FOUND responses are logged at DEBUG level. - */ - private static final boolean LOG_NOT_FOUND_AS_DEBUG_DEFAULT = true; - @Inject private ConfigurationService configurationService; @@ -294,7 +289,7 @@ private void sendErrorResponseFromException(final HttpServletRequest request, String message = ex.getMessage(); if (statusCodesLoggedAsErrors.contains(statusCode)) { log.error("{} (status:{})", message, statusCode, ex); - } else if (!isNotFoundSuppressed(statusCode)) { + } else { StackTraceElement[] trace = ex.getStackTrace(); String location = trace.length <= 0 ? "unknown" : trace[0].toString(); logClientError(statusCode, message, ex.getClass().getName(), location); @@ -331,7 +326,7 @@ private void sendErrorResponse(final HttpServletRequest request, if (HttpStatus.valueOf(statusCode).is5xxServerError() || LOG_AS_ERROR.contains(statusCode)) { // Log the full error and status code log.error("{} (status:{})", message, statusCode, ex); - } else if (HttpStatus.valueOf(statusCode).is4xxClientError() && !isNotFoundSuppressed(statusCode)) { + } else if (HttpStatus.valueOf(statusCode).is4xxClientError()) { String location; String exceptionMessage; if (null == ex) { @@ -350,13 +345,13 @@ private void sendErrorResponse(final HttpServletRequest request, } /** - * Log a 4xx client error. By default, 404 NOT_FOUND is logged at DEBUG level - * (normal REST response), all other 4xx errors are logged at WARN level. - * This behavior for 404 can be overridden via {@link #P_LOG_NOT_FOUND_AS_DEBUG}. + * Log a 4xx client error. 404 NOT_FOUND is sent to a dedicated logger ({@link #notFoundLog}) + * at DEBUG level, all other 4xx errors are logged at WARN level. + * The 404 log level is controllable via standard Log4j2 configuration. */ private void logClientError(int statusCode, String message, String exceptionMessage, String location) { - if (statusCode == HttpServletResponse.SC_NOT_FOUND && isNotFoundLoggedAsDebug()) { - log.debug("{} (status:{} exception: {} at: {})", message, statusCode, + if (statusCode == HttpServletResponse.SC_NOT_FOUND) { + notFoundLog.debug("{} (status:{} exception: {} at: {})", message, statusCode, exceptionMessage, location); } else { log.warn("{} (status:{} exception: {} at: {})", message, statusCode, @@ -364,23 +359,6 @@ private void logClientError(int statusCode, String message, String exceptionMess } } - private boolean isNotFoundLoggedAsDebug() { - return configurationService.getBooleanProperty( - P_LOG_NOT_FOUND_AS_DEBUG, - LOG_NOT_FOUND_AS_DEBUG_DEFAULT - ); - } - - /** - * Check if a 404 response would be suppressed (debug-404 enabled but DEBUG logging off). - * Used to skip unnecessary stack trace extraction on frequent 404s in production. - */ - private boolean isNotFoundSuppressed(int statusCode) { - return statusCode == HttpServletResponse.SC_NOT_FOUND - && isNotFoundLoggedAsDebug() - && !log.isDebugEnabled(); - } - /** * Get set of status codes that should be treated as errors. * diff --git a/dspace/config/dspace.cfg b/dspace/config/dspace.cfg index 7941e7656a12..8330d7ee3cf1 100644 --- a/dspace/config/dspace.cfg +++ b/dspace/config/dspace.cfg @@ -244,10 +244,6 @@ logging.server.include-query-string = false # repeating the assignment logging.server.include-stacktrace-for-httpcode = 422 logging.server.max-payload-length = 10000 -# When true, 404 NOT_FOUND responses are logged at DEBUG level instead of WARN. -# This prevents flooding production logs with expected 404s (e.g. missing config properties). -# Set to false if you want to see 404 responses in WARN logs. -logging.server.debug-404 = true ##### DOI registration agency credentials ###### # To mint DOIs you have to use a DOI registration agency like DataCite. Several diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 1ccfe744de94..6be820cd0603 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -58,6 +58,13 @@ + + + + + + + + + + + + + + + Date: Fri, 13 Mar 2026 14:09:48 +0100 Subject: [PATCH 5/7] Suppress 404 warn logs via dedicated Log4j2 logger (org.dspace.app.rest.NotFound) --- .../DSpaceApiExceptionControllerAdvice.java | 12 ++++++------ dspace/config/log4j2-cli.xml | 2 +- dspace/config/log4j2-container.xml | 2 +- dspace/config/log4j2.xml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index 271d650ad8a2..bd910f2f4b4e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -62,9 +62,9 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH private static final Logger log = LogManager.getLogger(); /** - * Dedicated logger for 404 NOT_FOUND responses. Defaults to DEBUG level so that - * expected 404s (e.g. missing config properties) don't flood production logs. - * Level is controllable via standard Log4j2 configuration. + * Dedicated logger for 404 NOT_FOUND responses. Configured at OFF level by default + * so that expected 404s don't flood production logs. + * Set to WARN in log4j2.xml to see 404 responses in logs. */ private static final Logger notFoundLog = LogManager.getLogger("org.dspace.app.rest.NotFound"); @@ -346,12 +346,12 @@ private void sendErrorResponse(final HttpServletRequest request, /** * Log a 4xx client error. 404 NOT_FOUND is sent to a dedicated logger ({@link #notFoundLog}) - * at DEBUG level, all other 4xx errors are logged at WARN level. - * The 404 log level is controllable via standard Log4j2 configuration. + * at WARN level, but the logger is set to OFF by default in log4j2.xml (suppressed). + * Set logger to WARN in log4j2.xml to see 404 responses in logs. */ private void logClientError(int statusCode, String message, String exceptionMessage, String location) { if (statusCode == HttpServletResponse.SC_NOT_FOUND) { - notFoundLog.debug("{} (status:{} exception: {} at: {})", message, statusCode, + notFoundLog.warn("{} (status:{} exception: {} at: {})", message, statusCode, exceptionMessage, location); } else { log.warn("{} (status:{} exception: {} at: {})", message, statusCode, diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 6be820cd0603..02fc85ef9769 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -60,7 +60,7 @@ diff --git a/dspace/config/log4j2-container.xml b/dspace/config/log4j2-container.xml index 4f395d404665..81508a38ddcb 100644 --- a/dspace/config/log4j2-container.xml +++ b/dspace/config/log4j2-container.xml @@ -37,7 +37,7 @@ diff --git a/dspace/config/log4j2.xml b/dspace/config/log4j2.xml index 256609d10c06..f19d8931e820 100644 --- a/dspace/config/log4j2.xml +++ b/dspace/config/log4j2.xml @@ -91,7 +91,7 @@ From 5221ee20df5f505c51f1b17a5e5b61e34c6582c3 Mon Sep 17 00:00:00 2001 From: milanmajchrak Date: Fri, 13 Mar 2026 14:45:56 +0100 Subject: [PATCH 6/7] Turn off that warn logs for the dspace.log --- dspace/config/log4j2-cli.xml | 7 ------- dspace/config/log4j2-container.xml | 7 ------- 2 files changed, 14 deletions(-) diff --git a/dspace/config/log4j2-cli.xml b/dspace/config/log4j2-cli.xml index 02fc85ef9769..1ccfe744de94 100644 --- a/dspace/config/log4j2-cli.xml +++ b/dspace/config/log4j2-cli.xml @@ -58,13 +58,6 @@ - - - - - - - - - - Date: Fri, 13 Mar 2026 14:58:42 +0100 Subject: [PATCH 7/7] Updated log name to be more unique --- .../app/rest/exception/DSpaceApiExceptionControllerAdvice.java | 2 +- dspace/config/log4j2.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java index bd910f2f4b4e..7faf82f379f4 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/exception/DSpaceApiExceptionControllerAdvice.java @@ -66,7 +66,7 @@ public class DSpaceApiExceptionControllerAdvice extends ResponseEntityExceptionH * so that expected 404s don't flood production logs. * Set to WARN in log4j2.xml to see 404 responses in logs. */ - private static final Logger notFoundLog = LogManager.getLogger("org.dspace.app.rest.NotFound"); + private static final Logger notFoundLog = LogManager.getLogger("org.dspace.app.rest.exception.DSpaceApiExceptionControllerAdvice.NotFound"); /** * Default collection of HTTP error codes to log as ERROR with full stack trace. diff --git a/dspace/config/log4j2.xml b/dspace/config/log4j2.xml index f19d8931e820..a2ad06ee33a6 100644 --- a/dspace/config/log4j2.xml +++ b/dspace/config/log4j2.xml @@ -90,7 +90,7 @@ -