ppkarwasz commented on code in PR #3513: URL: https://github.com/apache/logging-log4j2/pull/3513#discussion_r2093225330
########## log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java: ########## @@ -43,16 +45,30 @@ public abstract static class AbstractFilterBuilder<B extends AbstractFilterBuild public static final String ATTR_ON_MISMATCH = "onMismatch"; public static final String ATTR_ON_MATCH = "onMatch"; + /** + * The action to perform when a match occurs. + */ @PluginBuilderAttribute(ATTR_ON_MATCH) - private Result onMatch = Result.NEUTRAL; + protected Result onMatch = Result.NEUTRAL; Review Comment: There is already a getter and a setter. Making this `protected` is not needed. ########## log4j-core/src/main/java/org/apache/logging/log4j/core/filter/AbstractFilter.java: ########## @@ -43,16 +45,30 @@ public abstract static class AbstractFilterBuilder<B extends AbstractFilterBuild public static final String ATTR_ON_MISMATCH = "onMismatch"; public static final String ATTR_ON_MATCH = "onMatch"; + /** + * The action to perform when a match occurs. + */ @PluginBuilderAttribute(ATTR_ON_MATCH) - private Result onMatch = Result.NEUTRAL; + protected Result onMatch = Result.NEUTRAL; + /** + * The action to perform when a mismatch occurs. + */ @PluginBuilderAttribute(ATTR_ON_MISMATCH) - private Result onMismatch = Result.DENY; + protected Result onMismatch = Result.DENY; Review Comment: There is already a getter and a setter. Making this `protected` is not needed. ########## log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java: ########## @@ -28,140 +25,312 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.message.StringFormattedMessage; +import org.apache.logging.log4j.message.StructuredDataMessage; import org.apache.logging.log4j.plugins.Configurable; import org.apache.logging.log4j.plugins.Plugin; -import org.apache.logging.log4j.plugins.PluginAttribute; -import org.apache.logging.log4j.plugins.PluginElement; +import org.apache.logging.log4j.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.plugins.PluginFactory; +import org.apache.logging.log4j.plugins.util.Assert; +import org.apache.logging.log4j.plugins.validation.constraints.Required; +import org.apache.logging.log4j.util.Strings; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** - * This filter returns the onMatch result if the message matches the regular expression. - * - * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to the result of - * calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). The default is false. - * + * This filter returns the {@code onMatch} result if the message exactly matches the configured + * "{@code regex}" regular-expression pattern; otherwise, it returns the {@code onMismatch} result. + * <p> + * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to + * the result of calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). + * The default is {@code false}. + * </p> */ @Configurable(elementType = Filter.ELEMENT_TYPE, printObject = true) +@NullMarked @Plugin public final class RegexFilter extends AbstractFilter { - private static final int DEFAULT_PATTERN_FLAGS = 0; + /** The pattern compiled from the regular-expression. */ private final Pattern pattern; + + /** Flag: if {@code true} use message format-pattern / field for the match target. */ private final boolean useRawMessage; - private RegexFilter(final boolean raw, final Pattern pattern, final Result onMatch, final Result onMismatch) { - super(onMatch, onMismatch); - this.pattern = pattern; - this.useRawMessage = raw; + /** + * Constructs a new {@code RegexFilter} configured by the given builder. + * @param builder the builder + * @throws IllegalArgumentException if the regular expression is not configured or cannot be compiled to a pattern + */ + private RegexFilter(final Builder builder) { + + super(builder); + + // NOTE: the constructor throws exceptions but is only called from Builder#build() where *null* + // should be returned for a misconfigured builder. *If* an exception is thrown here + // it will be caught and logged in the builder and not propagated by returning *null*. + + if (Strings.isBlank(builder.regex)) { + throw new IllegalArgumentException("The 'regex' attribute must not be null or empty."); + } + + this.useRawMessage = Boolean.TRUE.equals(builder.useRawMsg); + + try { + this.pattern = Pattern.compile(builder.regex); + } catch (final Exception ex) { + throw new IllegalArgumentException("Unable to compile regular expression: '" + builder.regex + "'.", ex); + } + } + + /** + * Returns the compiled regular-expression pattern. + * @return the pattern (will never be {@code null} + */ + public Pattern getPattern() { + return this.pattern; } Review Comment: Since there is already a `getRegex` method below, I think this method can be removed. ########## log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java: ########## @@ -28,140 +25,312 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.message.StringFormattedMessage; +import org.apache.logging.log4j.message.StructuredDataMessage; import org.apache.logging.log4j.plugins.Configurable; import org.apache.logging.log4j.plugins.Plugin; -import org.apache.logging.log4j.plugins.PluginAttribute; -import org.apache.logging.log4j.plugins.PluginElement; +import org.apache.logging.log4j.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.plugins.PluginFactory; +import org.apache.logging.log4j.plugins.util.Assert; +import org.apache.logging.log4j.plugins.validation.constraints.Required; +import org.apache.logging.log4j.util.Strings; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** - * This filter returns the onMatch result if the message matches the regular expression. - * - * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to the result of - * calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). The default is false. - * + * This filter returns the {@code onMatch} result if the message exactly matches the configured + * "{@code regex}" regular-expression pattern; otherwise, it returns the {@code onMismatch} result. + * <p> + * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to + * the result of calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). + * The default is {@code false}. + * </p> */ @Configurable(elementType = Filter.ELEMENT_TYPE, printObject = true) +@NullMarked @Plugin public final class RegexFilter extends AbstractFilter { - private static final int DEFAULT_PATTERN_FLAGS = 0; + /** The pattern compiled from the regular-expression. */ private final Pattern pattern; + + /** Flag: if {@code true} use message format-pattern / field for the match target. */ private final boolean useRawMessage; - private RegexFilter(final boolean raw, final Pattern pattern, final Result onMatch, final Result onMismatch) { - super(onMatch, onMismatch); - this.pattern = pattern; - this.useRawMessage = raw; + /** + * Constructs a new {@code RegexFilter} configured by the given builder. + * @param builder the builder + * @throws IllegalArgumentException if the regular expression is not configured or cannot be compiled to a pattern + */ + private RegexFilter(final Builder builder) { + + super(builder); + + // NOTE: the constructor throws exceptions but is only called from Builder#build() where *null* + // should be returned for a misconfigured builder. *If* an exception is thrown here + // it will be caught and logged in the builder and not propagated by returning *null*. + + if (Strings.isBlank(builder.regex)) { + throw new IllegalArgumentException("The 'regex' attribute must not be null or empty."); + } + + this.useRawMessage = Boolean.TRUE.equals(builder.useRawMsg); + + try { + this.pattern = Pattern.compile(builder.regex); + } catch (final Exception ex) { + throw new IllegalArgumentException("Unable to compile regular expression: '" + builder.regex + "'.", ex); + } + } + + /** + * Returns the compiled regular-expression pattern. + * @return the pattern (will never be {@code null} + */ + public Pattern getPattern() { + return this.pattern; } + /** + * Returns the regular-expression. + * @return the regular-expression (it may be an empty string but never {@code null}) + */ + public String getRegex() { + return this.pattern.pattern(); + } + + /** + * Returns whether the raw-message should be used. + * @return {@code true} if the raw message should be used; otherwise, {@code false} + */ + public boolean isUseRawMessage() { + return this.useRawMessage; + } + + /** + * {@inheritDoc} + * <p> + * This implementation performs the filter evaluation against the given message formatted with + * the given parameters. + * </p> + * <p> + * The following method arguments are ignored by this filter method implementation: + * <ul> + * <li>{@code logger}</li> + * <li>{@code level}</li> + * <li>{@code marker}</li> + * </ul> + * </p> + */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) { - if (useRawMessage || params == null || params.length == 0) { - return filter(msg); - } - return filter(ParameterizedMessage.format(msg, params)); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable String msg, + final @Nullable Object @Nullable ... params) { + + return (useRawMessage || params == null || params.length == 0) + ? filter(msg) + : filter(ParameterizedMessage.format(msg, params)); } + /** + * {@inheritDoc} + * <p> + * This implementation performs the filter evaluation against the given message. + * </p> + * <p> + * The following method arguments are ignored by this filter method implementation: + * <ul> + * <li>{@code logger}</li> + * <li>{@code level}</li> + * <li>{@code marker}</li> + * <li>{@code throwable}</li> + * </ul> + * </p> + */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) { - if (msg == null) { - return onMismatch; - } - return filter(msg.toString()); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable Object message, + final @Nullable Throwable throwable) { + + return (message == null) ? this.onMismatch : filter(message.toString()); } + /** + * {@inheritDoc} + * <p> + * This implementation performs the filter evaluation against the given message. + * </p> + * <p> + * The following method arguments are ignored by this filter method implementation: + * <ul> + * <li>{@code logger}</li> + * <li>{@code level}</li> + * <li>{@code marker}</li> + * <li>{@code throwable}</li> + * </ul> + * </p> + */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) { - if (msg == null) { - return onMismatch; - } - final String text = useRawMessage ? msg.getFormat() : msg.getFormattedMessage(); - return filter(text); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable Message message, + final @Nullable Throwable throwable) { + return (message == null) ? this.onMismatch : filter(getMessageTextByType(message)); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code event} argument is {@code null} + */ @Override public Result filter(final LogEvent event) { - final String text = useRawMessage - ? event.getMessage().getFormat() - : event.getMessage().getFormattedMessage(); - return filter(text); + Objects.requireNonNull(event, "The 'event' argument must not be null."); + return filter(getMessageTextByType(event.getMessage())); } - private Result filter(final String msg) { - if (msg == null) { - return onMismatch; - } - final Matcher m = pattern.matcher(msg); - return m.matches() ? onMatch : onMismatch; + /** + * Apply the filter to the given message and return the {@code onMatch} result if the <i>entire</i> + * message matches the configured regex pattern; otherwise, {@code onMismatch}. + * <p> + * If the given '{@code msg}' is {@code null} the configured {@code onMismatch} result will be returned. + * </p> + * @param msg the message + * @return the {@code onMatch} result if the pattern matches; otherwise, the {@code onMismatch} result + */ + public Result filter(final @Nullable String msg) { + return (msg != null && pattern.matcher(msg).matches()) ? onMatch : onMismatch; + } + + /** + * Tests the filter pattern against the given Log4j {@code Message}. + * <p> + * If the raw-message flag is enabled and message is an instance of the following, the raw message format + * will be returned. + * </p> + * <ul> + * <li>{@link ParameterizedMessage}</li> + * <li>{@link StringFormattedMessage}</li> + * <li>{@link StructuredDataMessage}</li> + * </ul> + * <p> + * If the '{@code useRawMessage}' flag is disabled <i>OR</i> the message is not one of the above + * implementations, the message's formatted message will be returned. + * </p> + * <h3>Developer Note</h3> + * <p> + * While `Message#getFormat()` is broken in general, it still makes sense for certain types. + * Hence, suppress the deprecation warning. + * </p> + * + * @param message the message + * @return the target message based on configuration and message-type + */ + @SuppressWarnings("deprecation") + private String getMessageTextByType(final Message message) { + return useRawMessage + && (message instanceof ParameterizedMessage + || message instanceof StringFormattedMessage + || message instanceof StructuredDataMessage) + ? message.getFormat() + : message.getFormattedMessage(); } + /** {@inheritDoc} */ @Override public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("useRaw=").append(useRawMessage); - sb.append(", pattern=").append(pattern.toString()); - return sb.toString(); + return "useRawMessage=" + useRawMessage + ", pattern=" + pattern; } /** - * Creates a Filter that matches a regular expression. - * - * @param regex - * The regular expression to match. - * @param patternFlags - * An array of Strings where each String is a {@link Pattern#compile(String, int)} compilation flag. - * @param useRawMsg - * If true, the raw message will be used, otherwise the formatted message will be used. - * @param onMatch - * The action to perform when a match occurs. - * @param onMismatch - * The action to perform when a mismatch occurs. - * @return The RegexFilter. - * @throws IllegalAccessException - * @throws IllegalArgumentException + * Creates a new builder instance. + * @return the new builder instance */ - // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder @PluginFactory - public static RegexFilter createFilter( - // @formatter:off - @PluginAttribute final String regex, - @PluginElement final String[] patternFlags, - @PluginAttribute final Boolean useRawMsg, - @PluginAttribute final Result onMatch, - @PluginAttribute final Result onMismatch) - // @formatter:on - throws IllegalArgumentException, IllegalAccessException { - if (regex == null) { - LOGGER.error("A regular expression must be provided for RegexFilter"); - return null; - } - return new RegexFilter(useRawMsg, Pattern.compile(regex, toPatternFlags(patternFlags)), onMatch, onMismatch); + public static Builder newBuilder() { + return new Builder(); } Review Comment: A plugin builder factory must be annotated with `@PluginBuilderFactory` instead of `@PluginFactory`. ########## log4j-core/src/main/java/org/apache/logging/log4j/core/filter/RegexFilter.java: ########## @@ -28,140 +25,312 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.message.StringFormattedMessage; +import org.apache.logging.log4j.message.StructuredDataMessage; import org.apache.logging.log4j.plugins.Configurable; import org.apache.logging.log4j.plugins.Plugin; -import org.apache.logging.log4j.plugins.PluginAttribute; -import org.apache.logging.log4j.plugins.PluginElement; +import org.apache.logging.log4j.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.plugins.PluginFactory; +import org.apache.logging.log4j.plugins.util.Assert; +import org.apache.logging.log4j.plugins.validation.constraints.Required; +import org.apache.logging.log4j.util.Strings; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** - * This filter returns the onMatch result if the message matches the regular expression. - * - * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to the result of - * calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). The default is false. - * + * This filter returns the {@code onMatch} result if the message exactly matches the configured + * "{@code regex}" regular-expression pattern; otherwise, it returns the {@code onMismatch} result. + * <p> + * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to + * the result of calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). + * The default is {@code false}. + * </p> */ @Configurable(elementType = Filter.ELEMENT_TYPE, printObject = true) +@NullMarked @Plugin public final class RegexFilter extends AbstractFilter { - private static final int DEFAULT_PATTERN_FLAGS = 0; + /** The pattern compiled from the regular-expression. */ private final Pattern pattern; + + /** Flag: if {@code true} use message format-pattern / field for the match target. */ private final boolean useRawMessage; - private RegexFilter(final boolean raw, final Pattern pattern, final Result onMatch, final Result onMismatch) { - super(onMatch, onMismatch); - this.pattern = pattern; - this.useRawMessage = raw; + /** + * Constructs a new {@code RegexFilter} configured by the given builder. + * @param builder the builder + * @throws IllegalArgumentException if the regular expression is not configured or cannot be compiled to a pattern + */ + private RegexFilter(final Builder builder) { + + super(builder); + + // NOTE: the constructor throws exceptions but is only called from Builder#build() where *null* + // should be returned for a misconfigured builder. *If* an exception is thrown here + // it will be caught and logged in the builder and not propagated by returning *null*. + + if (Strings.isBlank(builder.regex)) { + throw new IllegalArgumentException("The 'regex' attribute must not be null or empty."); + } + + this.useRawMessage = Boolean.TRUE.equals(builder.useRawMsg); + + try { + this.pattern = Pattern.compile(builder.regex); + } catch (final Exception ex) { + throw new IllegalArgumentException("Unable to compile regular expression: '" + builder.regex + "'.", ex); + } + } + + /** + * Returns the compiled regular-expression pattern. + * @return the pattern (will never be {@code null} + */ + public Pattern getPattern() { + return this.pattern; } + /** + * Returns the regular-expression. + * @return the regular-expression (it may be an empty string but never {@code null}) + */ + public String getRegex() { + return this.pattern.pattern(); + } + + /** + * Returns whether the raw-message should be used. + * @return {@code true} if the raw message should be used; otherwise, {@code false} + */ + public boolean isUseRawMessage() { + return this.useRawMessage; + } + + /** + * {@inheritDoc} + * <p> + * This implementation performs the filter evaluation against the given message formatted with + * the given parameters. + * </p> + * <p> + * The following method arguments are ignored by this filter method implementation: + * <ul> + * <li>{@code logger}</li> + * <li>{@code level}</li> + * <li>{@code marker}</li> + * </ul> + * </p> + */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) { - if (useRawMessage || params == null || params.length == 0) { - return filter(msg); - } - return filter(ParameterizedMessage.format(msg, params)); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable String msg, + final @Nullable Object @Nullable ... params) { + + return (useRawMessage || params == null || params.length == 0) + ? filter(msg) + : filter(ParameterizedMessage.format(msg, params)); } + /** + * {@inheritDoc} + * <p> + * This implementation performs the filter evaluation against the given message. + * </p> + * <p> + * The following method arguments are ignored by this filter method implementation: + * <ul> + * <li>{@code logger}</li> + * <li>{@code level}</li> + * <li>{@code marker}</li> + * <li>{@code throwable}</li> + * </ul> + * </p> + */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) { - if (msg == null) { - return onMismatch; - } - return filter(msg.toString()); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable Object message, + final @Nullable Throwable throwable) { + + return (message == null) ? this.onMismatch : filter(message.toString()); } + /** + * {@inheritDoc} + * <p> + * This implementation performs the filter evaluation against the given message. + * </p> + * <p> + * The following method arguments are ignored by this filter method implementation: + * <ul> + * <li>{@code logger}</li> + * <li>{@code level}</li> + * <li>{@code marker}</li> + * <li>{@code throwable}</li> + * </ul> + * </p> + */ @Override public Result filter( - final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) { - if (msg == null) { - return onMismatch; - } - final String text = useRawMessage ? msg.getFormat() : msg.getFormattedMessage(); - return filter(text); + final @Nullable Logger logger, + final @Nullable Level level, + final @Nullable Marker marker, + final @Nullable Message message, + final @Nullable Throwable throwable) { + return (message == null) ? this.onMismatch : filter(getMessageTextByType(message)); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code event} argument is {@code null} + */ @Override public Result filter(final LogEvent event) { - final String text = useRawMessage - ? event.getMessage().getFormat() - : event.getMessage().getFormattedMessage(); - return filter(text); + Objects.requireNonNull(event, "The 'event' argument must not be null."); + return filter(getMessageTextByType(event.getMessage())); } - private Result filter(final String msg) { - if (msg == null) { - return onMismatch; - } - final Matcher m = pattern.matcher(msg); - return m.matches() ? onMatch : onMismatch; + /** + * Apply the filter to the given message and return the {@code onMatch} result if the <i>entire</i> + * message matches the configured regex pattern; otherwise, {@code onMismatch}. + * <p> + * If the given '{@code msg}' is {@code null} the configured {@code onMismatch} result will be returned. + * </p> + * @param msg the message + * @return the {@code onMatch} result if the pattern matches; otherwise, the {@code onMismatch} result + */ + public Result filter(final @Nullable String msg) { + return (msg != null && pattern.matcher(msg).matches()) ? onMatch : onMismatch; + } + + /** + * Tests the filter pattern against the given Log4j {@code Message}. + * <p> + * If the raw-message flag is enabled and message is an instance of the following, the raw message format + * will be returned. + * </p> + * <ul> + * <li>{@link ParameterizedMessage}</li> + * <li>{@link StringFormattedMessage}</li> + * <li>{@link StructuredDataMessage}</li> + * </ul> + * <p> + * If the '{@code useRawMessage}' flag is disabled <i>OR</i> the message is not one of the above + * implementations, the message's formatted message will be returned. + * </p> + * <h3>Developer Note</h3> + * <p> + * While `Message#getFormat()` is broken in general, it still makes sense for certain types. + * Hence, suppress the deprecation warning. + * </p> + * + * @param message the message + * @return the target message based on configuration and message-type + */ + @SuppressWarnings("deprecation") + private String getMessageTextByType(final Message message) { + return useRawMessage + && (message instanceof ParameterizedMessage + || message instanceof StringFormattedMessage + || message instanceof StructuredDataMessage) + ? message.getFormat() + : message.getFormattedMessage(); } + /** {@inheritDoc} */ @Override public String toString() { - final StringBuilder sb = new StringBuilder(); - sb.append("useRaw=").append(useRawMessage); - sb.append(", pattern=").append(pattern.toString()); - return sb.toString(); + return "useRawMessage=" + useRawMessage + ", pattern=" + pattern; } /** - * Creates a Filter that matches a regular expression. - * - * @param regex - * The regular expression to match. - * @param patternFlags - * An array of Strings where each String is a {@link Pattern#compile(String, int)} compilation flag. - * @param useRawMsg - * If true, the raw message will be used, otherwise the formatted message will be used. - * @param onMatch - * The action to perform when a match occurs. - * @param onMismatch - * The action to perform when a mismatch occurs. - * @return The RegexFilter. - * @throws IllegalAccessException - * @throws IllegalArgumentException + * Creates a new builder instance. + * @return the new builder instance */ - // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder @PluginFactory - public static RegexFilter createFilter( - // @formatter:off - @PluginAttribute final String regex, - @PluginElement final String[] patternFlags, - @PluginAttribute final Boolean useRawMsg, - @PluginAttribute final Result onMatch, - @PluginAttribute final Result onMismatch) - // @formatter:on - throws IllegalArgumentException, IllegalAccessException { - if (regex == null) { - LOGGER.error("A regular expression must be provided for RegexFilter"); - return null; - } - return new RegexFilter(useRawMsg, Pattern.compile(regex, toPatternFlags(patternFlags)), onMatch, onMismatch); Review Comment: For backward compatibility, we can not remove this method, but we can: - Remove the `@PluginFactory` annotation. - Deprecated it. - Rewrite it in terms of builder calls. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@logging.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org