Skip to content

Commit 5fe60bd

Browse files
LOG4J2-3672 Avoid invoking DateFormatSymbols.getZoneStrings() in FastDateParser
1 parent 1187b78 commit 5fe60bd

File tree

2 files changed

+39
-38
lines changed

2 files changed

+39
-38
lines changed

log4j-core/src/main/java/org/apache/logging/log4j/core/util/datetime/FastDateParser.java

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.IOException;
2020
import java.io.ObjectInputStream;
2121
import java.io.Serializable;
22-
import java.text.DateFormatSymbols;
2322
import java.text.ParseException;
2423
import java.text.ParsePosition;
2524
import java.util.ArrayList;
@@ -40,6 +39,7 @@
4039
import java.util.regex.Pattern;
4140

4241
import org.apache.logging.log4j.core.util.Integers;
42+
import org.apache.logging.log4j.util.PropertiesUtil;
4343

4444
import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
4545

@@ -79,6 +79,11 @@
7979
* @see FastDatePrinter
8080
*/
8181
public class FastDateParser implements DateParser, Serializable {
82+
/**
83+
* Enables the parsing of "friendly" timezone names. Since building a list of all available names incurs an additional heap cost (~3MB on x86_64),
84+
* in internal JDK structures which cannot be garbage collected, it is disabled by default.
85+
*/
86+
public static final String LOG4J2_ENABLE_FRIENDLY_TIMEZONE_NAMES = "log4j2.enableFriendlyTimezoneNames";
8287

8388
/**
8489
* Required for serialization support.
@@ -102,7 +107,7 @@ public class FastDateParser implements DateParser, Serializable {
102107
// comparator used to sort regex alternatives
103108
// alternatives should be ordered longer first, and shorter last. ('february' before 'feb')
104109
// all entries must be lowercase by locale.
105-
private static final Comparator<String> LONGER_FIRST_LOWERCASE = (left, right) -> right.compareTo(left);
110+
private static final Comparator<String> LONGER_FIRST_LOWERCASE = Comparator.reverseOrder();
106111

107112
/**
108113
* <p>Constructs a new FastDateParser.</p>
@@ -551,7 +556,7 @@ boolean parse(final FastDateParser parser, final Calendar calendar, final String
551556

552557
/**
553558
* Obtain a Strategy given a field from a SimpleDateFormat pattern
554-
* @param formatField A sub-sequence of the SimpleDateFormat pattern
559+
* @param f A sub-sequence of the SimpleDateFormat pattern
555560
* @param definingCalendar The calendar to obtain the short and long values
556561
* @return The Strategy that will handle parsing for the field
557562
*/
@@ -719,7 +724,7 @@ private static class CaseInsensitiveTextStrategy extends PatternStrategy {
719724
@Override
720725
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
721726
final Integer iVal = lKeyValues.get(value.toLowerCase(locale));
722-
cal.set(field, iVal.intValue());
727+
cal.set(field, iVal);
723728
}
724729
}
725730

@@ -843,44 +848,31 @@ private static class TzInfo {
843848
final StringBuilder sb = new StringBuilder();
844849
sb.append("((?iu)" + RFC_822_TIME_ZONE + "|" + GMT_OPTION );
845850

846-
final Set<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
851+
if (PropertiesUtil.getProperties().getBooleanProperty(LOG4J2_ENABLE_FRIENDLY_TIMEZONE_NAMES)) {
852+
final Set<String> sorted = new TreeSet<>(LONGER_FIRST_LOWERCASE);
847853

848-
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
849-
for (final String[] zoneNames : zones) {
850-
// offset 0 is the time zone ID and is not localized
851-
final String tzId = zoneNames[ID];
852-
if (tzId.equalsIgnoreCase("GMT")) {
853-
continue;
854-
}
855-
final TimeZone tz = TimeZone.getTimeZone(tzId);
856-
// offset 1 is long standard name
857-
// offset 2 is short standard name
858-
final TzInfo standard = new TzInfo(tz, false);
859-
TzInfo tzInfo = standard;
860-
for (int i = 1; i < zoneNames.length; ++i) {
861-
switch (i) {
862-
case 3: // offset 3 is long daylight savings (or summertime) name
863-
// offset 4 is the short summertime name
864-
tzInfo = new TzInfo(tz, true);
865-
break;
866-
case 5: // offset 5 starts additional names, probably standard time
867-
tzInfo = standard;
868-
break;
854+
for (final String tzId : TimeZone.getAvailableIDs()) {
855+
if (tzId.equalsIgnoreCase("GMT")) {
856+
continue;
869857
}
870-
if (zoneNames[i] != null) {
871-
final String key = zoneNames[i].toLowerCase(locale);
872-
// ignore the data associated with duplicates supplied in
873-
// the additional names
874-
if (sorted.add(key)) {
875-
tzNames.put(key, tzInfo);
858+
final TimeZone tz = TimeZone.getTimeZone(tzId);
859+
final TzInfo standard = new TzInfo(tz, false);
860+
final TzInfo dst = new TzInfo(tz, true);
861+
862+
for(boolean daylight : new Boolean[] {false, true}) {
863+
for(int style : new Integer[]{TimeZone.SHORT, TimeZone.LONG}) {
864+
String name = tz.getDisplayName(daylight, style, locale).toLowerCase(locale);
865+
if (sorted.add(name)) {
866+
tzNames.put(name, daylight ? dst : standard);
867+
}
876868
}
877869
}
878870
}
879-
}
880-
// order the regex alternatives with longer strings first, greedy
881-
// match will ensure longest string will be consumed
882-
for (final String zoneName : sorted) {
883-
simpleQuote(sb.append('|'), zoneName);
871+
// order the regex alternatives with longer strings first, greedy
872+
// match will ensure longest string will be consumed
873+
for (final String zoneName : sorted) {
874+
simpleQuote(sb.append('|'), zoneName);
875+
}
884876
}
885877
sb.append(")");
886878
createPattern(sb);

src/site/xdoc/manual/configuration.xml.vm

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2978,12 +2978,21 @@ public class AwesomeTest {
29782978
that advertises the same language(s) in order for scripting to be enabled. If no languages are specified, which is
29792979
the default, the ScriptManager will not be installed.</td>
29802980
</tr>
2981-
<tr>
2981+
<tr>
29822982
<td><a name="log4j2.disableCloudConfigLoggingSystem"/>log4j2.disableCloudConfigLoggingSystem</td>
29832983
<td></td>
29842984
<td></td>
29852985
<td>Disables the usage of the Spring Boot <tt>Log4j2CloudConfigLoggingSystem</tt>. Defaults to <tt>false</tt>.</td>
29862986
</tr>
2987+
<tr>
2988+
<td><a name="log4j2.enableFriendlyTimezoneNames"/>log4j2.enableFriendlyTimezoneNames</td>
2989+
<td></td>
2990+
<td></td>
2991+
<td>Enables the parsing of "friendly" timezone names in the <a href="layouts.html#PatternDate">Pattern Layout
2992+
date</a>. Since building a list of all available names incurs an additional heap cost (~3MB on x86_64) in
2993+
internal JDK structures which cannot be garbage collected, it is disabled by default.
2994+
Defaults to <tt>false</tt>.</td>
2995+
</tr>
29872996
</table>
29882997

29892998
</subsection>

0 commit comments

Comments
 (0)