This is an automated email from the ASF dual-hosted git repository. abhishekrb pushed a commit to branch ordered_event_map in repository https://gitbox.apache.org/repos/asf/druid.git
commit c0e34431738277d380b3a57549a9c35fda61df19 Author: Abhishek Balaji Radhakrishnan <[email protected]> AuthorDate: Fri Feb 6 13:48:10 2026 -0800 Checkpoint changes --- .../druid/java/util/emitter/core/EventMap.java | 6 ++- .../util/emitter/service/ServiceMetricEvent.java | 2 +- .../druid/java/util/emitter/core/EventMapTest.java | 48 ++++++++++++++++++++++ .../emitter/service/ServiceMetricEventTest.java | 25 +++++++++++ .../org/apache/druid/server/RequestLogLine.java | 11 +++-- .../druid/server/log/DefaultRequestLogEvent.java | 5 +-- .../server/log/DefaultRequestLogEventTest.java | 13 +++++- 7 files changed, 98 insertions(+), 12 deletions(-) diff --git a/processing/src/main/java/org/apache/druid/java/util/emitter/core/EventMap.java b/processing/src/main/java/org/apache/druid/java/util/emitter/core/EventMap.java index 50237eb0238..e1f5d8e13a3 100644 --- a/processing/src/main/java/org/apache/druid/java/util/emitter/core/EventMap.java +++ b/processing/src/main/java/org/apache/druid/java/util/emitter/core/EventMap.java @@ -22,16 +22,18 @@ package org.apache.druid.java.util.emitter.core; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** - * EventMap is a hash map implementation. It can be safely serialzed to JSON using Jackson serializer as it + * EventMap is a linked hash map implementation where the insertion order of key-value pairs is maintained. + * It can be safely serialzed to JSON using Jackson serializer as it * respects the polymorphic annotations on entires (unlike standard Map). The example of polymorphic class is a query * interface, where different native query types are resolved by additional field called "queryType". * This implementation ensures that the annotation on the values are respected during serialization. */ @JsonSerialize(using = EventMapSerializer.class) -public class EventMap extends HashMap<String, Object> +public class EventMap extends LinkedHashMap<String, Object> { /** * Returns builder with Fluent API to build EventMap instance using method chaining diff --git a/processing/src/main/java/org/apache/druid/java/util/emitter/service/ServiceMetricEvent.java b/processing/src/main/java/org/apache/druid/java/util/emitter/service/ServiceMetricEvent.java index 41395ffea58..31f77a51283 100644 --- a/processing/src/main/java/org/apache/druid/java/util/emitter/service/ServiceMetricEvent.java +++ b/processing/src/main/java/org/apache/druid/java/util/emitter/service/ServiceMetricEvent.java @@ -116,9 +116,9 @@ public class ServiceMetricEvent implements Event .builder() .put(FEED, getFeed()) .put(TIMESTAMP, createdTime.toString()) - .putAll(serviceDims) .put(METRIC, metric) .put(VALUE, value) + .putAll(serviceDims) .putAll(userDims) .build(); } diff --git a/processing/src/test/java/org/apache/druid/java/util/emitter/core/EventMapTest.java b/processing/src/test/java/org/apache/druid/java/util/emitter/core/EventMapTest.java new file mode 100644 index 00000000000..68a6b90a9af --- /dev/null +++ b/processing/src/test/java/org/apache/druid/java/util/emitter/core/EventMapTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.java.util.emitter.core; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.apache.druid.jackson.DefaultObjectMapper; +import org.junit.Assert; +import org.junit.Test; + +public class EventMapTest +{ + @Test + public void testSerializedEventMapIsOrdered() throws JsonProcessingException + { + final EventMap map = EventMap.builder() + .put("k1", "v1") + .put("k2", "v2") + .put("k3", "v3") + .put("k4", "v4") + .put("k5", "v5") + .put("k0", "v0") + .put("a0", "a0") + .put("x0", "x0") + .build(); + + Assert.assertEquals( + "{\"k1\":\"v1\",\"k2\":\"v2\",\"k3\":\"v3\",\"k4\":\"v4\",\"k5\":\"v5\",\"k0\":\"v0\",\"a0\":\"a0\",\"x0\":\"x0\"}", + new DefaultObjectMapper().writeValueAsString(map) + ); + } +} \ No newline at end of file diff --git a/processing/src/test/java/org/apache/druid/java/util/emitter/service/ServiceMetricEventTest.java b/processing/src/test/java/org/apache/druid/java/util/emitter/service/ServiceMetricEventTest.java index 2b942870802..a9d58facf85 100644 --- a/processing/src/test/java/org/apache/druid/java/util/emitter/service/ServiceMetricEventTest.java +++ b/processing/src/test/java/org/apache/druid/java/util/emitter/service/ServiceMetricEventTest.java @@ -20,9 +20,14 @@ package org.apache.druid.java.util.emitter.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; +import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.IAE; +import org.apache.druid.java.util.emitter.core.EventMap; +import org.joda.time.DateTime; import org.junit.Assert; import org.junit.Test; @@ -51,6 +56,7 @@ public class ServiceMetricEventTest .setDimension("user10", "j") .setMetric("test-metric", 1234) .build("test", "localhost"); + Assert.assertEquals( ImmutableMap.<String, Object>builder() .put("feed", "metrics") @@ -359,4 +365,23 @@ public class ServiceMetricEventTest () -> eventBuilder.setDimension(null, new String[]{"a"}) ); } + + @Test + public void testSerializedServiceMetricEventIsOrdered() throws JsonProcessingException + { + ObjectMapper mapper = new DefaultObjectMapper(); + ServiceMetricEvent event = ServiceMetricEvent.builder() + .setCreatedTime(DateTimes.of("2026-01-01")) + .setMetric("m1", 42) + .setDimension("dim1", "xyz") + .setFeed("test_feed") + .setDimension("dim2", "xyz") + .build("broker", "hostA"); + + + Assert.assertEquals( + "{\"feed\":\"test_feed\",\"timestamp\":\"2026-01-01T00:00:00.000Z\",\"metric\":\"m1\",\"value\":42,\"service\":\"broker\",\"host\":\"hostA\",\"dim1\":\"xyz\",\"dim2\":\"xyz\"}", + mapper.writeValueAsString(event.toMap()) + ); + } } diff --git a/server/src/main/java/org/apache/druid/server/RequestLogLine.java b/server/src/main/java/org/apache/druid/server/RequestLogLine.java index 0e744520cc3..9d22f07c5f0 100644 --- a/server/src/main/java/org/apache/druid/server/RequestLogLine.java +++ b/server/src/main/java/org/apache/druid/server/RequestLogLine.java @@ -31,6 +31,7 @@ import org.joda.time.DateTime; import javax.annotation.Nullable; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -92,16 +93,18 @@ public class RequestLogLine public String getSqlQueryLine(ObjectMapper objectMapper) throws JsonProcessingException { + + final Map<String, Object> queryMap = new LinkedHashMap<>(); + queryMap.put("context", sqlQueryContext); + queryMap.put("query", sql == null ? "<unavailable>" : sql); + return JOINER.join( Arrays.asList( timestamp, remoteAddr, "", objectMapper.writeValueAsString(queryStats), - objectMapper.writeValueAsString(ImmutableMap.of( - "query", sql == null ? "<unavailable>" : sql, - "context", sqlQueryContext - )) + objectMapper.writeValueAsString(queryMap) ) ); } diff --git a/server/src/main/java/org/apache/druid/server/log/DefaultRequestLogEvent.java b/server/src/main/java/org/apache/druid/server/log/DefaultRequestLogEvent.java index d7f75d216eb..e22131f09f1 100644 --- a/server/src/main/java/org/apache/druid/server/log/DefaultRequestLogEvent.java +++ b/server/src/main/java/org/apache/druid/server/log/DefaultRequestLogEvent.java @@ -63,6 +63,8 @@ public final class DefaultRequestLogEvent implements RequestLogEvent .put("timestamp", getCreatedTime()) .put("service", getService()) .put("host", getHost()) + .put("remoteAddr", getRemoteAddr()) + .put("queryStats", getQueryStats()) .putNonNull("query", getQuery()); if (getSql() != null) { @@ -70,9 +72,6 @@ public final class DefaultRequestLogEvent implements RequestLogEvent .put("sqlQueryContext", getSqlQueryContext()); } - builder.put("remoteAddr", getRemoteAddr()) - .put("queryStats", getQueryStats()); - return builder.build(); } diff --git a/server/src/test/java/org/apache/druid/server/log/DefaultRequestLogEventTest.java b/server/src/test/java/org/apache/druid/server/log/DefaultRequestLogEventTest.java index 0f7e00b484e..631a4d4ccb0 100644 --- a/server/src/test/java/org/apache/druid/server/log/DefaultRequestLogEventTest.java +++ b/server/src/test/java/org/apache/druid/server/log/DefaultRequestLogEventTest.java @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.jackson.DefaultObjectMapper; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.granularity.Granularities; import org.apache.druid.java.util.emitter.core.Event; import org.apache.druid.java.util.emitter.core.EventMap; @@ -126,7 +127,7 @@ public class DefaultRequestLogEventTest } @Test - public void testDefaultRequestLogEventToMapSQL() + public void testDefaultRequestLogEventToMapSQL() throws JsonProcessingException { final String feed = "test"; final DateTime timestamp = DateTimes.of(2019, 12, 12, 3, 1); @@ -164,7 +165,15 @@ public class DefaultRequestLogEventTest expected.put("remoteAddr", host); expected.put("queryStats", queryStats); - Assert.assertEquals(expected, defaultRequestLogEvent.toMap()); + EventMap observedMap = defaultRequestLogEvent.toMap(); + Assert.assertEquals(expected, observedMap); + Assert.assertEquals( + StringUtils.format( + "{\"feed\":\"test\",\"timestamp\":\"%s\",\"service\":\"druid-service\",\"host\":\"127.0.0.1\",\"remoteAddr\":\"127.0.0.1\",\"queryStats\":{\"sqlQuery/time\":13,\"sqlQuery/planningTimeMs\":1,\"sqlQuery/bytes\":10,\"success\":true,\"identity\":\"allowAll\"},\"sql\":\"select * from 1337\",\"sqlQueryContext\":{}}", + timestamp + ), + new DefaultObjectMapper().writeValueAsString(observedMap) + ); } @Test --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
