diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java index 961a30f61a4..43f5c9afc9e 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/DatePatternConverterTest.java @@ -21,6 +21,7 @@ import java.util.Calendar; import java.util.Date; +import java.util.stream.Stream; import org.apache.logging.log4j.core.AbstractLogEvent; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.time.Instant; @@ -161,4 +162,27 @@ public void testGetPatternReturnsNullForUnixMillis() { public void testNewInstanceAllowsNullParameter() { DatePatternConverter.newInstance(null); // no errors } + + private static final String[] PATTERN_NAMES = + Stream.of(NamedInstantPattern.values()).map(Enum::name).toArray(String[]::new); + + @Test + public void testPredefinedFormatWithoutTimezone() { + for (final String patternName : PATTERN_NAMES) { + assertEquals( + DatePatternConverter.decodeNamedPattern(patternName), + DatePatternConverter.newInstance(new String[] {patternName}).getPattern()); + } + } + + @Test + public void testPredefinedFormatWithTimezone() { + for (final String patternName : PATTERN_NAMES) { + // Pacific Standard Time = UTC-08:00 + assertEquals( + DatePatternConverter.decodeNamedPattern(patternName), + DatePatternConverter.newInstance(new String[] {patternName, "PST"}) + .getPattern()); + } + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java index 75e4150236a..70a4d45b482 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/DefaultConfiguration.java @@ -36,7 +36,7 @@ public class DefaultConfiguration extends AbstractConfiguration { /** * The default Pattern used for the default Layout. */ - public static final String DEFAULT_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"; + public static final String DEFAULT_PATTERN = "%d{ABSOLUTE_PERIOD} [%thread] %-5level %logger{36} - %msg%n"; /** * Only for tests. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java index d4a0bda04bb..051e9a02093 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/DatePatternConverter.java @@ -47,8 +47,6 @@ public final class DatePatternConverter extends LogEventPatternConverter impleme private static final String CLASS_NAME = DatePatternConverter.class.getSimpleName(); - private static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; - private final InstantFormatter formatter; private DatePatternConverter(@Nullable final String[] options) { @@ -62,7 +60,7 @@ private static InstantFormatter createFormatter(@Nullable final String[] options } catch (final Exception error) { logOptionReadFailure(options, error, "failed for options: {}, falling back to the default instance"); } - return InstantPatternFormatter.newBuilder().setPattern(DEFAULT_PATTERN).build(); + return InstantPatternFormatter.newBuilder().setPattern(NamedInstantPattern.DEFAULT.getPattern()).build(); } private static InstantFormatter createFormatterUnsafely(@Nullable final String[] options) { @@ -90,7 +88,17 @@ private static InstantFormatter createFormatterUnsafely(@Nullable final String[] } private static String readPattern(@Nullable final String[] options) { - return options != null && options.length > 0 && options[0] != null ? options[0] : DEFAULT_PATTERN; + return options != null && options.length > 0 && options[0] != null + ? decodeNamedPattern(options[0]) + : NamedInstantPattern.DEFAULT.getPattern(); + } + + static String decodeNamedPattern(final String pattern) { + try { + return NamedInstantPattern.valueOf(pattern).getPattern(); + } catch (final IllegalArgumentException ignored) { + return pattern; + } } private static TimeZone readTimeZone(@Nullable final String[] options) { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NamedInstantPattern.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NamedInstantPattern.java new file mode 100644 index 00000000000..37855acd19f --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/NamedInstantPattern.java @@ -0,0 +1,77 @@ +/* + * 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.pattern; + +import org.jspecify.annotations.NullMarked; + +/** + * Named date-time patterns supported by {@link DatePatternConverter}. + */ +@NullMarked +public enum NamedInstantPattern { + ABSOLUTE("HH:mm:ss,SSS"), + + ABSOLUTE_MICROS("HH:mm:ss,SSSSSS"), + + ABSOLUTE_NANOS("HH:mm:ss,SSSSSSSSS"), + + ABSOLUTE_PERIOD("HH:mm:ss.SSS"), + + COMPACT("yyyyMMddHHmmssSSS"), + + DATE("dd MMM yyyy HH:mm:ss,SSS"), + + DATE_PERIOD("dd MMM yyyy HH:mm:ss.SSS"), + + DEFAULT("yyyy-MM-dd HH:mm:ss,SSS"), + + DEFAULT_MICROS("yyyy-MM-dd HH:mm:ss,SSSSSS"), + + DEFAULT_NANOS("yyyy-MM-dd HH:mm:ss,SSSSSSSSS"), + + DEFAULT_PERIOD("yyyy-MM-dd HH:mm:ss.SSS"), + + ISO8601_BASIC("yyyyMMdd'T'HHmmss,SSS"), + + ISO8601_BASIC_PERIOD("yyyyMMdd'T'HHmmss.SSS"), + + ISO8601("yyyy-MM-dd'T'HH:mm:ss,SSS"), + + ISO8601_OFFSET_DATE_TIME_HH("yyyy-MM-dd'T'HH:mm:ss,SSSx"), + + ISO8601_OFFSET_DATE_TIME_HHMM("yyyy-MM-dd'T'HH:mm:ss,SSSxx"), + + ISO8601_OFFSET_DATE_TIME_HHCMM("yyyy-MM-dd'T'HH:mm:ss,SSSxxx"), + + ISO8601_PERIOD("yyyy-MM-dd'T'HH:mm:ss.SSS"), + + ISO8601_PERIOD_MICROS("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"), + + US_MONTH_DAY_YEAR2_TIME("dd/MM/yy HH:mm:ss.SSS"), + + US_MONTH_DAY_YEAR4_TIME("dd/MM/yyyy HH:mm:ss.SSS"); + + private final String pattern; + + NamedInstantPattern(final String pattern) { + this.pattern = pattern; + } + + public String getPattern() { + return pattern; + } +} diff --git a/src/changelog/.3.x.x/4128_restore_named_instant_patterns.xml b/src/changelog/.3.x.x/4128_restore_named_instant_patterns.xml new file mode 100644 index 00000000000..cf54064dbd8 --- /dev/null +++ b/src/changelog/.3.x.x/4128_restore_named_instant_patterns.xml @@ -0,0 +1,8 @@ + + + + Restore named date & time patterns in Pattern Layout and export `org.apache.logging.log4j.core.pattern.NamedInstantPattern` for programmatic access +