Uploaded image for project: 'Log4j 2'
  1. Log4j 2
  2. LOG4J2-3152

JSONLayout - Add mechanism for registering additional modules (i.e. JodaModule)

    XMLWordPrintableJSON

Details

    • Improvement
    • Status: Closed
    • Minor
    • Resolution: Won't Fix
    • 2.14.0
    • None
    • Layouts
    • None

    Description

      When I attempt to use a JsonLayout with the following configuration:

      <JsonLayout compact="true" eventEol="true" properties="true" objectMessageAsJsonObject="true" />

       

      and log an object with a Joda LocalDateTime property, I get the following exception:

       

      2021-08-26 15:48:30,437 Log4j2-TF-67-AsyncLoggerConfig-6 ERROR com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Joda date/time type `org.joda.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-joda" to enable handling (through reference chain: org.apache.logging.log4j.core.layout.AbstractJacksonLayout$LogEventWithAdditionalFields["logEvent"]>org.apache.logging.log4j.core.impl.Log4jLogEvent["message"]>com.mycompany.StartEvent["timeStamp"])2021-08-26 15:48:30,437 Log4j2-TF-67-AsyncLoggerConfig-6 ERROR com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Joda date/time type `org.joda.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-joda" to enable handling (through reference chain: org.apache.logging.log4j.core.layout.AbstractJacksonLayout$LogEventWithAdditionalFields["logEvent"]>org.apache.logging.log4j.core.impl.Log4jLogEvent["message"]>com.mycompany.StartEvent["timeStamp"])
      {{     at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)}}
      {{     at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276) }}
      {{     at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35) }}
      {{     at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)}}
      {{     at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) }}
      {{     at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) }}
      {{     at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) }}
      {{     at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) }}
      {{     at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) }}
      {{     at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) }}
      {{     at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:3126) }}
      {{     at com.fasterxml.jackson.core.base.GeneratorBase.writeObject(GeneratorBase.java:388) }}
      {{     at org.apache.logging.log4j.core.jackson.ObjectMessageSerializer.serialize(ObjectMessageSerializer.java:44) }}
      {{     at org.apache.logging.log4j.core.jackson.ObjectMessageSerializer.serialize(ObjectMessageSerializer.java:33) }}
      {{     at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) }}
      {{     at com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter.serializeAsField(SimpleBeanPropertyFilter.java:208) }}
      {{     at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFieldsFiltered(BeanSerializerBase.java:822) }}
      {{     at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer.serialize(UnwrappingBeanSerializer.java:136) }}
      {{     at com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter.serializeAsField(UnwrappingBeanPropertyWriter.java:127) }}
      {{     at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) }}
      {{     at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) }}
      {{     at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) }}
      {{     at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) }}
      {{     at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1514) }}
      {{     at com.fasterxml.jackson.databind.ObjectWriter._writeValueAndClose(ObjectWriter.java:1215) }}
      {{     at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1059) }}
      {{     at org.apache.logging.log4j.core.layout.AbstractJacksonLayout.toSerializable(AbstractJacksonLayout.java:344) }}
      {{     at org.apache.logging.log4j.core.layout.JsonLayout.toSerializable(JsonLayout.java:291) }}
      {{     at org.apache.logging.log4j.core.layout.AbstractJacksonLayout.toSerializable(AbstractJacksonLayout.java:292) }}
      {{     at org.apache.logging.log4j.core.layout.JsonLayout.toSerializable(JsonLayout.java:68) }}
      {{     at org.apache.logging.log4j.core.layout.AbstractJacksonLayout.toSerializable(AbstractJacksonLayout.java:52) }}
      {{     at org.apache.logging.log4j.core.layout.AbstractStringLayout.toByteArray(AbstractStringLayout.java:308) }}
      {{     at org.apache.logging.log4j.core.layout.AbstractLayout.encode(AbstractLayout.java:210) }}
      {{     at org.apache.logging.log4j.core.layout.AbstractLayout.encode(AbstractLayout.java:37) }}
      {{     at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.directEncodeEvent(AbstractOutputStreamAppender.java:197) }}
      {{     at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.tryAppend(AbstractOutputStreamAppender.java:190) }}
      {{     at org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.append(AbstractOutputStreamAppender.java:181) }}
      {{     at org.apache.logging.log4j.core.appender.RollingFileAppender.append(RollingFileAppender.java:312) }}
      {{     at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156) }}
      {{     at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129) }}
      {{     at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120) }}
      {{     at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84) }}
      {{     at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:543) }}
      {{     at org.apache.logging.log4j.core.async.AsyncLoggerConfig.callAppenders(AsyncLoggerConfig.java:127) }}
      {{     at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:502) }}
      {{     at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:485) }}
      {{     at org.apache.logging.log4j.core.async.AsyncLoggerConfig.log(AsyncLoggerConfig.java:121) }}
      {{     at org.apache.logging.log4j.core.async.AsyncLoggerConfig.logToAsyncLoggerConfigsOnCurrentThread(AsyncLoggerConfig.java:169) }}
      {{     at org.apache.logging.log4j.core.async.AsyncLoggerConfigDisruptor$Log4jEventWrapperHandler.onEvent(AsyncLoggerConfigDisruptor.java:112) }}
      {{     at org.apache.logging.log4j.core.async.AsyncLoggerConfigDisruptor$Log4jEventWrapperHandler.onEvent(AsyncLoggerConfigDisruptor.java:98) }}
      {{     at com.lmax.disruptor.BatchEventProcessor.processEvents(BatchEventProcessor.java:169) }}
      {{     at com.lmax.disruptor.BatchEventProcessor.run(BatchEventProcessor.java:126) }}
      {{     at java.lang.Thread.run(Thread.java:748)}}
      {{  }}

      Looking through both the log4j2 documentation and code, there is no mechanism for being able to add further modules to the Jackson ObjectMapper object that the JsonAppender uses, i.e:

      public Log4jJsonObjectMapper(final boolean encodeThreadContextAsList, final boolean includeStacktrace, final boolean stacktraceAsString, final boolean objectMessageAsJsonObject) {

      {{    this.registerModule(new Log4jJsonModule(encodeThreadContextAsList,}}{{    includeStacktrace, stacktraceAsString, objectMessageAsJsonObject));}}

          {{this.registerModule(new JodaModule());}}

      {{    this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);}}
      }

       

      Perhaps the config could look something like:

       

      <JsonLayout compact="true" eventEol="true" properties="true" objectMessageAsJsonObject="true">

        <module class="com.fasterxml.jackson.datatype.joda.JodaModule">

      </JsonLayout>

       

      I've been able to work around this by creating my own versions of the following classes:

       - JSONLayout,

       - JacksonFactory,

       - Log4jJsonObjectMapper

      but this is not ideal as it's brittle to internal log4j changes.

       

      Attachments

        Activity

          People

            Unassigned Unassigned
            alehane Andy Lehane
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: