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]

Reply via email to