You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by vy...@apache.org on 2020/11/06 12:44:55 UTC

[logging-log4j2] 07/07: LOG4J2-2936 Add message parameter resolver to JSON template layout.

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

vy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 51278468e49ff1b4548225a6a38f060061694547
Author: Volkan Yazici <vo...@gmail.com>
AuthorDate: Fri Nov 6 13:30:39 2020 +0100

    LOG4J2-2936 Add message parameter resolver to JSON template layout.
---
 .../json/resolver/EventResolverFactories.java      |   1 +
 .../template/json/resolver/LevelResolver.java      |   2 +-
 .../json/resolver/MessageParameterResolver.java    | 131 +++++++++++++++++++++
 .../resolver/MessageParameterResolverFactory.java  |  41 +++++++
 .../template/json/JsonTemplateLayoutTest.java      |  51 ++++++++
 .../asciidoc/manual/json-template-layout.adoc.vm   |  52 ++++++++
 6 files changed, 277 insertions(+), 1 deletion(-)

diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverFactories.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverFactories.java
index 07fe97b..b4b4f49 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverFactories.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/EventResolverFactories.java
@@ -44,6 +44,7 @@ enum EventResolverFactories {;
                 MapResolverFactory.getInstance(),
                 MarkerResolverFactory.getInstance(),
                 MessageResolverFactory.getInstance(),
+                MessageParameterResolverFactory.getInstance(),
                 PatternResolverFactory.getInstance(),
                 SourceResolverFactory.getInstance(),
                 ThreadResolverFactory.getInstance(),
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/LevelResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/LevelResolver.java
index 89de7e0..2952eb4 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/LevelResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/LevelResolver.java
@@ -27,7 +27,7 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
- * Level resolver.
+ * {@link Level} resolver.
  *
  * <h3>Configuration</h3>
  *
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
new file mode 100644
index 0000000..a21dff6
--- /dev/null
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
@@ -0,0 +1,131 @@
+/*
+ * 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.logging.log4j.layout.template.json.resolver;
+
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
+import org.apache.logging.log4j.message.Message;
+
+/**
+ * {@link Message} parameter (i.e., {@link Message#getParameters()}) resolver.
+ *
+ * <h3>Configuration</h3>
+ *
+ * <pre>
+ * config      = [ stringified ] , [ index ]
+ * stringified = "stringified" -> boolean
+ * index       = "index" -> number
+ * </pre>
+ *
+ * <h3>Examples</h3>
+ *
+ * Resolve the message parameters into an array:
+ *
+ * <pre>
+ * {
+ *   "$resolver": "messageParameter"
+ * }
+ * </pre>
+ *
+ * Resolve the string representation of all message parameters into an array:
+ *
+ * <pre>
+ * {
+ *   "$resolver": "messageParameter",
+ *   "stringified": true
+ * }
+ * </pre>
+ *
+ * Resolve the first message parameter:
+ *
+ * <pre>
+ * {
+ *   "$resolver": "messageParameter",
+ *   "index": 0
+ * }
+ *
+ * Resolve the string representation of the first message parameter:
+ *
+ * <pre>
+ * {
+ *   "$resolver": "messageParameter",
+ *   "index": 0,
+ *   "stringified": true
+ * }
+ * </pre>
+ */
+final class MessageParameterResolver implements EventResolver {
+
+    private final boolean stringified;
+
+    private final int index;
+
+    MessageParameterResolver(final TemplateResolverConfig config) {
+        this.stringified = config.getBoolean("stringified", false);
+        final Integer index = config.getInteger("index");
+        if (index != null && index < 0) {
+            throw new IllegalArgumentException("was expecting a positive index: " + config);
+        }
+        this.index = index == null ? -1 : index;
+    }
+
+    static String getName() {
+        return "messageParameter";
+    }
+
+    @Override
+    public void resolve(final LogEvent logEvent, final JsonWriter jsonWriter) {
+
+        // Short-circuit if there are no parameters.
+        final Object[] parameters = logEvent.getMessage().getParameters();
+        if (parameters.length == 0) {
+            jsonWriter.writeNull();
+            return;
+        }
+
+        // Resolve all parameters.
+        if (index < 0) {
+            jsonWriter.writeArrayStart();
+            for (int i = 0; i < parameters.length; i++) {
+                if (i > 0) {
+                    jsonWriter.writeSeparator();
+                }
+                final Object parameter = parameters[i];
+                if (stringified) {
+                    final String stringifiedParameter = String.valueOf(parameter);
+                    jsonWriter.writeString(stringifiedParameter);
+                } else {
+                    jsonWriter.writeValue(parameter);
+                }
+            }
+            jsonWriter.writeArrayEnd();
+        }
+
+        // Resolve a single parameter.
+        else {
+            final Object parameter = parameters[index];
+            if (stringified) {
+                final String stringifiedParameter = String.valueOf(parameter);
+                jsonWriter.writeString(stringifiedParameter);
+            } else {
+                jsonWriter.writeValue(parameter);
+            }
+        }
+
+    }
+
+}
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolverFactory.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolverFactory.java
new file mode 100644
index 0000000..db1c369
--- /dev/null
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolverFactory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.logging.log4j.layout.template.json.resolver;
+
+final class MessageParameterResolverFactory implements EventResolverFactory<MessageParameterResolver> {
+
+    private static final MessageParameterResolverFactory INSTANCE = new MessageParameterResolverFactory();
+
+    private MessageParameterResolverFactory() {}
+
+    static MessageParameterResolverFactory getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String getName() {
+        return MessageParameterResolver.getName();
+    }
+
+    @Override
+    public MessageParameterResolver create(
+            final EventResolverContext context,
+            final TemplateResolverConfig config) {
+        return new MessageParameterResolver(config);
+    }
+
+}
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
index 9de4d87..782db2d 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
@@ -40,6 +40,7 @@ import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
 import org.apache.logging.log4j.layout.template.json.util.MapAccessor;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessage;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.message.StringMapMessage;
 import org.apache.logging.log4j.test.AvailablePortFinder;
@@ -1786,6 +1787,56 @@ public class JsonTemplateLayoutTest {
     }
 
     @Test
+    public void test_MessageParameterResolver() {
+
+        // Create the event template.
+        final String eventTemplate = writeJson(Map(
+                "po*", Map(
+                        "$resolver", "messageParameter"),
+                "ps*", Map(
+                        "$resolver", "messageParameter",
+                        "stringified", true),
+                "po2", Map(
+                        "$resolver", "messageParameter",
+                        "index", 2),
+                "ps2", Map(
+                        "$resolver", "messageParameter",
+                        "index", 2,
+                        "stringified", true)));
+
+        // Create the layout.
+        final JsonTemplateLayout layout = JsonTemplateLayout
+                .newBuilder()
+                .setConfiguration(CONFIGURATION)
+                .setEventTemplate(eventTemplate)
+                .build();
+
+        // Create the log event.
+        final Object[] parameters = {1L + (long) Integer.MAX_VALUE, "foo", 56};
+        final Message message = new ParameterizedMessage("foo", parameters);
+        final Level level = Level.FATAL;
+        final LogEvent logEvent = Log4jLogEvent
+                .newBuilder()
+                .setLoggerName(LOGGER_NAME)
+                .setMessage(message)
+                .setLevel(level)
+                .build();
+
+        // Check the serialized event.
+        usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
+            assertThat(accessor.getObject("po*")).isEqualTo(Arrays.asList(parameters));
+            List<String> stringifiedParameters = Arrays
+                    .stream(parameters)
+                    .map(String::valueOf)
+                    .collect(Collectors.toList());
+            assertThat(accessor.getObject("ps*")).isEqualTo(stringifiedParameters);
+            assertThat(accessor.getObject("po2")).isEqualTo(parameters[2]);
+            assertThat(accessor.getString("ps2")).isEqualTo(stringifiedParameters.get(2));
+        });
+
+    }
+
+    @Test
     public void test_unresolvable_nested_fields_are_skipped() {
 
         // Create the event template.
diff --git a/src/site/asciidoc/manual/json-template-layout.adoc.vm b/src/site/asciidoc/manual/json-template-layout.adoc.vm
index 1c370ca..2bc7e44 100644
--- a/src/site/asciidoc/manual/json-template-layout.adoc.vm
+++ b/src/site/asciidoc/manual/json-template-layout.adoc.vm
@@ -804,6 +804,58 @@ Using this configuration, a `SimpleMessage` will generate a
 `{"action": "login", "sessionId": "87asd97a"}`. Note that both emitted JSONs are
 of type `object` and have no type-conflicting fields.
 
+| messageParameter
+a|
+[source]
+----
+config      = [ stringified ] , [ index ]
+stringified = "stringified" -> boolean
+index       = "index" -> number
+----
+| `logEvent.getMessage().getParameters()`
+| `stringified` flag translates to `String.valueOf(value)`, hence mind
+  not-`String`-typed values.
+a|
+Resolve the message parameters into an array:
+
+[source,json]
+----
+{
+  "$resolver": "messageParameter"
+}
+----
+
+Resolve the string representation of all message parameters into an array:
+
+[source,json]
+----
+{
+  "$resolver": "messageParameter",
+  "stringified": true
+}
+----
+
+Resolve the first message parameter:
+
+[source,json]
+----
+{
+  "$resolver": "messageParameter",
+  "index": 0
+}
+----
+
+Resolve the string representation of the first message parameter:
+
+[source,json]
+----
+{
+  "$resolver": "messageParameter",
+  "index": 0,
+  "stringified": true
+}
+----
+
 | ndc
 a|
 [source]