Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature request: objectMessageAsJsonObject #187

Open
hdevalke opened this issue May 16, 2022 · 1 comment
Open

feature request: objectMessageAsJsonObject #187

hdevalke opened this issue May 16, 2022 · 1 comment
Labels
agent-java community Issues and PRs created by the community triage Issues and PRs that need to be triaged

Comments

@hdevalke
Copy link

Log4j2 ObjectMessages are automatically converted to json objects.
This makes the log statements fail, if the object cannot be converted to json or it generates invalid json.

I would propose a objectMessageAsJsonObject config.
Putting it to false would just put the objects toString() result into the message field, as described in the documentation of log4j2.

e.g.

public class EcsLogTest {

    static class A {
        String getA() {
            Objects.requireNonNull(null);
            return null;
        }
    }

    @Test
    void test() {
        LogManager.getLogger().info(new A());
    }
}

produces:

{"@timestamp":"2022-05-16T12:06:08.284Z", "log.level": "INFO",   , "ecs.version": "1.2.0","process.thread.name":"main","log.logger":"EcsLogTest"}

This patch would probably enough to implement this feature:

diff --git a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java
index 1a99742..41937c7 100644
--- a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java
+++ b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java
@@ -79,9 +79,10 @@ public class EcsLayout extends AbstractStringLayout {
     private final boolean includeOrigin;
     private final PatternFormatter[] exceptionPatternFormatter;
     private final ConcurrentMap<Class<? extends MultiformatMessage>, Boolean> supportsJson = new ConcurrentHashMap<Class<? extends MultiformatMessage>, Boolean>();
+    private final boolean objectMessageAsJsonObject;
 
     private EcsLayout(Configuration config, String serviceName, String serviceVersion, String serviceEnvironment, String serviceNodeName, String eventDataset, boolean includeMarkers,
-                      KeyValuePair[] additionalFields, boolean includeOrigin, String exceptionPattern, boolean stackTraceAsArray) {
+                      KeyValuePair[] additionalFields, boolean includeOrigin, String exceptionPattern, boolean stackTraceAsArray, boolean objectMessageAsJsonObject) {
         super(config, UTF_8, null, null);
         this.serviceName = serviceName;
         this.serviceVersion = serviceVersion;
@@ -92,6 +93,7 @@ public class EcsLayout extends AbstractStringLayout {
         this.includeOrigin = includeOrigin;
         this.stackTraceAsArray = stackTraceAsArray;
         this.additionalFields = additionalFields;
+        this.objectMessageAsJsonObject = objectMessageAsJsonObject;
         fieldValuePatternFormatter = new PatternFormatter[additionalFields.length][];
         for (int i = 0; i < additionalFields.length; i++) {
             KeyValuePair additionalField = additionalFields[i];
@@ -253,7 +255,7 @@ public class EcsLayout extends AbstractStringLayout {
             } else {
                 serializeSimpleMessage(builder, gcFree, message, thrown);
             }
-        } else if (JACKSON_SERIALIZER != null && message instanceof ObjectMessage) {
+        } else if (JACKSON_SERIALIZER != null && message instanceof ObjectMessage && objectMessageAsJsonObject) {
             final StringBuilder jsonBuffer = EcsJsonSerializer.getMessageStringBuilder();
             JACKSON_SERIALIZER.formatTo(jsonBuffer, (ObjectMessage) message);
             addJson(builder, jsonBuffer);
@@ -377,6 +379,8 @@ public class EcsLayout extends AbstractStringLayout {
         private KeyValuePair[] additionalFields = new KeyValuePair[]{};
         @PluginBuilderAttribute("includeOrigin")
         private boolean includeOrigin = false;
+        @PluginBuilderAttribute("objectMessageAsJsonObject")
+        private boolean objectMessageAsJsonObject = true;
 
         Builder() {
         }
@@ -428,6 +432,10 @@ public class EcsLayout extends AbstractStringLayout {
             return exceptionPattern;
         }
 
+        public boolean isObjectMessageAsJsonObject() {
+            return objectMessageAsJsonObject;
+        }
+
         /**
          * Additional fields to set on each log event.
          *
@@ -483,11 +491,16 @@ public class EcsLayout extends AbstractStringLayout {
             return this;
         }
 
+        public EcsLayout.Builder setObjectMessageAsJsonObject(boolean objectMessageAsJsonObject) {
+            this.objectMessageAsJsonObject = objectMessageAsJsonObject;
+            return this;
+        }
+
         @Override
         public EcsLayout build() {
             return new EcsLayout(getConfiguration(), serviceName, serviceVersion, serviceEnvironment, serviceNodeName,
                     EcsJsonSerializer.computeEventDataset(eventDataset, serviceName),
-                    includeMarkers, additionalFields, includeOrigin, exceptionPattern, stackTraceAsArray);
+                    includeMarkers, additionalFields, includeOrigin, exceptionPattern, stackTraceAsArray, objectMessageAsJsonObject);
         }
     }
 }
@github-actions github-actions bot added agent-java community Issues and PRs created by the community triage Issues and PRs that need to be triaged labels May 16, 2022
@hdevalke
Copy link
Author

Another option is to bypass log4j to create object messages using a custom message factory and use -Dlog4j2.messageFactory=CustomMessageFactory. But I think a configuration property is a nicer solution.

public class CustomMessageFactory extends AbstractMessageFactory {
	@Override
	public Message newMessage(String message, Object... params) {
		return new ParameterizedMessage(message, params);
	}

	@Override
	public Message newMessage(Object message) {
		return new ParameterizedMessage("{}", message);
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
agent-java community Issues and PRs created by the community triage Issues and PRs that need to be triaged
Projects
None yet
Development

No branches or pull requests

1 participant