This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/karaf-decanter.git


The following commit(s) were added to refs/heads/main by this push:
     new 2a0a2c62 Add timestamp format and timezone support in the JSON 
marshaller. (#449)
2a0a2c62 is described below

commit 2a0a2c62f950fd09126a1a65449c11a4ed2e03e3
Author: JB Onofré <[email protected]>
AuthorDate: Sun Mar 16 11:18:17 2025 +0100

    Add timestamp format and timezone support in the JSON marshaller. (#449)
---
 appender/jdbc/pom.xml                              |  5 ++
 .../decanter/appender/jdbc/TestJdbcAppender.java   |  4 +-
 appender/mqtt/pom.xml                              |  5 ++
 .../decanter/appender/mqtt/TestMqttAppender.java   |  2 +-
 .../org.apache.karaf.decanter.marshaller.json.cfg  | 13 ++++-
 .../decanter/marshaller/json/JsonMarshaller.java   | 66 +++++++++++++++++----
 .../marshaller/json/TestJsonMarshaller.java        | 68 +++++++++++++++++++++-
 7 files changed, 145 insertions(+), 18 deletions(-)

diff --git a/appender/jdbc/pom.xml b/appender/jdbc/pom.xml
index aba709e3..71403eae 100644
--- a/appender/jdbc/pom.xml
+++ b/appender/jdbc/pom.xml
@@ -82,6 +82,11 @@
             <artifactId>johnzon-core</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/appender/jdbc/src/test/java/org/apache/karaf/decanter/appender/jdbc/TestJdbcAppender.java
 
b/appender/jdbc/src/test/java/org/apache/karaf/decanter/appender/jdbc/TestJdbcAppender.java
index b61a4369..c7c2ebb6 100644
--- 
a/appender/jdbc/src/test/java/org/apache/karaf/decanter/appender/jdbc/TestJdbcAppender.java
+++ 
b/appender/jdbc/src/test/java/org/apache/karaf/decanter/appender/jdbc/TestJdbcAppender.java
@@ -75,7 +75,7 @@ public class TestJdbcAppender {
             JsonReader reader = Json.createReader(new StringReader(json));
             JsonObject jsonO = reader.readObject();
             Assert.assertEquals("Timestamp db", TIMESTAMP, dbTimeStamp);
-            Assert.assertEquals("Timestamp string", 
"2016-02-02T15:59:40,634Z",jsonO.getString("@timestamp"));
+            Assert.assertEquals("Timestamp string", 
"2016-02-02T15:59:40.634",jsonO.getString("@timestamp"));
             Assert.assertEquals("timestamp long", TIMESTAMP, 
jsonO.getJsonNumber(EventConstants.TIMESTAMP).longValue());
             Assert.assertEquals("Topic", TOPIC, 
jsonO.getString(EventConstants.EVENT_TOPIC.replace('.','_')));
             Assert.assertFalse(res.next());
@@ -126,7 +126,7 @@ public class TestJdbcAppender {
             JsonReader reader = Json.createReader(new StringReader(json));
             JsonObject jsonO = reader.readObject();
             Assert.assertEquals("Timestamp db", TIMESTAMP, dbTimeStamp);
-            Assert.assertEquals("Timestamp string", 
"2016-02-02T15:59:40,634Z",jsonO.getString("@timestamp"));
+            Assert.assertEquals("Timestamp string", 
"2016-02-02T15:59:40.634",jsonO.getString("@timestamp"));
             Assert.assertEquals("timestamp long", TIMESTAMP, 
jsonO.getJsonNumber(EventConstants.TIMESTAMP).longValue());
             Assert.assertEquals("Topic", TOPIC, 
jsonO.getString(EventConstants.EVENT_TOPIC.replace('.','_')));
             Assert.assertFalse(res.next());
diff --git a/appender/mqtt/pom.xml b/appender/mqtt/pom.xml
index f9600cd6..1f9fbbfe 100644
--- a/appender/mqtt/pom.xml
+++ b/appender/mqtt/pom.xml
@@ -92,6 +92,11 @@
             <artifactId>activemq-kahadb-store</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     
     <build>
diff --git 
a/appender/mqtt/src/test/java/org/apache/karaf/decanter/appender/mqtt/TestMqttAppender.java
 
b/appender/mqtt/src/test/java/org/apache/karaf/decanter/appender/mqtt/TestMqttAppender.java
index f74420d5..01402b29 100644
--- 
a/appender/mqtt/src/test/java/org/apache/karaf/decanter/appender/mqtt/TestMqttAppender.java
+++ 
b/appender/mqtt/src/test/java/org/apache/karaf/decanter/appender/mqtt/TestMqttAppender.java
@@ -93,7 +93,7 @@ public class TestMqttAppender  {
         String jsonSt = received.iterator().next().toString();
         JsonReader reader = Json.createReader(new StringReader(jsonSt));
         JsonObject jsonO = reader.readObject();
-        Assert.assertEquals("2016-02-02T15:59:40,634Z", 
jsonO.getString("@timestamp"));
+        Assert.assertEquals("2016-02-02T15:59:40.634", 
jsonO.getString("@timestamp"));
         Assert.assertEquals(TIMESTAMP, 
jsonO.getJsonNumber(EventConstants.TIMESTAMP).longValue());
         Assert.assertEquals("decanter", 
jsonO.getString(EventConstants.EVENT_TOPIC.replace('.', '_')));
         
diff --git 
a/marshaller/json/src/main/cfg/org.apache.karaf.decanter.marshaller.json.cfg 
b/marshaller/json/src/main/cfg/org.apache.karaf.decanter.marshaller.json.cfg
index b2690dd3..69a56cb7 100644
--- a/marshaller/json/src/main/cfg/org.apache.karaf.decanter.marshaller.json.cfg
+++ b/marshaller/json/src/main/cfg/org.apache.karaf.decanter.marshaller.json.cfg
@@ -21,4 +21,15 @@
 # Decanter JSON marshaller configuration
 #
 
-# replaceDotsByUnderscores=true
+# date format configuration, e.g. how the timestamp is formatted during 
marshalling.
+# timestamp.format accepts ISO_DATE_TIME, BASIC_ISO_DATE, ISO_LOCAL_DATE, 
ISO_OFFSET_DATE, ISO_DATE, ISO_LOCAL_TIME, ISO_OFFSET_TIME, ISO_TIME, 
ISO_LOCAL_DATE_TIME, ISO_OFFSET_DATE_TIME, ISO_ZONED_DATE_TIME, 
ISO_ORDINAL_DATE, ISO_WEEK_DATE, ISO_INSTANT, RFC_1123_DATE_TIME, or a custom 
pattern.
+# See 
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
 for more detail.
+# By default, the ISO_DATE_TIME format is used.
+#timestamp.format=ISO_DATE_TIME
+# timestamp.zone define the time ZoneId.
+# You can use UTC, GMT, or any value defined in 
https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html#SHORT_IDS
+# By default the UTC zone is used.
+#timestamp.zone=UTC
+
+# Replace the dots by underscores during marshalling
+#replaceDotsByUnderscores=true
diff --git 
a/marshaller/json/src/main/java/org/apache/karaf/decanter/marshaller/json/JsonMarshaller.java
 
b/marshaller/json/src/main/java/org/apache/karaf/decanter/marshaller/json/JsonMarshaller.java
index b006c51b..7e082f11 100644
--- 
a/marshaller/json/src/main/java/org/apache/karaf/decanter/marshaller/json/JsonMarshaller.java
+++ 
b/marshaller/json/src/main/java/org/apache/karaf/decanter/marshaller/json/JsonMarshaller.java
@@ -19,13 +19,15 @@ package org.apache.karaf.decanter.marshaller.json;
 import java.io.OutputStream;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.TimeZone;
 
 import javax.json.Json;
 import javax.json.JsonArray;
@@ -48,14 +50,13 @@ import org.osgi.service.event.EventConstants;
 )
 public class JsonMarshaller implements Marshaller {
 
-    private SimpleDateFormat tsFormat;
+    private final static String TIMESTAMP_FORMAT_PROPERTY = "timestamp.format";
+    private final static String TIMESTAMP_ZONE_PROPERTY = "timestamp.zone";
     
     boolean replaceDotsByUnderscores = true;
-    
-    public JsonMarshaller() {
-        tsFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SSSX");
-        tsFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
-    }
+
+    private DateTimeFormatter timestampFormatter = 
DateTimeFormatter.ISO_DATE_TIME;
+    private ZoneId timestampZoneId = ZoneId.of("UTC");
 
     @Activate
     public void activate(ComponentContext componentContext) {
@@ -63,6 +64,47 @@ public class JsonMarshaller implements Marshaller {
     }
 
     public void activate(Dictionary<String, Object> config) {
+        if (config.get(TIMESTAMP_FORMAT_PROPERTY) != null) {
+            String timestampFormatterProp = (String) 
config.get(TIMESTAMP_FORMAT_PROPERTY);
+            if (timestampFormatterProp.equalsIgnoreCase("ISO_DATE_TIME")) {
+                timestampFormatter = DateTimeFormatter.ISO_DATE_TIME;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("BASIC_ISO_DATE")) {
+                timestampFormatter = DateTimeFormatter.BASIC_ISO_DATE;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_LOCAL_DATE")) {
+                timestampFormatter = DateTimeFormatter.ISO_LOCAL_DATE;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_OFFSET_DATE")) {
+                timestampFormatter = DateTimeFormatter.ISO_OFFSET_DATE;
+            } else if (timestampFormatterProp.equalsIgnoreCase("ISO_DATE")) {
+                timestampFormatter = DateTimeFormatter.ISO_DATE;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_LOCAL_TIME")) {
+                timestampFormatter = DateTimeFormatter.ISO_LOCAL_TIME;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_OFFSET_TIME")) {
+                timestampFormatter = DateTimeFormatter.ISO_OFFSET_TIME;
+            } else if (timestampFormatterProp.equalsIgnoreCase("ISO_TIME")) {
+                timestampFormatter = DateTimeFormatter.ISO_TIME;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_LOCAL_DATE_TIME")) {
+                timestampFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_OFFSET_DATE_TIME")) {
+                timestampFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_ZONED_DATE_TIME")) {
+                timestampFormatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_ORDINAL_DATE")) {
+                timestampFormatter = DateTimeFormatter.ISO_ORDINAL_DATE;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("ISO_WEEK_DATE")) {
+                timestampFormatter = DateTimeFormatter.ISO_WEEK_DATE;
+            } else if (timestampFormatterProp.equalsIgnoreCase("ISO_INSTANT")) 
{
+                timestampFormatter = DateTimeFormatter.ISO_INSTANT;
+            } else if 
(timestampFormatterProp.equalsIgnoreCase("RFC_1123_DATE_TIME")) {
+                timestampFormatter = DateTimeFormatter.RFC_1123_DATE_TIME;
+            } else {
+                timestampFormatter = 
DateTimeFormatter.ofPattern(timestampFormatterProp);
+            }
+        }
+
+        if (config.get(TIMESTAMP_ZONE_PROPERTY) != null) {
+            timestampZoneId = ZoneId.of((String) 
config.get(TIMESTAMP_ZONE_PROPERTY), ZoneId.SHORT_IDS);
+        }
+
         replaceDotsByUnderscores = (config.get("replaceDotsByUnderscores") != 
null) ? 
             Boolean.valueOf((String) config.get("replaceDotsByUnderscores")) : 
true;
     }
@@ -92,9 +134,9 @@ public class JsonMarshaller implements Marshaller {
     }
 
     private void addTimestamp(Event event, JsonObjectBuilder json) {
-        Long ts = (Long)event.getProperty(EventConstants.TIMESTAMP);
-        Date date = ts != null ? new Date(ts) : new Date();
-        json.add("@timestamp", tsFormat.format(date));
+        Long timestamp = (Long)event.getProperty(EventConstants.TIMESTAMP);
+        LocalDateTime date = (timestamp != null) ? 
LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), timestampZoneId) : 
LocalDateTime.now();
+        json.add("@timestamp", date.format(timestampFormatter));
     }
 
     @SuppressWarnings("unchecked")
diff --git 
a/marshaller/json/src/test/java/org/apache/karaf/decanter/marshaller/json/TestJsonMarshaller.java
 
b/marshaller/json/src/test/java/org/apache/karaf/decanter/marshaller/json/TestJsonMarshaller.java
index b5b8e716..100e30b7 100644
--- 
a/marshaller/json/src/test/java/org/apache/karaf/decanter/marshaller/json/TestJsonMarshaller.java
+++ 
b/marshaller/json/src/test/java/org/apache/karaf/decanter/marshaller/json/TestJsonMarshaller.java
@@ -50,9 +50,73 @@ public class TestJsonMarshaller {
        map.put("c", "d");
        String jsonSt = marshaller.marshal(new Event(EXPECTED_TOPIC, map));
        System.out.println(jsonSt);
+
+       JsonReader reader = Json.createReader(new StringReader(jsonSt));
+       JsonObject jsonO = reader.readObject();
+       Assert.assertEquals("Timestamp string", "2016-02-02T15:59:40.634", 
jsonO.getString("@timestamp"));
+       long ts = jsonO.getJsonNumber(EventConstants.TIMESTAMP).longValue();
+       Assert.assertEquals("timestamp long", EXPECTED_TIMESTAMP, ts);
+       Assert.assertEquals("Topic", EXPECTED_TOPIC, 
jsonO.getString(EventConstants.EVENT_TOPIC.replace('.', '_')));
+   }
+
+   @Test
+   public void testCustomTimestampFormat() throws Exception {
+       JsonMarshaller marshaller = new JsonMarshaller();
+
+       Dictionary<String, Object> config = new Hashtable<>();
+       config.put("timestamp.format", "BASIC_ISO_DATE");
+       marshaller.activate(config);
+
+       Map<String, Object> map = new HashMap<>();
+       map.put(EventConstants.TIMESTAMP, EXPECTED_TIMESTAMP);
+       map.put("c", "d");
+       String jsonSt = marshaller.marshal(new Event(EXPECTED_TOPIC, map));
+
+       JsonReader reader = Json.createReader(new StringReader(jsonSt));
+       JsonObject jsonO = reader.readObject();
+       Assert.assertEquals("Timestamp string", "20160202", 
jsonO.getString("@timestamp"));
+       long ts = jsonO.getJsonNumber(EventConstants.TIMESTAMP).longValue();
+       Assert.assertEquals("timestamp long", EXPECTED_TIMESTAMP, ts);
+       Assert.assertEquals("Topic", EXPECTED_TOPIC, 
jsonO.getString(EventConstants.EVENT_TOPIC.replace('.', '_')));
+   }
+
+   @Test
+   public void testCustomTimestampFormatPattern() throws Exception {
+       JsonMarshaller marshaller = new JsonMarshaller();
+
+       Dictionary<String, Object> config = new Hashtable<>();
+       config.put("timestamp.format", "yyyy MM dd");
+       marshaller.activate(config);
+
+       Map<String, Object> map = new HashMap<>();
+       map.put(EventConstants.TIMESTAMP, EXPECTED_TIMESTAMP);
+       map.put("c", "d");
+       String jsonSt = marshaller.marshal(new Event(EXPECTED_TOPIC, map));
+
+       JsonReader reader = Json.createReader(new StringReader(jsonSt));
+       JsonObject jsonO = reader.readObject();
+       Assert.assertEquals("Timestamp string", "2016 02 02", 
jsonO.getString("@timestamp"));
+       long ts = jsonO.getJsonNumber(EventConstants.TIMESTAMP).longValue();
+       Assert.assertEquals("timestamp long", EXPECTED_TIMESTAMP, ts);
+       Assert.assertEquals("Topic", EXPECTED_TOPIC, 
jsonO.getString(EventConstants.EVENT_TOPIC.replace('.', '_')));
+   }
+
+   @Test
+   public void testCustomTimestampZone() throws Exception {
+       JsonMarshaller marshaller = new JsonMarshaller();
+
+       Dictionary<String, Object> config = new Hashtable<>();
+       config.put("timestamp.zone", "PST");
+       marshaller.activate(config);
+
+       Map<String, Object> map = new HashMap<>();
+       map.put(EventConstants.TIMESTAMP, EXPECTED_TIMESTAMP);
+       map.put("c", "d");
+       String jsonSt = marshaller.marshal(new Event(EXPECTED_TOPIC, map));
+
        JsonReader reader = Json.createReader(new StringReader(jsonSt));
        JsonObject jsonO = reader.readObject();
-       Assert.assertEquals("Timestamp string", 
"2016-02-02T15:59:40,634Z",jsonO.getString("@timestamp"));
+       Assert.assertEquals("Timestamp string", "2016-02-02T07:59:40.634", 
jsonO.getString("@timestamp"));
        long ts = jsonO.getJsonNumber(EventConstants.TIMESTAMP).longValue();
        Assert.assertEquals("timestamp long", EXPECTED_TIMESTAMP, ts);
        Assert.assertEquals("Topic", EXPECTED_TOPIC, 
jsonO.getString(EventConstants.EVENT_TOPIC.replace('.', '_')));
@@ -122,7 +186,7 @@ public class TestJsonMarshaller {
 
        JsonReader reader = Json.createReader(new StringReader(jsonString));
        JsonObject jsonObject = reader.readObject();
-       Assert.assertEquals("Timestamp string", "2016-02-02T15:59:40,634Z", 
jsonObject.getString("@timestamp"));
+       Assert.assertEquals("Timestamp string", "2016-02-02T15:59:40.634", 
jsonObject.getString("@timestamp"));
        long ts = 
jsonObject.getJsonNumber(EventConstants.TIMESTAMP).longValue();
        Assert.assertEquals("timestamp long", EXPECTED_TIMESTAMP, ts);
 

Reply via email to