This is an automated email from the ASF dual-hosted git repository.
vy pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/2.x by this push:
new 1fdfe142d5 Allow severity-based log message filtering in
`PluginProcessor` (#4063)
1fdfe142d5 is described below
commit 1fdfe142d57febdd22a9e7d91ccdcc6d71a6efb0
Author: treyfarmigoni <[email protected]>
AuthorDate: Tue Mar 17 14:59:43 2026 -0500
Allow severity-based log message filtering in `PluginProcessor` (#4063)
---
.../processor/PluginProcessorPublicSetterTest.java | 72 ++++++++++++++++---
.../config/plugins/processor/PluginProcessor.java | 81 +++++++++++++++++++---
.../plugin_processor_min_allowed_message_kind.xml | 14 ++++
.../antora/modules/ROOT/pages/manual/plugins.adoc | 32 +++++++++
4 files changed, 180 insertions(+), 19 deletions(-)
diff --git
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorPublicSetterTest.java
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorPublicSetterTest.java
index 587490e2c9..2b6a03d1fe 100644
---
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorPublicSetterTest.java
+++
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessorPublicSetterTest.java
@@ -37,6 +37,8 @@ import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
public class PluginProcessorPublicSetterTest {
@@ -50,6 +52,10 @@ public class PluginProcessorPublicSetterTest {
@BeforeEach
void setup() {
+ setupWithOptions();
+ }
+
+ private void setupWithOptions(final String... extraOptions) {
// Instantiate the tooling
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
diagnosticCollector = new DiagnosticCollector<>();
@@ -67,13 +73,12 @@ public class PluginProcessorPublicSetterTest {
// get compilation units
Iterable<? extends JavaFileObject> compilationUnits =
fileManager.getJavaFileObjects(createdFile);
- JavaCompiler.CompilationTask task = compiler.getTask(
- null,
- fileManager,
- diagnosticCollector,
- Arrays.asList("-proc:only", "-processor",
PluginProcessor.class.getName()),
- null,
- compilationUnits);
+ final List<String> args =
+ new java.util.ArrayList<>(Arrays.asList("-proc:only",
"-processor", PluginProcessor.class.getName()));
+ args.addAll(Arrays.asList(extraOptions));
+
+ JavaCompiler.CompilationTask task =
+ compiler.getTask(null, fileManager, diagnosticCollector, args,
null, compilationUnits);
task.call();
errorDiagnostics = diagnosticCollector.getDiagnostics().stream()
@@ -92,7 +97,6 @@ public class PluginProcessorPublicSetterTest {
@Test
void warnWhenPluginBuilderAttributeLacksPublicSetter() {
-
assertThat(errorDiagnostics).anyMatch(errorMessage -> errorMessage
.getMessage(Locale.ROOT)
.contains("The field `attribute` does not have a public
setter"));
@@ -107,4 +111,56 @@ public class PluginProcessorPublicSetterTest {
.contains(
"The field
`attributeWithoutPublicSetterButWithSuppressAnnotation` does not have a public
setter"));
}
+
+ @Test
+ void noteEmittedByDefault() {
+ final List<Diagnostic<? extends JavaFileObject>> noteDiagnostics =
diagnosticCollector.getDiagnostics().stream()
+ .filter(d -> d.getKind() == Diagnostic.Kind.NOTE)
+ .collect(Collectors.toList());
+ assertThat(noteDiagnostics).anyMatch(d ->
d.getMessage(Locale.ROOT).contains("writing plugin descriptor"));
+ }
+
+ @Test
+ void notesSuppressedWhenMinKindIsError() {
+ setupWithOptions("-A" +
PluginProcessor.MIN_ALLOWED_MESSAGE_KIND_OPTION + "=ERROR");
+
+ final List<Diagnostic<? extends JavaFileObject>> noteDiagnostics =
diagnosticCollector.getDiagnostics().stream()
+ .filter(d -> d.getKind() == Diagnostic.Kind.NOTE)
+ .collect(Collectors.toList());
+ assertThat(noteDiagnostics).noneMatch(d ->
d.getMessage(Locale.ROOT).contains("writing plugin descriptor"));
+ }
+
+ @Test
+ void errorsStillEmittedWhenMinKindIsError() {
+ setupWithOptions("-A" +
PluginProcessor.MIN_ALLOWED_MESSAGE_KIND_OPTION + "=ERROR");
+
+ assertThat(errorDiagnostics).anyMatch(d -> d.getMessage(Locale.ROOT)
+ .contains("The field `attribute` does not have a public
setter"));
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"NOTE", "note"})
+ void explicitNoteKindBehavesLikeDefault(final String kindValue) {
+ setupWithOptions("-A" +
PluginProcessor.MIN_ALLOWED_MESSAGE_KIND_OPTION + "=" + kindValue);
+
+ assertThat(errorDiagnostics).anyMatch(d -> d.getMessage(Locale.ROOT)
+ .contains("The field `attribute` does not have a public
setter"));
+
+ final List<Diagnostic<? extends JavaFileObject>> noteDiagnostics =
diagnosticCollector.getDiagnostics().stream()
+ .filter(d -> d.getKind() == Diagnostic.Kind.NOTE)
+ .collect(Collectors.toList());
+ assertThat(noteDiagnostics).anyMatch(d ->
d.getMessage(Locale.ROOT).contains("writing plugin descriptor"));
+ }
+
+ @Test
+ void invalidKindValueEmitsWarning() {
+ setupWithOptions("-A" +
PluginProcessor.MIN_ALLOWED_MESSAGE_KIND_OPTION + "=INVALID");
+
+ final List<Diagnostic<? extends JavaFileObject>> warningDiagnostics =
+ diagnosticCollector.getDiagnostics().stream()
+ .filter(d -> d.getKind() == Diagnostic.Kind.WARNING)
+ .collect(Collectors.toList());
+ assertThat(warningDiagnostics)
+ .anyMatch(d ->
d.getMessage(Locale.ROOT).contains("unrecognized value `INVALID`"));
+ }
}
diff --git
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java
index c01dfa5d59..94557a7ddb 100644
---
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java
+++
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.java
@@ -35,9 +35,11 @@ import java.util.Objects;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
@@ -60,6 +62,7 @@ import org.apache.logging.log4j.util.Strings;
*/
@ServiceProvider(value = Processor.class, resolution = Resolution.OPTIONAL)
@SupportedAnnotationTypes("org.apache.logging.log4j.core.config.plugins.Plugin")
+@SupportedOptions("log4j.plugin.processor.minAllowedMessageKind")
public class PluginProcessor extends AbstractProcessor {
// TODO: this could be made more abstract to allow for compile-time and
run-time plugin processing
@@ -68,6 +71,21 @@ public class PluginProcessor extends AbstractProcessor {
private static final String SUPPRESS_WARNING_PUBLIC_SETTER_STRING =
"log4j.public.setter";
+ /**
+ * Annotation processor option that controls the minimum {@link
Diagnostic.Kind} of messages emitted by this
+ * processor.
+ * <p>
+ * Some build environments (e.g. Maven with {@code -Werror}) treat
compiler notes or warnings as errors.
+ * Setting this option to {@code WARNING} or {@code ERROR} suppresses
informational notes emitted during
+ * normal processing.
+ * </p>
+ * <p>
+ * Accepted values (case-insensitive): {@code NOTE}, {@code WARNING},
{@code MANDATORY_WARNING},
+ * {@code ERROR}, {@code OTHER}. Defaults to {@code NOTE}.
+ * </p>
+ */
+ static final String MIN_ALLOWED_MESSAGE_KIND_OPTION =
"log4j.plugin.processor.minAllowedMessageKind";
+
/**
* The location of the plugin cache data file. This file is written to by
this processor, and read from by
* {@link org.apache.logging.log4j.core.config.plugins.util.PluginManager}.
@@ -77,6 +95,30 @@ public class PluginProcessor extends AbstractProcessor {
private final List<Element> processedElements = new ArrayList<>();
private final PluginCache pluginCache = new PluginCache();
+ private Diagnostic.Kind minAllowedMessageKind = Diagnostic.Kind.NOTE;
+
+ @Override
+ public void init(final ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ final String kindValue =
processingEnv.getOptions().get(MIN_ALLOWED_MESSAGE_KIND_OPTION);
+ if (kindValue != null) {
+ try {
+ minAllowedMessageKind =
Diagnostic.Kind.valueOf(kindValue.toUpperCase(Locale.ROOT));
+ } catch (final IllegalArgumentException e) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Diagnostic.Kind.WARNING,
+ String.format(
+ "%s: unrecognized value `%s` for
option `%s`, using default `%s`. Valid values: %s",
+ PluginProcessor.class.getName(),
+ kindValue,
+ MIN_ALLOWED_MESSAGE_KIND_OPTION,
+ Diagnostic.Kind.NOTE,
+
Arrays.toString(Diagnostic.Kind.values())));
+ }
+ }
+ }
@Override
public SourceVersion getSupportedSourceVersion() {
@@ -86,9 +128,28 @@ public class PluginProcessor extends AbstractProcessor {
private static final String PLUGIN_BUILDER_ATTRIBUTE_ANNOTATION =
"org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute";
+ /**
+ * Prints a message via the {@link Messager} only if {@code kind} is at
least as severe as the configured
+ * {@link #MIN_ALLOWED_MESSAGE_KIND_OPTION minimum allowed kind}.
+ */
+ private void printMessage(final Diagnostic.Kind kind, final String
message) {
+ if (kind.ordinal() <= minAllowedMessageKind.ordinal()) {
+ processingEnv.getMessager().printMessage(kind, message);
+ }
+ }
+
+ /**
+ * Prints a message via the {@link Messager} only if {@code kind} is at
least as severe as the configured
+ * {@link #MIN_ALLOWED_MESSAGE_KIND_OPTION minimum allowed kind}.
+ */
+ private void printMessage(final Diagnostic.Kind kind, final String
message, final Element element) {
+ if (kind.ordinal() <= minAllowedMessageKind.ordinal()) {
+ processingEnv.getMessager().printMessage(kind, message, element);
+ }
+ }
+
@Override
public boolean process(final Set<? extends TypeElement> annotations, final
RoundEnvironment roundEnv) {
- final Messager messager = processingEnv.getMessager();
final Elements elementUtils = processingEnv.getElementUtils();
// Process the elements for this round
if (!annotations.isEmpty()) {
@@ -105,7 +166,7 @@ public class PluginProcessor extends AbstractProcessor {
// Write the cache file
if (roundEnv.processingOver() && !processedElements.isEmpty()) {
try {
- messager.printMessage(
+ printMessage(
Diagnostic.Kind.NOTE,
String.format(
"%s: writing plugin descriptor for %d Log4j
Plugins to `%s`.",
@@ -118,7 +179,7 @@ public class PluginProcessor extends AbstractProcessor {
.append(PLUGIN_CACHE_FILE)
.append("\n");
e.printStackTrace(new PrintWriter(sw));
-
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, sw.toString());
+ printMessage(Diagnostic.Kind.ERROR, sw.toString());
}
}
// Do not claim the annotations to allow other annotation processors
to run
@@ -180,14 +241,12 @@ public class PluginProcessor extends AbstractProcessor {
}
}
// If the setter was not found generate a compiler warning.
- processingEnv
- .getMessager()
- .printMessage(
- Diagnostic.Kind.ERROR,
- String.format(
- "The field `%s` does not have a public
setter, Note that @SuppressWarnings(\"%s\"), can be used on the field to
suppress the compilation error. ",
- fieldName,
SUPPRESS_WARNING_PUBLIC_SETTER_STRING),
- element);
+ printMessage(
+ Diagnostic.Kind.ERROR,
+ String.format(
+ "The field `%s` does not have a public setter,
Note that @SuppressWarnings(\"%s\"), can be used on the field to suppress the
compilation error. ",
+ fieldName, SUPPRESS_WARNING_PUBLIC_SETTER_STRING),
+ element);
}
}
diff --git a/src/changelog/.2.x.x/plugin_processor_min_allowed_message_kind.xml
b/src/changelog/.2.x.x/plugin_processor_min_allowed_message_kind.xml
new file mode 100644
index 0000000000..3663ec2850
--- /dev/null
+++ b/src/changelog/.2.x.x/plugin_processor_min_allowed_message_kind.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ https://logging.apache.org/xml/ns
+ https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+ type="added">
+ <issue id="3380"
link="https://github.com/apache/logging-log4j2/discussions/3380"/>
+ <issue id="4063" link="https://github.com/apache/logging-log4j2/pull/4063"/>
+ <description format="asciidoc">
+ Add `log4j.plugin.processor.minAllowedMessageKind` annotation processor
option to `PluginProcessor` to filter diagnostic messages by severity.
+ This allows builds that treat compiler notes as errors (e.g. Maven with
`-Werror`) to suppress informational notes emitted during normal plugin
processing.
+ </description>
+</entry>
diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc
b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc
index 6b751f0736..f8d6b5ff4b 100644
--- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc
@@ -215,6 +215,38 @@ The `GraalVmProcessor` requires your project's `groupId`
and `artifactId` to cor
Provide these values to the processor using the `log4j.graalvm.groupId` and
`log4j.graalvm.artifactId` annotation processor options.
====
+.Suppressing notes from `PluginProcessor` in strict build environments
+[%collapsible]
+====
+Some build environments treat all compiler notes or warnings as errors (e.g.,
Maven with `-Werror` or Gradle with `options.compilerArgs << '-Werror'`).
+By default, `PluginProcessor` emits a `NOTE`-level diagnostic when it writes
the plugin descriptor, which can cause the build to fail in those environments.
+To suppress these informational notes, pass the
`log4j.plugin.processor.minAllowedMessageKind` annotation processor option with
a value of `WARNING` or `ERROR`.
+This instructs the processor to only emit diagnostics at or above the
specified severity, silencing routine notes while preserving genuine warnings
and errors.
+
+Accepted values (case-insensitive): `NOTE` (default), `WARNING`,
`MANDATORY_WARNING`, `ERROR`, `OTHER`.
+
+[tabs]
+=====
+Maven::
++
+[source,xml]
+----
+<compilerArgs>
+ <arg>-Alog4j.plugin.processor.minAllowedMessageKind=WARNING</arg>
+</compilerArgs>
+----
+
+Gradle::
++
+[source,groovy]
+----
+compileJava {
+ options.compilerArgs <<
'-Alog4j.plugin.processor.minAllowedMessageKind=WARNING'
+}
+----
+=====
+====
+
You need to configure your build tool as follows to use both plugin processors:
[tabs]