Andrew, I have pushed a fix to the `release-2.x` branch with commit
`2dd7063fb6c6fe73070aded6607a2e2d53613b46`.
Mind giving it a try, please?

For the records, created LOG4J2-3183
<https://issues.apache.org/jira/browse/LOG4J2-3183>.

On Thu, Oct 28, 2021 at 11:02 AM Volkan Yazıcı <vol...@yazi.ci> wrote:

> 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
>>
>

Reply via email to