I sadly am able to reproduce the issue Andrew: public final class TimeDriftDemo {
public static void main(String[] args) throws InterruptedException { initLog4j(); Logger logger = LogManager.getLogger(); for (int i = 0; i < 5; i++) { logger.info("{}", i); Thread.sleep(100); } } private static LoggerContext initLog4j() { // Create the configuration builder. ConfigurationBuilder<BuiltConfiguration> configBuilder = ConfigurationBuilderFactory .newConfigurationBuilder() .setStatusLevel(Level.ERROR) .setConfigurationName(TimeDriftDemo.class.getSimpleName()); // Create the configuration. String appenderName = "Console"; String eventTemplate = "" + "[{\"$resolver\":\"timestamp\",\"epoch\":{\"unit\":\"nanos\"}}" + ",{\"$resolver\":\"timestamp\",\"pattern\":{\"format\":\"ss.SSS'Z'\",\"timeZone\":\"UTC\",\"locale\":\"en_US\"}}" + "]"; Configuration config = configBuilder .add(configBuilder .newAppender(appenderName, "Console") .add(configBuilder .newLayout("JsonTemplateLayout") .addAttribute( "eventTemplate", eventTemplate))) .add(configBuilder.newLogger("com.vlkan", Level.TRACE)) .add(configBuilder .newRootLogger(Level.ERROR) .add(configBuilder.newAppenderRef(appenderName))) .build(false); // Initialize the configuration. return Configurator.initialize(config); } } I will create a ticket and act on it. Thanks so much for the report! On Thu, Oct 28, 2021 at 3:19 AM Andrew Harris (andharr2) < andrew.har...@appdynamics.com> wrote: > Volkan, > > I was able to get the test case you supplied running in > JsonTemplateLayoutTest. I confirm that it passes on my system as well. In > fact, all the tests in `log4j-layout-template-json` at `rel/2.14.1` pass > (except JsonTemplateLayoutGcFreeTest, which I'm willing to believe is a > quirk of that test vis-a-vis my setup). That tells me that log rendering in > unit-test isolation operates as expected. > > I extended the test case to use SystemClock to generate two Instants with > a short pause between generation, like: > ``` > final Clock systemClock = new SystemClock(); > final MutableInstant instant3 = new MutableInstant(); > instant3.initFrom(systemClock); > try { Thread.sleep(100); } catch (InterruptedException e) {}; > final MutableInstant instant4 = new MutableInstant(); > instant4.initFrom(systemClock); > > final LogEvent logEvent3 = > Log4jLogEvent.newBuilder().setInstant(instant3).build(); > final LogEvent logEvent4 = > Log4jLogEvent.newBuilder().setInstant(instant4).build(); > > usingSerializedLogEventAccessor(layout, logEvent3, accessor3 -> { > usingSerializedLogEventAccessor(layout, logEvent4, accessor4 > -> { > Long epoch3 = accessor3.getObject("timestamp.epoch", > Long.class); > String iso3 = accessor3.getString("timestamp.iso8601"); > > Long epoch4 = accessor4.getObject("timestamp.epoch", > Long.class); > String iso4 = accessor4.getString("timestamp.iso8601"); > > assertThat(epoch4 - epoch3).isGreaterThanOrEqualTo(100L); > assertThat(iso3).isNotEqualTo(iso4); > }); > }); > ``` > > This test portion also passed. This tells me that the act of deriving the > time from a provided clock operates as expected. > > Back in my example application, I did some more experimentation and found > the following: > > - Removing the timestamp pattern resolver from the JSON template does not > change behavior. Epoch timestamps continue to be repeated. > - Changing the timestamp epoch resolver to use millis or seconds does not > change behavior. Epoch timestamps are correctly rounded and rendered, they > just don't appear to advance. > > Seeking to rule out whether this was due to behavior in the logger, I > switched to an async logger using Disruptor 3.4.4: > - Passing > '-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector' > does not appear to change behavior. > - Using an AsyncLogger does not appear to change behavior. > - Changing `log4j2.clock` to use CachedClock (vs SystemClock) does not > change behavior. Some precision in the nano timestamp is lost, as I believe > is expected. > > I sought to rule out JDK differences, so I ran my bench application > against Azul Zulu 8, 11, and 16, as well as AdoptOpenJDK 11 on J9. No > change in behavior between these, the epoch was still repeated between runs. > > Using a PatternLayout allows both Unix epoch and pattern-derived > timestamps to be rendered as expected: > ``` > <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} - %logger{36} - %msg > timestamp=%d{UNIX_MILLIS} %n"/> > ``` > > """ > 2021-10-27 18:07:28.111 - example.Log4jApp - Starting count > timestamp=1635383248111 > 2021-10-27 18:07:28.118 - example.Log4jApp - Event 0 > timestamp=1635383248118 > 2021-10-27 18:07:29.123 - example.Log4jApp - Event 1 > timestamp=1635383249123 > """ > > I confirmed the behavior of the PatternLayout with an AsyncLogger as well, > and it too worked as expected. > > Please let me know what might be worth trying next here, and thank you for > your ideas up to now. I'm quite confused at this point. :) > > Andrew >