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