Skip to content

Commit 02f7bee

Browse files
committed
Document potential optimization directions
1 parent 523251d commit 02f7bee

File tree

2 files changed

+67
-24
lines changed

2 files changed

+67
-24
lines changed

log4j-core-test/src/test/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatterTest.java

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import static org.apache.logging.log4j.core.util.internal.instant.InstantPatternDynamicFormatter.sequencePattern;
2222
import static org.assertj.core.api.Assertions.assertThat;
2323

24-
import java.time.Instant;
2524
import java.time.format.DateTimeFormatter;
2625
import java.time.temporal.ChronoUnit;
2726
import java.util.ArrayList;
@@ -38,7 +37,6 @@
3837
import org.apache.logging.log4j.core.util.internal.instant.InstantPatternDynamicFormatter.PatternSequence;
3938
import org.apache.logging.log4j.core.util.internal.instant.InstantPatternDynamicFormatter.StaticPatternSequence;
4039
import org.apache.logging.log4j.util.Constants;
41-
import org.junit.jupiter.api.Test;
4240
import org.junit.jupiter.params.ParameterizedTest;
4341
import org.junit.jupiter.params.provider.Arguments;
4442
import org.junit.jupiter.params.provider.MethodSource;
@@ -379,27 +377,48 @@ private static String formatInstant(
379377
return buffer.toString();
380378
}
381379

382-
@Test
383-
void f() {
384-
final String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS";
385-
final Locale LOCALE = Locale.US;
386-
final TimeZone TIME_ZONE = TimeZone.getTimeZone("UTC");
387-
final InstantPatternFormatter formatter = InstantPatternFormatter.newBuilder()
388-
.setPattern(pattern)
389-
.setLocale(LOCALE)
390-
.setTimeZone(TIME_ZONE)
391-
.setCachingEnabled(false)
392-
.build();
393-
final StringBuilder buffer = new StringBuilder();
394-
final MutableInstant mutableInstant = new MutableInstant();
395-
396-
final Instant instant1 = Instant.now();
397-
mutableInstant.initFromEpochSecond(instant1.getEpochSecond(), instant1.getNano());
398-
formatter.formatTo(buffer, mutableInstant);
399-
400-
buffer.setLength(0);
401-
final Instant instant2 = instant1.plusMillis(1);
402-
mutableInstant.initFromEpochSecond(instant2.getEpochSecond(), instant2.getNano());
403-
formatter.formatTo(buffer, mutableInstant);
380+
@ParameterizedTest
381+
@MethodSource("formatterInputs")
382+
void verify_manually_computed_sub_minute_precision_values(
383+
final String ignoredPattern,
384+
final Locale ignoredLocale,
385+
final TimeZone timeZone,
386+
final MutableInstant instant) {
387+
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
388+
"HH:mm:ss.S-SS-SSS-SSSS-SSSSS-SSSSSS-SSSSSSS-SSSSSSSS-SSSSSSSSS|n")
389+
.withZone(timeZone.toZoneId());
390+
final String formatterOutput = formatter.format(instant);
391+
final int offsetMillis = timeZone.getOffset(instant.getEpochMillisecond());
392+
final long adjustedEpochSeconds = (instant.getEpochMillisecond() + offsetMillis) / 1000;
393+
// 86400 seconds per day, 3600 seconds per hour
394+
final int local_H = (int) ((adjustedEpochSeconds % 86400L) / 3600L);
395+
final int local_m = (int) ((adjustedEpochSeconds / 60) % 60);
396+
final int local_s = (int) (adjustedEpochSeconds % 60);
397+
final int local_S = instant.getNanoOfSecond() / 100000000;
398+
final int local_SS = instant.getNanoOfSecond() / 10000000;
399+
final int local_SSS = instant.getNanoOfSecond() / 1000000;
400+
final int local_SSSS = instant.getNanoOfSecond() / 100000;
401+
final int local_SSSSS = instant.getNanoOfSecond() / 10000;
402+
final int local_SSSSSS = instant.getNanoOfSecond() / 1000;
403+
final int local_SSSSSSS = instant.getNanoOfSecond() / 100;
404+
final int local_SSSSSSSS = instant.getNanoOfSecond() / 10;
405+
final int local_SSSSSSSSS = instant.getNanoOfSecond();
406+
final int local_n = instant.getNanoOfSecond();
407+
final String output = String.format(
408+
"%02d:%02d:%02d.%d-%d-%d-%d-%d-%d-%d-%d-%d|%d",
409+
local_H,
410+
local_m,
411+
local_s,
412+
local_S,
413+
local_SS,
414+
local_SSS,
415+
local_SSSS,
416+
local_SSSSS,
417+
local_SSSSSS,
418+
local_SSSSSSS,
419+
local_SSSSSSSS,
420+
local_SSSSSSSSS,
421+
local_n);
422+
assertThat(output).isEqualTo(formatterOutput);
404423
}
405424
}

log4j-core/src/main/java/org/apache/logging/log4j/core/util/internal/instant/InstantPatternDynamicFormatter.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,30 @@
4747
* <li>Precompute and cache the output for parts that are of precision lower than or equal to {@value InstantPatternDynamicFormatter#PRECISION_THRESHOLD} (i.e., {@code yyyy-MM-dd'T'HH:mm:} and {@code X}) and cache it</li>
4848
* <li>Upon a formatting request, combine the cached outputs with the dynamic parts (i.e., {@code ss.SSS})</li>
4949
* </ol>
50+
* <h2>Implementation note</h2>
51+
* <p>
52+
* Formatting can actually even be made faster and garbage-free by manually formatting sub-minute precision directives as follows:
53+
* </p>
54+
* <pre>{@code
55+
* int offsetMillis = timeZone.getOffset(mutableInstant.getEpochMillisecond());
56+
* long adjustedEpochSeconds = (instant.getEpochMillisecond() + offsetMillis) / 1000;
57+
* int local_s = (int) (adjustedEpochSeconds % 60);
58+
* int local_S = instant.getNanoOfSecond() / 100000000;
59+
* int local_SS = instant.getNanoOfSecond() / 10000000;
60+
* int local_SSS = instant.getNanoOfSecond() / 1000000;
61+
* int local_SSSS = instant.getNanoOfSecond() / 100000;
62+
* int local_SSSSS = instant.getNanoOfSecond() / 10000;
63+
* int local_SSSSSS = instant.getNanoOfSecond() / 1000;
64+
* int local_SSSSSSS = instant.getNanoOfSecond() / 100;
65+
* int local_SSSSSSSS = instant.getNanoOfSecond() / 10;
66+
* int local_SSSSSSSSS = instant.getNanoOfSecond();
67+
* int local_n = instant.getNanoOfSecond();
68+
* }</pre>
69+
* <p>
70+
* Though this will require more hardcoded formatting and a change in the sequence merging strategies.
71+
* Hence, this optimization is intentionally shelved off due to involved complexity.
72+
* See {@code verify_manually_computed_sub_minute_precision_values()} in {@code InstantPatternDynamicFormatterTest} for a demonstration of this optimization.
73+
* </p>
5074
*
5175
* @since 2.25.0
5276
*/

0 commit comments

Comments
 (0)