Add final modifier to method parameters. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/7a5dcd43 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/7a5dcd43 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/7a5dcd43
Branch: refs/heads/LOG4J2-1395 Commit: 7a5dcd43d559971caf3f00c55586f372ca4d9046 Parents: fe22ab8 Author: ggregory <[email protected]> Authored: Fri Jun 3 10:05:18 2016 -0700 Committer: ggregory <[email protected]> Committed: Fri Jun 3 10:05:18 2016 -0700 ---------------------------------------------------------------------- .../org/apache/logging/log4j/core/Logger.java | 2 +- .../logging/log4j/core/LoggerContext.java | 4 +- .../log4j/core/appender/AbstractManager.java | 8 +- .../log4j/core/appender/AsyncAppender.java | 2 +- .../log4j/core/appender/ConsoleAppender.java | 2 +- .../core/appender/CountingNoOpAppender.java | 4 +- .../core/appender/OutputStreamManager.java | 4 +- .../log4j/core/appender/SocketAppender.java | 2 +- .../appender/rolling/CronTriggeringPolicy.java | 6 +- .../rolling/DefaultRolloverStrategy.java | 6 +- .../log4j/core/appender/rolling/FileSize.java | 180 ++--- .../core/appender/rolling/action/Duration.java | 514 +++++++------- .../core/appender/rolling/action/IfAll.java | 232 +++---- .../core/appender/routing/IdlePurgePolicy.java | 8 +- .../core/appender/routing/RoutingAppender.java | 4 +- .../logging/log4j/core/async/AsyncLogger.java | 4 +- .../core/async/AsyncLoggerConfigDisruptor.java | 2 +- .../log4j/core/async/AsyncLoggerContext.java | 4 +- .../log4j/core/async/AsyncLoggerDisruptor.java | 472 ++++++------- .../log4j/core/async/RingBufferLogEvent.java | 10 +- .../core/config/AbstractConfiguration.java | 8 +- .../AwaitCompletionReliabilityStrategy.java | 350 +++++----- ...AwaitUnconditionallyReliabilityStrategy.java | 246 +++---- .../log4j/core/config/ConfigurationFactory.java | 2 +- .../core/config/ConfigurationScheduler.java | 16 +- .../core/config/ConfiguratonFileWatcher.java | 4 +- .../logging/log4j/core/config/Configurator.java | 688 +++++++++---------- .../core/config/DefaultReliabilityStrategy.java | 214 +++--- .../core/config/LockingReliabilityStrategy.java | 270 ++++---- .../impl/DefaultConfigurationBuilder.java | 20 +- .../impl/DefaultLoggerComponentBuilder.java | 4 +- .../impl/DefaultScriptFileComponentBuilder.java | 8 +- .../composite/CompositeConfiguration.java | 6 +- .../config/composite/DefaultMergeStrategy.java | 16 +- .../properties/PropertiesConfiguration.java | 2 +- .../PropertiesConfigurationBuilder.java | 2 +- .../log4j/core/config/xml/XmlConfiguration.java | 2 +- .../core/filter/DynamicThresholdFilter.java | 2 +- .../logging/log4j/core/impl/Log4jLogEvent.java | 32 +- .../log4j/core/impl/MutableLogEvent.java | 30 +- .../log4j/core/layout/AbstractStringLayout.java | 2 +- .../logging/log4j/core/layout/GelfLayout.java | 4 +- .../log4j/core/layout/JacksonFactory.java | 4 +- .../core/layout/MarkerPatternSelector.java | 2 +- .../core/layout/ScriptPatternSelector.java | 4 +- .../log4j/core/layout/TextEncoderHelper.java | 2 +- .../logging/log4j/core/net/SmtpManager.java | 4 +- .../log4j/core/net/TcpSocketManager.java | 2 +- .../pattern/EqualsReplacementConverter.java | 2 +- .../log4j/core/pattern/NameAbbreviator.java | 4 +- .../logging/log4j/core/script/Script.java | 2 +- .../logging/log4j/core/script/ScriptFile.java | 2 +- .../log4j/core/script/ScriptManager.java | 2 +- .../logging/log4j/core/script/ScriptRef.java | 4 +- .../logging/log4j/core/util/CronExpression.java | 48 +- .../logging/log4j/core/util/DummyNanoClock.java | 96 +-- .../core/util/ExtensionLanguageMapping.java | 6 +- .../logging/log4j/core/util/FileUtils.java | 2 +- .../logging/log4j/core/util/JsonUtils.java | 6 +- .../logging/log4j/core/util/StringEncoder.java | 6 +- .../logging/log4j/core/util/WatchManager.java | 10 +- .../core/util/datetime/FixedDateFormat.java | 686 +++++++++--------- .../core/GcFreeAsynchronousLoggingTest.java | 2 +- .../log4j/core/GcFreeLoggingTestUtil.java | 4 +- .../core/GcFreeMixedSyncAyncLoggingTest.java | 2 +- .../core/GcFreeSynchronousLoggingTest.java | 2 +- .../AsyncAppenderQueueFullPolicyTest.java | 2 +- .../appender/mom/kafka/KafkaAppenderTest.java | 292 ++++---- .../RollingAppenderCustomDeleteActionTest.java | 2 +- ...lingAppenderDeleteAccumulatedCount1Test.java | 4 +- ...lingAppenderDeleteAccumulatedCount2Test.java | 4 +- .../RollingAppenderDeleteMaxDepthTest.java | 4 +- .../RollingAppenderDeleteNestedTest.java | 4 +- ...ollingAppenderNoUnconditionalDeleteTest.java | 2 +- .../rolling/action/CountingCondition.java | 124 ++-- .../rolling/action/DeleteActionTest.java | 238 +++---- .../rolling/action/DeletingVisitorTest.java | 270 ++++---- .../appender/rolling/action/FixedCondition.java | 88 +-- .../action/PathSortByModificationTimeTest.java | 186 ++--- .../routing/RoutingAppenderWithPurgingTest.java | 2 +- .../core/async/perftest/PerfTestDriver.java | 4 +- .../core/async/perftest/ResponseTimeTest.java | 18 +- .../core/async/perftest/SimplePerfTest.java | 2 +- .../core/config/CompositeConfigurationTest.java | 2 +- .../log4j/core/config/ConfiguratorTest.java | 94 +-- .../builder/ConfigurationAssemblerTest.java | 2 +- .../builder/CustomConfigurationFactory.java | 4 +- .../core/filter/AbstractFilterableTest.java | 4 +- .../log4j/core/impl/Log4jLogEventTest.java | 6 +- .../log4j/core/impl/ThrowableProxyTest.java | 2 +- .../core/layout/CsvLogEventLayoutTest.java | 2 +- .../core/layout/CsvParameterLayoutTest.java | 2 +- .../log4j/core/layout/GelfLayoutTest.java | 2 +- .../log4j/core/layout/PatternLayoutTest.java | 2 +- .../log4j/core/layout/PatternSelectorTest.java | 130 ++-- .../core/layout/SpyByteBufferDestination.java | 2 +- .../core/pattern/DatePatternConverterTest.java | 2 +- ...qualsIgnoreCaseReplacementConverterTest.java | 2 +- .../log4j/core/pattern/PatternParserTest.java | 2 +- ...riablesNotEmptyReplacementConverterTest.java | 2 +- .../log4j/core/util/WatchManagerTest.java | 4 +- .../logging/log4j/junit/LoggerContextRule.java | 4 +- .../test/appender/EncodingListAppender.java | 4 +- .../logging/log4j/jul/AbstractLoggerTest.java | 4 +- 104 files changed, 2913 insertions(+), 2913 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java index d08dd8a..99f4207 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java @@ -632,7 +632,7 @@ public class Logger extends AbstractLogger implements Supplier<LoggerConfig> { private final String name; private final MessageFactory messageFactory; - public LoggerProxy(String name, MessageFactory messageFactory) { + public LoggerProxy(final String name, final MessageFactory messageFactory) { this.name = name; this.messageFactory = messageFactory; } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java index 82a6de8..fea1975 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java @@ -424,7 +424,7 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi * @return True if the Logger exists, false otherwise. */ @Override - public boolean hasLogger(final String name, MessageFactory messageFactory) { + public boolean hasLogger(final String name, final MessageFactory messageFactory) { return loggerRegistry.hasLogger(name, messageFactory); } @@ -435,7 +435,7 @@ public class LoggerContext extends AbstractLifeCycle implements org.apache.loggi * @return True if the Logger exists, false otherwise. */ @Override - public boolean hasLogger(final String name, Class<? extends MessageFactory> messageFactoryClass) { + public boolean hasLogger(final String name, final Class<? extends MessageFactory> messageFactoryClass) { return loggerRegistry.hasLogger(name, messageFactoryClass); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java index 45f5141..fd15f17 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java @@ -150,21 +150,21 @@ public abstract class AbstractManager { return new HashMap<>(); } - protected void log(Level level, String message, Throwable throwable) { + protected void log(final Level level, final String message, final Throwable throwable) { Message m = LOGGER.getMessageFactory().newMessage("{} {} {}: {}", getClass().getSimpleName(), getName(), message, throwable); LOGGER.log(level, m, throwable); } - protected void logDebug(String message, Throwable throwable) { + protected void logDebug(final String message, final Throwable throwable) { log(Level.DEBUG, message, throwable); } - protected void logError(String message, Throwable throwable) { + protected void logError(final String message, final Throwable throwable) { log(Level.ERROR, message, throwable); } - protected void logWarn(String message, Throwable throwable) { + protected void logWarn(final String message, final Throwable throwable) { log(Level.WARN, message, throwable); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java index 83afd92..0abb07e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java @@ -141,7 +141,7 @@ public final class AsyncAppender extends AbstractAppender { * @param logEvent The LogEvent. */ @Override - public void append(LogEvent logEvent) { + public void append(final LogEvent logEvent) { if (!isStarted()) { throw new IllegalStateException("AsyncAppender " + getName() + " is not active"); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java index 4e1e87c..0eb05d8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ConsoleAppender.java @@ -69,7 +69,7 @@ public final class ConsoleAppender extends AbstractOutputStreamAppender<OutputSt } private ConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, - final OutputStreamManager manager, final boolean ignoreExceptions, Target target) { + final OutputStreamManager manager, final boolean ignoreExceptions, final Target target) { super(name, layout, filter, ignoreExceptions, true, manager); this.target = target; } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/CountingNoOpAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/CountingNoOpAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/CountingNoOpAppender.java index 8164aea..38775eb 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/CountingNoOpAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/CountingNoOpAppender.java @@ -33,7 +33,7 @@ public class CountingNoOpAppender extends AbstractAppender { private final AtomicLong total = new AtomicLong(); - public CountingNoOpAppender(String name, Layout<?> layout) { + public CountingNoOpAppender(final String name, final Layout<?> layout) { super(name, null, layout); } @@ -42,7 +42,7 @@ public class CountingNoOpAppender extends AbstractAppender { } @Override - public void append(LogEvent event) { + public void append(final LogEvent event) { total.incrementAndGet(); } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java index 20ef401..5e3ae5d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamManager.java @@ -143,7 +143,7 @@ public class OutputStreamManager extends AbstractManager implements ByteBufferDe * @param immediateFlush If true, flushes after writing. * @throws AppenderLoggingException if an error occurs. */ - protected void write(final byte[] bytes, boolean immediateFlush) { + protected void write(final byte[] bytes, final boolean immediateFlush) { write(bytes, 0, bytes.length, immediateFlush); } @@ -168,7 +168,7 @@ public class OutputStreamManager extends AbstractManager implements ByteBufferDe * @param immediateFlush flushes immediately after writing. * @throws AppenderLoggingException if an error occurs. */ - protected synchronized void write(final byte[] bytes, final int offset, final int length, boolean immediateFlush) { + protected synchronized void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) { if (immediateFlush && byteBuffer.position() == 0) { writeToDestination(bytes, offset, length); flushDestination(); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java index 1a542db..c49ed46 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SocketAppender.java @@ -199,7 +199,7 @@ public class SocketAppender extends AbstractOutputStreamAppender<AbstractSocketM final String name, final String immediateFlush, final String ignore, - Layout<? extends Serializable> layout, + final Layout<? extends Serializable> layout, final Filter filter, final String advertise, final Configuration config) { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java index b45d6e0..b4d1b1b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/CronTriggeringPolicy.java @@ -44,7 +44,7 @@ public final class CronTriggeringPolicy implements TriggeringPolicy { private final Configuration configuration; private final boolean checkOnStartup; - private CronTriggeringPolicy(CronExpression schedule, boolean checkOnStartup, Configuration configuration) { + private CronTriggeringPolicy(final CronExpression schedule, final boolean checkOnStartup, final Configuration configuration) { this.cronExpression = schedule; this.configuration = configuration; this.checkOnStartup = checkOnStartup; @@ -89,7 +89,7 @@ public final class CronTriggeringPolicy implements TriggeringPolicy { */ @PluginFactory public static CronTriggeringPolicy createPolicy( - @PluginConfiguration Configuration configuration, + @PluginConfiguration final Configuration configuration, @PluginAttribute("evaluateOnStartup") final String evaluateOnStartup, @PluginAttribute("schedule") final String schedule) { CronExpression cronExpression; @@ -107,7 +107,7 @@ public final class CronTriggeringPolicy implements TriggeringPolicy { return new CronTriggeringPolicy(cronExpression, checkOnStartup, configuration); } - private static CronExpression getSchedule(String expression) { + private static CronExpression getSchedule(final String expression) { try { return new CronExpression(expression); } catch (ParseException pe) { http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java index 547cc3e..6097586 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java @@ -131,7 +131,7 @@ public class DefaultRolloverStrategy implements RolloverStrategy { } }; - static FileExtensions lookup(String fileExtension) { + static FileExtensions lookup(final String fileExtension) { for (FileExtensions ext : values()) { if (ext.isExtensionFor(fileExtension)) { return ext; @@ -162,11 +162,11 @@ public class DefaultRolloverStrategy implements RolloverStrategy { return extension.length(); } - File source(String fileName) { + File source(final String fileName) { return new File(fileName); } - File target(String fileName) { + File target(final String fileName) { return new File(fileName); } }; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileSize.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileSize.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileSize.java index e134b1f..08e6b9e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileSize.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileSize.java @@ -1,90 +1,90 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - -package org.apache.logging.log4j.core.appender.rolling; - -import java.text.NumberFormat; -import java.text.ParseException; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.status.StatusLogger; - -/** - * FileSize utility class. - */ -public final class FileSize { - private static final Logger LOGGER = StatusLogger.getLogger(); - - private static final long KB = 1024; - private static final long MB = KB * KB; - private static final long GB = KB * MB; - - /** - * Pattern for string parsing. - */ - private static final Pattern VALUE_PATTERN = - Pattern.compile("([0-9]+([\\.,][0-9]+)?)\\s*(|K|M|G)B?", Pattern.CASE_INSENSITIVE); - - private FileSize() { - } - - /** - * Converts a string to a number of bytes. Strings consist of a floating point value followed by - * K, M, or G for kilobytes, megabytes, gigabytes, respectively. The - * abbreviations KB, MB, and GB are also accepted. Matching is case insensitive. - * - * @param string The string to convert - * @return The Bytes value for the string - */ - public static long parse(final String string, long defaultValue) { - final Matcher matcher = VALUE_PATTERN.matcher(string); - - // Valid input? - if (matcher.matches()) { - try { - // Get double precision value - final long value = NumberFormat.getNumberInstance(Locale.getDefault()).parse( - matcher.group(1)).longValue(); - - // Get units specified - final String units = matcher.group(3); - - if (units.isEmpty()) { - return value; - } else if (units.equalsIgnoreCase("K")) { - return value * KB; - } else if (units.equalsIgnoreCase("M")) { - return value * MB; - } else if (units.equalsIgnoreCase("G")) { - return value * GB; - } else { - LOGGER.error("FileSize units not recognized: " + string); - return defaultValue; - } - } catch (final ParseException e) { - LOGGER.error("FileSize unable to parse numeric part: " + string, e); - return defaultValue; - } - } - LOGGER.error("FileSize unable to parse bytes: " + string); - return defaultValue; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.core.appender.rolling; + +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.status.StatusLogger; + +/** + * FileSize utility class. + */ +public final class FileSize { + private static final Logger LOGGER = StatusLogger.getLogger(); + + private static final long KB = 1024; + private static final long MB = KB * KB; + private static final long GB = KB * MB; + + /** + * Pattern for string parsing. + */ + private static final Pattern VALUE_PATTERN = + Pattern.compile("([0-9]+([\\.,][0-9]+)?)\\s*(|K|M|G)B?", Pattern.CASE_INSENSITIVE); + + private FileSize() { + } + + /** + * Converts a string to a number of bytes. Strings consist of a floating point value followed by + * K, M, or G for kilobytes, megabytes, gigabytes, respectively. The + * abbreviations KB, MB, and GB are also accepted. Matching is case insensitive. + * + * @param string The string to convert + * @return The Bytes value for the string + */ + public static long parse(final String string, final long defaultValue) { + final Matcher matcher = VALUE_PATTERN.matcher(string); + + // Valid input? + if (matcher.matches()) { + try { + // Get double precision value + final long value = NumberFormat.getNumberInstance(Locale.getDefault()).parse( + matcher.group(1)).longValue(); + + // Get units specified + final String units = matcher.group(3); + + if (units.isEmpty()) { + return value; + } else if (units.equalsIgnoreCase("K")) { + return value * KB; + } else if (units.equalsIgnoreCase("M")) { + return value * MB; + } else if (units.equalsIgnoreCase("G")) { + return value * GB; + } else { + LOGGER.error("FileSize units not recognized: " + string); + return defaultValue; + } + } catch (final ParseException e) { + LOGGER.error("FileSize unable to parse numeric part: " + string, e); + return defaultValue; + } + } + LOGGER.error("FileSize unable to parse bytes: " + string); + return defaultValue; + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/Duration.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/Duration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/Duration.java index 8508f03..a5103f7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/Duration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/Duration.java @@ -1,257 +1,257 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - -package org.apache.logging.log4j.core.appender.rolling.action; - -import java.io.Serializable; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Simplified implementation of the <a href="https://en.wikipedia.org/wiki/ISO_8601#Durations">ISO-8601 Durations</a> - * standard. The supported format is {@code PnDTnHnMnS}, with 'P' and 'T' optional. Days are considered to be exactly 24 - * hours. - * <p> - * Similarly to the {@code java.time.Duration} class, this class does not support year or month sections in the format. - * This implementation does not support fractions or negative values. - * - * @see #parse(CharSequence) - */ -public class Duration implements Serializable, Comparable<Duration> { - private static final long serialVersionUID = -3756810052716342061L; - - /** - * Constant for a duration of zero. - */ - public static final Duration ZERO = new Duration(0); - - /** - * Hours per day. - */ - private static final int HOURS_PER_DAY = 24; - /** - * Minutes per hour. - */ - private static final int MINUTES_PER_HOUR = 60; - /** - * Seconds per minute. - */ - private static final int SECONDS_PER_MINUTE = 60; - /** - * Seconds per hour. - */ - private static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; - /** - * Seconds per day. - */ - private static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; - - /** - * The pattern for parsing. - */ - private static final Pattern PATTERN = Pattern.compile("P?(?:([0-9]+)D)?" - + "(T?(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)?S)?)?", Pattern.CASE_INSENSITIVE); - - /** - * The number of seconds in the duration. - */ - private final long seconds; - - /** - * Constructs an instance of {@code Duration} using seconds. - * - * @param seconds the length of the duration in seconds, positive or negative - */ - private Duration(long seconds) { - super(); - this.seconds = seconds; - } - - /** - * Obtains a {@code Duration} from a text string such as {@code PnDTnHnMnS}. - * <p> - * This will parse a textual representation of a duration, including the string produced by {@code toString()}. The - * formats accepted are based on the ISO-8601 duration format {@code PnDTnHnMnS} with days considered to be exactly - * 24 hours. - * <p> - * This implementation does not support negative numbers or fractions (so the smallest non-zero value a Duration can - * have is one second). - * <p> - * The string optionally starts with the ASCII letter "P" in upper or lower case. There are then four sections, each - * consisting of a number and a suffix. The sections have suffixes in ASCII of "D", "H", "M" and "S" for days, - * hours, minutes and seconds, accepted in upper or lower case. The suffixes must occur in order. The ASCII letter - * "T" may occur before the first occurrence, if any, of an hour, minute or second section. At least one of the four - * sections must be present, and if "T" is present there must be at least one section after the "T". The number part - * of each section must consist of one or more ASCII digits. The number may not be prefixed by the ASCII negative or - * positive symbol. The number of days, hours, minutes and seconds must parse to a {@code long}. - * <p> - * Examples: - * - * <pre> - * "PT20S" -- parses as "20 seconds" - * "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds) - * "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds) - * "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds) - * "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes" - * </pre> - * - * @param text the text to parse, not null - * @return the parsed duration, not null - * @throws IllegalArgumentException if the text cannot be parsed to a duration - */ - public static Duration parse(CharSequence text) { - Objects.requireNonNull(text, "text"); - Matcher matcher = PATTERN.matcher(text); - if (matcher.matches()) { - // check for letter T but no time sections - if ("T".equals(matcher.group(2)) == false) { - String dayMatch = matcher.group(1); - String hourMatch = matcher.group(3); - String minuteMatch = matcher.group(4); - String secondMatch = matcher.group(5); - if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) { - long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days"); - long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours"); - long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes"); - long seconds = parseNumber(text, secondMatch, 1, "seconds"); - try { - return create(daysAsSecs, hoursAsSecs, minsAsSecs, seconds); - } catch (ArithmeticException ex) { - throw new IllegalArgumentException("Text cannot be parsed to a Duration (overflow) " + text, ex); - } - } - } - } - throw new IllegalArgumentException("Text cannot be parsed to a Duration: " + text); - } - - private static long parseNumber(final CharSequence text, final String parsed, final int multiplier, - final String errorText) { - // regex limits to [0-9]+ - if (parsed == null) { - return 0; - } - try { - final long val = Long.parseLong(parsed); - return val * multiplier; - } catch (final Exception ex) { - throw new IllegalArgumentException("Text cannot be parsed to a Duration: " + errorText + " (in " + text - + ")", ex); - } - } - - private static Duration create(final long daysAsSecs, final long hoursAsSecs, final long minsAsSecs, final long secs) { - return create(daysAsSecs + hoursAsSecs + minsAsSecs + secs); - } - - /** - * Obtains an instance of {@code Duration} using seconds. - * - * @param seconds the length of the duration in seconds, positive only - */ - private static Duration create(final long seconds) { - if ((seconds) == 0) { - return ZERO; - } - return new Duration(seconds); - } - - /** - * Converts this duration to the total length in milliseconds. - * - * @return the total length of the duration in milliseconds - */ - public long toMillis() { - return seconds * 1000L; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Duration)) { - return false; - } - Duration other = (Duration) obj; - return other.seconds == this.seconds; - } - - @Override - public int hashCode() { - return (int) (seconds ^ (seconds >>> 32)); - } - - /** - * A string representation of this duration using ISO-8601 seconds based representation, such as {@code PT8H6M12S}. - * <p> - * The format of the returned string will be {@code PnDTnHnMnS}, where n is the relevant days, hours, minutes or - * seconds part of the duration. If a section has a zero value, it is omitted. The hours, minutes and seconds are - * all positive. - * <p> - * Examples: - * - * <pre> - * "20 seconds" -- "PT20S - * "15 minutes" (15 * 60 seconds) -- "PT15M" - * "10 hours" (10 * 3600 seconds) -- "PT10H" - * "2 days" (2 * 86400 seconds) -- "P2D" - * </pre> - * - * @return an ISO-8601 representation of this duration, not null - */ - @Override - public String toString() { - if (this == ZERO) { - return "PT0S"; - } - final long days = seconds / SECONDS_PER_DAY; - final long hours = (seconds % SECONDS_PER_DAY) / SECONDS_PER_HOUR; - final int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE); - final int secs = (int) (seconds % SECONDS_PER_MINUTE); - final StringBuilder buf = new StringBuilder(24); - buf.append("P"); - if (days != 0) { - buf.append(days).append('D'); - } - if ((hours | minutes | secs) != 0) { - buf.append('T'); - } - if (hours != 0) { - buf.append(hours).append('H'); - } - if (minutes != 0) { - buf.append(minutes).append('M'); - } - if (secs == 0 && buf.length() > 0) { - return buf.toString(); - } - buf.append(secs).append('S'); - return buf.toString(); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(Duration other) { - return Long.signum(toMillis() - other.toMillis()); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.core.appender.rolling.action; + +import java.io.Serializable; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Simplified implementation of the <a href="https://en.wikipedia.org/wiki/ISO_8601#Durations">ISO-8601 Durations</a> + * standard. The supported format is {@code PnDTnHnMnS}, with 'P' and 'T' optional. Days are considered to be exactly 24 + * hours. + * <p> + * Similarly to the {@code java.time.Duration} class, this class does not support year or month sections in the format. + * This implementation does not support fractions or negative values. + * + * @see #parse(CharSequence) + */ +public class Duration implements Serializable, Comparable<Duration> { + private static final long serialVersionUID = -3756810052716342061L; + + /** + * Constant for a duration of zero. + */ + public static final Duration ZERO = new Duration(0); + + /** + * Hours per day. + */ + private static final int HOURS_PER_DAY = 24; + /** + * Minutes per hour. + */ + private static final int MINUTES_PER_HOUR = 60; + /** + * Seconds per minute. + */ + private static final int SECONDS_PER_MINUTE = 60; + /** + * Seconds per hour. + */ + private static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; + /** + * Seconds per day. + */ + private static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; + + /** + * The pattern for parsing. + */ + private static final Pattern PATTERN = Pattern.compile("P?(?:([0-9]+)D)?" + + "(T?(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)?S)?)?", Pattern.CASE_INSENSITIVE); + + /** + * The number of seconds in the duration. + */ + private final long seconds; + + /** + * Constructs an instance of {@code Duration} using seconds. + * + * @param seconds the length of the duration in seconds, positive or negative + */ + private Duration(final long seconds) { + super(); + this.seconds = seconds; + } + + /** + * Obtains a {@code Duration} from a text string such as {@code PnDTnHnMnS}. + * <p> + * This will parse a textual representation of a duration, including the string produced by {@code toString()}. The + * formats accepted are based on the ISO-8601 duration format {@code PnDTnHnMnS} with days considered to be exactly + * 24 hours. + * <p> + * This implementation does not support negative numbers or fractions (so the smallest non-zero value a Duration can + * have is one second). + * <p> + * The string optionally starts with the ASCII letter "P" in upper or lower case. There are then four sections, each + * consisting of a number and a suffix. The sections have suffixes in ASCII of "D", "H", "M" and "S" for days, + * hours, minutes and seconds, accepted in upper or lower case. The suffixes must occur in order. The ASCII letter + * "T" may occur before the first occurrence, if any, of an hour, minute or second section. At least one of the four + * sections must be present, and if "T" is present there must be at least one section after the "T". The number part + * of each section must consist of one or more ASCII digits. The number may not be prefixed by the ASCII negative or + * positive symbol. The number of days, hours, minutes and seconds must parse to a {@code long}. + * <p> + * Examples: + * + * <pre> + * "PT20S" -- parses as "20 seconds" + * "PT15M" -- parses as "15 minutes" (where a minute is 60 seconds) + * "PT10H" -- parses as "10 hours" (where an hour is 3600 seconds) + * "P2D" -- parses as "2 days" (where a day is 24 hours or 86400 seconds) + * "P2DT3H4M" -- parses as "2 days, 3 hours and 4 minutes" + * </pre> + * + * @param text the text to parse, not null + * @return the parsed duration, not null + * @throws IllegalArgumentException if the text cannot be parsed to a duration + */ + public static Duration parse(final CharSequence text) { + Objects.requireNonNull(text, "text"); + Matcher matcher = PATTERN.matcher(text); + if (matcher.matches()) { + // check for letter T but no time sections + if ("T".equals(matcher.group(2)) == false) { + String dayMatch = matcher.group(1); + String hourMatch = matcher.group(3); + String minuteMatch = matcher.group(4); + String secondMatch = matcher.group(5); + if (dayMatch != null || hourMatch != null || minuteMatch != null || secondMatch != null) { + long daysAsSecs = parseNumber(text, dayMatch, SECONDS_PER_DAY, "days"); + long hoursAsSecs = parseNumber(text, hourMatch, SECONDS_PER_HOUR, "hours"); + long minsAsSecs = parseNumber(text, minuteMatch, SECONDS_PER_MINUTE, "minutes"); + long seconds = parseNumber(text, secondMatch, 1, "seconds"); + try { + return create(daysAsSecs, hoursAsSecs, minsAsSecs, seconds); + } catch (ArithmeticException ex) { + throw new IllegalArgumentException("Text cannot be parsed to a Duration (overflow) " + text, ex); + } + } + } + } + throw new IllegalArgumentException("Text cannot be parsed to a Duration: " + text); + } + + private static long parseNumber(final CharSequence text, final String parsed, final int multiplier, + final String errorText) { + // regex limits to [0-9]+ + if (parsed == null) { + return 0; + } + try { + final long val = Long.parseLong(parsed); + return val * multiplier; + } catch (final Exception ex) { + throw new IllegalArgumentException("Text cannot be parsed to a Duration: " + errorText + " (in " + text + + ")", ex); + } + } + + private static Duration create(final long daysAsSecs, final long hoursAsSecs, final long minsAsSecs, final long secs) { + return create(daysAsSecs + hoursAsSecs + minsAsSecs + secs); + } + + /** + * Obtains an instance of {@code Duration} using seconds. + * + * @param seconds the length of the duration in seconds, positive only + */ + private static Duration create(final long seconds) { + if ((seconds) == 0) { + return ZERO; + } + return new Duration(seconds); + } + + /** + * Converts this duration to the total length in milliseconds. + * + * @return the total length of the duration in milliseconds + */ + public long toMillis() { + return seconds * 1000L; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Duration)) { + return false; + } + Duration other = (Duration) obj; + return other.seconds == this.seconds; + } + + @Override + public int hashCode() { + return (int) (seconds ^ (seconds >>> 32)); + } + + /** + * A string representation of this duration using ISO-8601 seconds based representation, such as {@code PT8H6M12S}. + * <p> + * The format of the returned string will be {@code PnDTnHnMnS}, where n is the relevant days, hours, minutes or + * seconds part of the duration. If a section has a zero value, it is omitted. The hours, minutes and seconds are + * all positive. + * <p> + * Examples: + * + * <pre> + * "20 seconds" -- "PT20S + * "15 minutes" (15 * 60 seconds) -- "PT15M" + * "10 hours" (10 * 3600 seconds) -- "PT10H" + * "2 days" (2 * 86400 seconds) -- "P2D" + * </pre> + * + * @return an ISO-8601 representation of this duration, not null + */ + @Override + public String toString() { + if (this == ZERO) { + return "PT0S"; + } + final long days = seconds / SECONDS_PER_DAY; + final long hours = (seconds % SECONDS_PER_DAY) / SECONDS_PER_HOUR; + final int minutes = (int) ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE); + final int secs = (int) (seconds % SECONDS_PER_MINUTE); + final StringBuilder buf = new StringBuilder(24); + buf.append("P"); + if (days != 0) { + buf.append(days).append('D'); + } + if ((hours | minutes | secs) != 0) { + buf.append('T'); + } + if (hours != 0) { + buf.append(hours).append('H'); + } + if (minutes != 0) { + buf.append(minutes).append('M'); + } + if (secs == 0 && buf.length() > 0) { + return buf.toString(); + } + buf.append(secs).append('S'); + return buf.toString(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(final Duration other) { + return Long.signum(toMillis() - other.toMillis()); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java index 627a082..9591804 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/IfAll.java @@ -1,116 +1,116 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.core.appender.rolling.action; - -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; -import java.util.Objects; - -import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.config.plugins.PluginElement; -import org.apache.logging.log4j.core.config.plugins.PluginFactory; - -/** - * Composite {@code PathCondition} that only accepts objects that are accepted by <em>all</em> component conditions. - * Corresponds to logical "AND". - */ -@Plugin(name = "IfAll", category = "Core", printObject = true) -public final class IfAll implements PathCondition { - - private final PathCondition[] components; - - private IfAll(final PathCondition... filters) { - this.components = Objects.requireNonNull(filters, "filters"); - } - - public PathCondition[] getDeleteFilters() { - return components; - } - - /* - * (non-Javadoc) - * - * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path, - * java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) - */ - @Override - public boolean accept(final Path baseDir, final Path relativePath, final BasicFileAttributes attrs) { - if (components == null || components.length == 0) { - return false; // unconditional delete not supported - } - return accept(components, baseDir, relativePath, attrs); - } - - /** - * Returns {@code true} if all the specified conditions accept the specified path, {@code false} otherwise. - * - * @param list the array of conditions to evaluate - * @param baseDir the directory from where to start scanning for deletion candidate files - * @param relativePath the candidate for deletion. This path is relative to the baseDir. - * @param attrs attributes of the candidate path - * @return {@code true} if all the specified conditions accept the specified path, {@code false} otherwise - * @throws NullPointerException if any of the parameters is {@code null} - */ - public static boolean accept(final PathCondition[] list, final Path baseDir, final Path relativePath, - final BasicFileAttributes attrs) { - for (final PathCondition component : list) { - if (!component.accept(baseDir, relativePath, attrs)) { - return false; - } - } - return true; - } - - /* - * (non-Javadoc) - * - * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#beforeFileTreeWalk() - */ - @Override - public void beforeFileTreeWalk() { - beforeFileTreeWalk(components); - } - - /** - * Calls {@link #beforeFileTreeWalk()} on all of the specified nested conditions. - * - * @param nestedConditions the conditions to call {@link #beforeFileTreeWalk()} on - */ - public static void beforeFileTreeWalk(PathCondition[] nestedConditions) { - for (PathCondition condition : nestedConditions) { - condition.beforeFileTreeWalk(); - } - } - - /** - * Create a Composite PathCondition whose components all need to accept before this condition accepts. - * - * @param components The component filters. - * @return A Composite PathCondition. - */ - @PluginFactory - public static IfAll createAndCondition( // - @PluginElement("PathConditions") final PathCondition... components) { - return new IfAll(components); - } - - @Override - public String toString() { - return "IfAll" + Arrays.toString(components); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.appender.rolling.action; + +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; +import java.util.Objects; + +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * Composite {@code PathCondition} that only accepts objects that are accepted by <em>all</em> component conditions. + * Corresponds to logical "AND". + */ +@Plugin(name = "IfAll", category = "Core", printObject = true) +public final class IfAll implements PathCondition { + + private final PathCondition[] components; + + private IfAll(final PathCondition... filters) { + this.components = Objects.requireNonNull(filters, "filters"); + } + + public PathCondition[] getDeleteFilters() { + return components; + } + + /* + * (non-Javadoc) + * + * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#accept(java.nio.file.Path, + * java.nio.file.Path, java.nio.file.attribute.BasicFileAttributes) + */ + @Override + public boolean accept(final Path baseDir, final Path relativePath, final BasicFileAttributes attrs) { + if (components == null || components.length == 0) { + return false; // unconditional delete not supported + } + return accept(components, baseDir, relativePath, attrs); + } + + /** + * Returns {@code true} if all the specified conditions accept the specified path, {@code false} otherwise. + * + * @param list the array of conditions to evaluate + * @param baseDir the directory from where to start scanning for deletion candidate files + * @param relativePath the candidate for deletion. This path is relative to the baseDir. + * @param attrs attributes of the candidate path + * @return {@code true} if all the specified conditions accept the specified path, {@code false} otherwise + * @throws NullPointerException if any of the parameters is {@code null} + */ + public static boolean accept(final PathCondition[] list, final Path baseDir, final Path relativePath, + final BasicFileAttributes attrs) { + for (final PathCondition component : list) { + if (!component.accept(baseDir, relativePath, attrs)) { + return false; + } + } + return true; + } + + /* + * (non-Javadoc) + * + * @see org.apache.logging.log4j.core.appender.rolling.action.PathCondition#beforeFileTreeWalk() + */ + @Override + public void beforeFileTreeWalk() { + beforeFileTreeWalk(components); + } + + /** + * Calls {@link #beforeFileTreeWalk()} on all of the specified nested conditions. + * + * @param nestedConditions the conditions to call {@link #beforeFileTreeWalk()} on + */ + public static void beforeFileTreeWalk(final PathCondition[] nestedConditions) { + for (PathCondition condition : nestedConditions) { + condition.beforeFileTreeWalk(); + } + } + + /** + * Create a Composite PathCondition whose components all need to accept before this condition accepts. + * + * @param components The component filters. + * @return A Composite PathCondition. + */ + @PluginFactory + public static IfAll createAndCondition( // + @PluginElement("PathConditions") final PathCondition... components) { + return new IfAll(components); + } + + @Override + public String toString() { + return "IfAll" + Arrays.toString(components); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/IdlePurgePolicy.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/IdlePurgePolicy.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/IdlePurgePolicy.java index 953f1bc..e9e7469 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/IdlePurgePolicy.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/IdlePurgePolicy.java @@ -48,13 +48,13 @@ public class IdlePurgePolicy extends AbstractLifeCycle implements PurgePolicy, R private final ConfigurationScheduler scheduler; private volatile ScheduledFuture<?> future = null; - public IdlePurgePolicy(long timeToLive, ConfigurationScheduler scheduler) { + public IdlePurgePolicy(final long timeToLive, final ConfigurationScheduler scheduler) { this.timeToLive = timeToLive; this.scheduler = scheduler; } @Override - public void initialize(RoutingAppender routingAppender) { + public void initialize(final RoutingAppender routingAppender) { this.routingAppender = routingAppender; } @@ -80,7 +80,7 @@ public class IdlePurgePolicy extends AbstractLifeCycle implements PurgePolicy, R } @Override - public void update(String key, LogEvent event) { + public void update(final String key, final LogEvent event) { long now = System.currentTimeMillis(); appendersUsage.put(key, now); if (future == null) { @@ -123,7 +123,7 @@ public class IdlePurgePolicy extends AbstractLifeCycle implements PurgePolicy, R public static PurgePolicy createPurgePolicy( @PluginAttribute("timeToLive") final String timeToLive, @PluginAttribute("timeUnit") final String timeUnit, - @PluginConfiguration Configuration configuration) { + @PluginConfiguration final Configuration configuration) { if (timeToLive == null) { LOGGER.error("A timeToLive value is required"); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java index a6b5df0..02f2d33 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java @@ -55,7 +55,7 @@ public final class RoutingAppender extends AbstractAppender { private final PurgePolicy purgePolicy; private RoutingAppender(final String name, final Filter filter, final boolean ignoreExceptions, final Routes routes, - final RewritePolicy rewritePolicy, final Configuration config, PurgePolicy purgePolicy) { + final RewritePolicy rewritePolicy, final Configuration config, final PurgePolicy purgePolicy) { super(name, filter, null, ignoreExceptions); this.routes = routes; this.config = config; @@ -181,7 +181,7 @@ public final class RoutingAppender extends AbstractAppender { * * @param key The appender's key */ - public void deleteAppender(String key) { + public void deleteAppender(final String key) { LOGGER.debug("Stopping route with key" + key); AppenderControl control = appenders.remove(key); control.getAppender().stop(); http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java index 4c590c1..df7eb8a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java @@ -98,7 +98,7 @@ public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBuf * @see org.apache.logging.log4j.core.Logger#updateConfiguration(org.apache.logging.log4j.core.config.Configuration) */ @Override - protected void updateConfiguration(Configuration newConfig) { + protected void updateConfiguration(final Configuration newConfig) { nanoClock = newConfig.getNanoClock(); includeLocation = newConfig.getLoggerConfig(name).isIncludeLocation(); super.updateConfiguration(newConfig); @@ -215,7 +215,7 @@ public class AsyncLogger extends Logger implements EventTranslatorVararg<RingBuf * @param fqcn fully qualified caller name. * @return the caller location if requested, {@code null} otherwise. */ - private StackTraceElement calcLocationIfRequested(String fqcn) { + private StackTraceElement calcLocationIfRequested(final String fqcn) { // location: very expensive operation. LOG4J2-153: // Only include if "includeLocation=true" is specified, // exclude if not specified or if "false" was specified. http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java index 276d2ed..d0c9eea 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDisruptor.java @@ -66,7 +66,7 @@ public class AsyncLoggerConfigDisruptor implements AsyncLoggerConfigDelegate { public Log4jEventWrapper() { } - public Log4jEventWrapper(MutableLogEvent mutableLogEvent) { + public Log4jEventWrapper(final MutableLogEvent mutableLogEvent) { event = mutableLogEvent; } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java index a256238..a06893c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerContext.java @@ -81,12 +81,12 @@ public class AsyncLoggerContext extends LoggerContext { * @see org.apache.logging.log4j.core.LoggerContext#start(org.apache.logging.log4j.core.config.Configuration) */ @Override - public void start(Configuration config) { + public void start(final Configuration config) { maybeStartHelper(config); super.start(config); } - private void maybeStartHelper(Configuration config) { + private void maybeStartHelper(final Configuration config) { // If no log4j configuration was found, there are no loggers // and there is no point in starting the disruptor (which takes up // significant memory and starts a thread). http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7a5dcd43/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java index 628f4ff..a2dcb19 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerDisruptor.java @@ -1,236 +1,236 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - -package org.apache.logging.log4j.core.async; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.core.jmx.RingBufferAdmin; -import org.apache.logging.log4j.status.StatusLogger; - -import com.lmax.disruptor.ExceptionHandler; -import com.lmax.disruptor.RingBuffer; -import com.lmax.disruptor.WaitStrategy; -import com.lmax.disruptor.dsl.Disruptor; -import com.lmax.disruptor.dsl.ProducerType; - -/** - * Helper class for async loggers: AsyncLoggerDisruptor handles the mechanics of working with the LMAX Disruptor, and - * works with its associated AsyncLoggerContext to synchronize the life cycle of the Disruptor and its thread with the - * life cycle of the context. The AsyncLoggerDisruptor of the context is shared by all AsyncLogger objects created by - * that AsyncLoggerContext. - */ -class AsyncLoggerDisruptor { - private static final int SLEEP_MILLIS_BETWEEN_DRAIN_ATTEMPTS = 50; - private static final int MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN = 200; - private static final StatusLogger LOGGER = StatusLogger.getLogger(); - - private volatile Disruptor<RingBufferLogEvent> disruptor; - private ExecutorService executor; - private String contextName; - - private boolean useThreadLocalTranslator = true; - private long backgroundThreadId; - private AsyncQueueFullPolicy asyncQueueFullPolicy; - private int ringBufferSize; - - AsyncLoggerDisruptor(String contextName) { - this.contextName = contextName; - } - - public String getContextName() { - return contextName; - } - - public void setContextName(String name) { - contextName = name; - } - - Disruptor<RingBufferLogEvent> getDisruptor() { - return disruptor; - } - - /** - * Creates and starts a new Disruptor and associated thread if none currently exists. - * - * @see #stop() - */ - synchronized void start() { - if (disruptor != null) { - LOGGER.trace( - "[{}] AsyncLoggerDisruptor not starting new disruptor for this context, using existing object.", - contextName); - return; - } - LOGGER.trace("[{}] AsyncLoggerDisruptor creating new disruptor for this context.", contextName); - ringBufferSize = DisruptorUtil.calculateRingBufferSize("AsyncLogger.RingBufferSize"); - final WaitStrategy waitStrategy = DisruptorUtil.createWaitStrategy("AsyncLogger.WaitStrategy"); - executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("AsyncLogger[" + contextName + "]")); - backgroundThreadId = DisruptorUtil.getExecutorThreadId(executor); - asyncQueueFullPolicy = AsyncQueueFullPolicyFactory.create(); - - disruptor = new Disruptor<>(RingBufferLogEvent.FACTORY, ringBufferSize, executor, ProducerType.MULTI, - waitStrategy); - - final ExceptionHandler<RingBufferLogEvent> errorHandler = DisruptorUtil.getAsyncLoggerExceptionHandler(); - disruptor.handleExceptionsWith(errorHandler); - - final RingBufferLogEventHandler[] handlers = {new RingBufferLogEventHandler()}; - disruptor.handleEventsWith(handlers); - - LOGGER.debug("[{}] Starting AsyncLogger disruptor for this context with ringbufferSize={}, waitStrategy={}, " - + "exceptionHandler={}...", contextName, disruptor.getRingBuffer().getBufferSize(), waitStrategy - .getClass().getSimpleName(), errorHandler); - disruptor.start(); - - LOGGER.trace("[{}] AsyncLoggers use a {} translator", contextName, useThreadLocalTranslator ? "threadlocal" - : "vararg"); - } - - /** - * Decreases the reference count. If the reference count reached zero, the Disruptor and its associated thread are - * shut down and their references set to {@code null}. - */ - synchronized void stop() { - final Disruptor<RingBufferLogEvent> temp = getDisruptor(); - if (temp == null) { - LOGGER.trace("[{}] AsyncLoggerDisruptor: disruptor for this context already shut down.", contextName); - return; // disruptor was already shut down by another thread - } - LOGGER.debug("[{}] AsyncLoggerDisruptor: shutting down disruptor for this context.", contextName); - - // We must guarantee that publishing to the RingBuffer has stopped before we call disruptor.shutdown(). - disruptor = null; // client code fails with NPE if log after stop. This is by design. - - // Calling Disruptor.shutdown() will wait until all enqueued events are fully processed, - // but this waiting happens in a busy-spin. To avoid (postpone) wasting CPU, - // we sleep in short chunks, up to 10 seconds, waiting for the ringbuffer to drain. - for (int i = 0; hasBacklog(temp) && i < MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN; i++) { - try { - Thread.sleep(SLEEP_MILLIS_BETWEEN_DRAIN_ATTEMPTS); // give up the CPU for a while - } catch (final InterruptedException e) { // ignored - } - } - temp.shutdown(); // busy-spins until all events currently in the disruptor have been processed - - LOGGER.trace("[{}] AsyncLoggerDisruptor: shutting down disruptor executor.", contextName); - executor.shutdown(); // finally, kill the processor thread - executor = null; - - if (DiscardingAsyncQueueFullPolicy.getDiscardCount(asyncQueueFullPolicy) > 0) { - LOGGER.trace("AsyncLoggerDisruptor: {} discarded {} events.", asyncQueueFullPolicy, - DiscardingAsyncQueueFullPolicy.getDiscardCount(asyncQueueFullPolicy)); - } - } - - /** - * Returns {@code true} if the specified disruptor still has unprocessed events. - */ - private static boolean hasBacklog(final Disruptor<?> theDisruptor) { - final RingBuffer<?> ringBuffer = theDisruptor.getRingBuffer(); - return !ringBuffer.hasAvailableCapacity(ringBuffer.getBufferSize()); - } - - /** - * Creates and returns a new {@code RingBufferAdmin} that instruments the ringbuffer of the {@code AsyncLogger}. - * - * @param jmxContextName name of the {@code AsyncLoggerContext} - * @return a new {@code RingBufferAdmin} that instruments the ringbuffer - */ - public RingBufferAdmin createRingBufferAdmin(final String jmxContextName) { - final RingBuffer<RingBufferLogEvent> ring = disruptor == null ? null : disruptor.getRingBuffer(); - return RingBufferAdmin.forAsyncLogger(ring, jmxContextName); - } - - EventRoute getEventRoute(final Level logLevel) { - final int remainingCapacity = remainingDisruptorCapacity(); - if (remainingCapacity < 0) { - return EventRoute.DISCARD; - } - return asyncQueueFullPolicy.getRoute(backgroundThreadId, logLevel); - } - - private int remainingDisruptorCapacity() { - final Disruptor<RingBufferLogEvent> temp = disruptor; - if (hasLog4jBeenShutDown(temp)) { - return -1; - } - return (int) temp.getRingBuffer().remainingCapacity(); - } - /** - * Returns {@code true} if the specified disruptor is null. - */ - private boolean hasLog4jBeenShutDown(final Disruptor<RingBufferLogEvent> aDisruptor) { - if (aDisruptor == null) { // LOG4J2-639 - LOGGER.warn("Ignoring log event after log4j was shut down"); - return true; - } - return false; - } - - public boolean tryPublish(final RingBufferLogEventTranslator translator) { - // LOG4J2-639: catch NPE if disruptor field was set to null in stop() - try { - return disruptor.getRingBuffer().tryPublishEvent(translator); - } catch (final NullPointerException npe) { - LOGGER.warn("[{}] Ignoring log event after log4j was shut down.", contextName); - return false; - } - } - - void enqueueLogMessageInfo(final RingBufferLogEventTranslator translator) { - // LOG4J2-639: catch NPE if disruptor field was set to null in stop() - try { - // Note: we deliberately access the volatile disruptor field afresh here. - // Avoiding this and using an older reference could result in adding a log event to the disruptor after it - // was shut down, which could cause the publishEvent method to hang and never return. - disruptor.publishEvent(translator); - } catch (final NullPointerException npe) { - LOGGER.warn("[{}] Ignoring log event after log4j was shut down.", contextName); - } - } - - /** - * Returns whether it is allowed to store non-JDK classes in ThreadLocal objects for efficiency. - * - * @return whether AsyncLoggers are allowed to use ThreadLocal objects - * @since 2.5 - * @see <a href="https://issues.apache.org/jira/browse/LOG4J2-1172">LOG4J2-1172</a> - */ - public boolean isUseThreadLocals() { - return useThreadLocalTranslator; - } - - /** - * Signals this AsyncLoggerDisruptor whether it is allowed to store non-JDK classes in ThreadLocal objects for - * efficiency. - * <p> - * This property may be modified after the {@link #start()} method has been called. - * </p> - * - * @param allow whether AsyncLoggers are allowed to use ThreadLocal objects - * @since 2.5 - * @see <a href="https://issues.apache.org/jira/browse/LOG4J2-1172">LOG4J2-1172</a> - */ - public void setUseThreadLocals(final boolean allow) { - useThreadLocalTranslator = allow; - LOGGER.trace("[{}] AsyncLoggers have been modified to use a {} translator", contextName, - useThreadLocalTranslator ? "threadlocal" : "vararg"); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.core.async; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.jmx.RingBufferAdmin; +import org.apache.logging.log4j.status.StatusLogger; + +import com.lmax.disruptor.ExceptionHandler; +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.WaitStrategy; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; + +/** + * Helper class for async loggers: AsyncLoggerDisruptor handles the mechanics of working with the LMAX Disruptor, and + * works with its associated AsyncLoggerContext to synchronize the life cycle of the Disruptor and its thread with the + * life cycle of the context. The AsyncLoggerDisruptor of the context is shared by all AsyncLogger objects created by + * that AsyncLoggerContext. + */ +class AsyncLoggerDisruptor { + private static final int SLEEP_MILLIS_BETWEEN_DRAIN_ATTEMPTS = 50; + private static final int MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN = 200; + private static final StatusLogger LOGGER = StatusLogger.getLogger(); + + private volatile Disruptor<RingBufferLogEvent> disruptor; + private ExecutorService executor; + private String contextName; + + private boolean useThreadLocalTranslator = true; + private long backgroundThreadId; + private AsyncQueueFullPolicy asyncQueueFullPolicy; + private int ringBufferSize; + + AsyncLoggerDisruptor(final String contextName) { + this.contextName = contextName; + } + + public String getContextName() { + return contextName; + } + + public void setContextName(final String name) { + contextName = name; + } + + Disruptor<RingBufferLogEvent> getDisruptor() { + return disruptor; + } + + /** + * Creates and starts a new Disruptor and associated thread if none currently exists. + * + * @see #stop() + */ + synchronized void start() { + if (disruptor != null) { + LOGGER.trace( + "[{}] AsyncLoggerDisruptor not starting new disruptor for this context, using existing object.", + contextName); + return; + } + LOGGER.trace("[{}] AsyncLoggerDisruptor creating new disruptor for this context.", contextName); + ringBufferSize = DisruptorUtil.calculateRingBufferSize("AsyncLogger.RingBufferSize"); + final WaitStrategy waitStrategy = DisruptorUtil.createWaitStrategy("AsyncLogger.WaitStrategy"); + executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("AsyncLogger[" + contextName + "]")); + backgroundThreadId = DisruptorUtil.getExecutorThreadId(executor); + asyncQueueFullPolicy = AsyncQueueFullPolicyFactory.create(); + + disruptor = new Disruptor<>(RingBufferLogEvent.FACTORY, ringBufferSize, executor, ProducerType.MULTI, + waitStrategy); + + final ExceptionHandler<RingBufferLogEvent> errorHandler = DisruptorUtil.getAsyncLoggerExceptionHandler(); + disruptor.handleExceptionsWith(errorHandler); + + final RingBufferLogEventHandler[] handlers = {new RingBufferLogEventHandler()}; + disruptor.handleEventsWith(handlers); + + LOGGER.debug("[{}] Starting AsyncLogger disruptor for this context with ringbufferSize={}, waitStrategy={}, " + + "exceptionHandler={}...", contextName, disruptor.getRingBuffer().getBufferSize(), waitStrategy + .getClass().getSimpleName(), errorHandler); + disruptor.start(); + + LOGGER.trace("[{}] AsyncLoggers use a {} translator", contextName, useThreadLocalTranslator ? "threadlocal" + : "vararg"); + } + + /** + * Decreases the reference count. If the reference count reached zero, the Disruptor and its associated thread are + * shut down and their references set to {@code null}. + */ + synchronized void stop() { + final Disruptor<RingBufferLogEvent> temp = getDisruptor(); + if (temp == null) { + LOGGER.trace("[{}] AsyncLoggerDisruptor: disruptor for this context already shut down.", contextName); + return; // disruptor was already shut down by another thread + } + LOGGER.debug("[{}] AsyncLoggerDisruptor: shutting down disruptor for this context.", contextName); + + // We must guarantee that publishing to the RingBuffer has stopped before we call disruptor.shutdown(). + disruptor = null; // client code fails with NPE if log after stop. This is by design. + + // Calling Disruptor.shutdown() will wait until all enqueued events are fully processed, + // but this waiting happens in a busy-spin. To avoid (postpone) wasting CPU, + // we sleep in short chunks, up to 10 seconds, waiting for the ringbuffer to drain. + for (int i = 0; hasBacklog(temp) && i < MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN; i++) { + try { + Thread.sleep(SLEEP_MILLIS_BETWEEN_DRAIN_ATTEMPTS); // give up the CPU for a while + } catch (final InterruptedException e) { // ignored + } + } + temp.shutdown(); // busy-spins until all events currently in the disruptor have been processed + + LOGGER.trace("[{}] AsyncLoggerDisruptor: shutting down disruptor executor.", contextName); + executor.shutdown(); // finally, kill the processor thread + executor = null; + + if (DiscardingAsyncQueueFullPolicy.getDiscardCount(asyncQueueFullPolicy) > 0) { + LOGGER.trace("AsyncLoggerDisruptor: {} discarded {} events.", asyncQueueFullPolicy, + DiscardingAsyncQueueFullPolicy.getDiscardCount(asyncQueueFullPolicy)); + } + } + + /** + * Returns {@code true} if the specified disruptor still has unprocessed events. + */ + private static boolean hasBacklog(final Disruptor<?> theDisruptor) { + final RingBuffer<?> ringBuffer = theDisruptor.getRingBuffer(); + return !ringBuffer.hasAvailableCapacity(ringBuffer.getBufferSize()); + } + + /** + * Creates and returns a new {@code RingBufferAdmin} that instruments the ringbuffer of the {@code AsyncLogger}. + * + * @param jmxContextName name of the {@code AsyncLoggerContext} + * @return a new {@code RingBufferAdmin} that instruments the ringbuffer + */ + public RingBufferAdmin createRingBufferAdmin(final String jmxContextName) { + final RingBuffer<RingBufferLogEvent> ring = disruptor == null ? null : disruptor.getRingBuffer(); + return RingBufferAdmin.forAsyncLogger(ring, jmxContextName); + } + + EventRoute getEventRoute(final Level logLevel) { + final int remainingCapacity = remainingDisruptorCapacity(); + if (remainingCapacity < 0) { + return EventRoute.DISCARD; + } + return asyncQueueFullPolicy.getRoute(backgroundThreadId, logLevel); + } + + private int remainingDisruptorCapacity() { + final Disruptor<RingBufferLogEvent> temp = disruptor; + if (hasLog4jBeenShutDown(temp)) { + return -1; + } + return (int) temp.getRingBuffer().remainingCapacity(); + } + /** + * Returns {@code true} if the specified disruptor is null. + */ + private boolean hasLog4jBeenShutDown(final Disruptor<RingBufferLogEvent> aDisruptor) { + if (aDisruptor == null) { // LOG4J2-639 + LOGGER.warn("Ignoring log event after log4j was shut down"); + return true; + } + return false; + } + + public boolean tryPublish(final RingBufferLogEventTranslator translator) { + // LOG4J2-639: catch NPE if disruptor field was set to null in stop() + try { + return disruptor.getRingBuffer().tryPublishEvent(translator); + } catch (final NullPointerException npe) { + LOGGER.warn("[{}] Ignoring log event after log4j was shut down.", contextName); + return false; + } + } + + void enqueueLogMessageInfo(final RingBufferLogEventTranslator translator) { + // LOG4J2-639: catch NPE if disruptor field was set to null in stop() + try { + // Note: we deliberately access the volatile disruptor field afresh here. + // Avoiding this and using an older reference could result in adding a log event to the disruptor after it + // was shut down, which could cause the publishEvent method to hang and never return. + disruptor.publishEvent(translator); + } catch (final NullPointerException npe) { + LOGGER.warn("[{}] Ignoring log event after log4j was shut down.", contextName); + } + } + + /** + * Returns whether it is allowed to store non-JDK classes in ThreadLocal objects for efficiency. + * + * @return whether AsyncLoggers are allowed to use ThreadLocal objects + * @since 2.5 + * @see <a href="https://issues.apache.org/jira/browse/LOG4J2-1172">LOG4J2-1172</a> + */ + public boolean isUseThreadLocals() { + return useThreadLocalTranslator; + } + + /** + * Signals this AsyncLoggerDisruptor whether it is allowed to store non-JDK classes in ThreadLocal objects for + * efficiency. + * <p> + * This property may be modified after the {@link #start()} method has been called. + * </p> + * + * @param allow whether AsyncLoggers are allowed to use ThreadLocal objects + * @since 2.5 + * @see <a href="https://issues.apache.org/jira/browse/LOG4J2-1172">LOG4J2-1172</a> + */ + public void setUseThreadLocals(final boolean allow) { + useThreadLocalTranslator = allow; + LOGGER.trace("[{}] AsyncLoggers have been modified to use a {} translator", contextName, + useThreadLocalTranslator ? "threadlocal" : "vararg"); + } +}
