You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@logging.apache.org by GitBox <gi...@apache.org> on 2020/01/28 20:03:07 UTC

[GitHub] [logging-log4j2] carterkozak commented on a change in pull request #335: Import of LogstashLayout as JsonTemplateLayout

carterkozak commented on a change in pull request #335: Import of LogstashLayout as JsonTemplateLayout
URL: https://github.com/apache/logging-log4j2/pull/335#discussion_r372026814
 
 

 ##########
 File path: log4j-layout-jackson-json-template/src/main/java/org/apache/logging/log4j/jackson/json/template/layout/JsonTemplateLayout.java
 ##########
 @@ -0,0 +1,678 @@
+/*
+ * Copyright 2017-2020 Volkan Yazıcı
+ *
+ * Licensed 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 permits and
+ * limitations under the License.
+ */
+
+package org.apache.logging.log4j.jackson.json.template.layout;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+import org.apache.logging.log4j.core.layout.ByteBufferDestinationHelper;
+import org.apache.logging.log4j.core.lookup.StrSubstitutor;
+import org.apache.logging.log4j.core.time.internal.format.FastDateFormat;
+import org.apache.logging.log4j.core.util.Constants;
+import org.apache.logging.log4j.core.util.KeyValuePair;
+import org.apache.logging.log4j.jackson.json.template.layout.resolver.EventResolverContext;
+import org.apache.logging.log4j.jackson.json.template.layout.resolver.StackTraceElementObjectResolverContext;
+import org.apache.logging.log4j.jackson.json.template.layout.resolver.TemplateResolver;
+import org.apache.logging.log4j.jackson.json.template.layout.resolver.TemplateResolvers;
+import org.apache.logging.log4j.jackson.json.template.layout.util.ByteBufferOutputStream;
+import org.apache.logging.log4j.jackson.json.template.layout.util.Uris;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.util.Strings;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.TimeZone;
+import java.util.function.Supplier;
+
+@Plugin(name = "JsonTemplateLayout",
+        category = Node.CATEGORY,
+        elementType = Layout.ELEMENT_TYPE,
+        printObject = true)
+public class JsonTemplateLayout implements StringLayout {
+
+    private static final Map<String, String> CONTENT_FORMAT = Collections.singletonMap("version", "1");
+
+    private final Charset charset;
+
+    private final String contentType;
+
+    private final byte[] emptyObjectJsonBytes;
+
+    private final TemplateResolver<LogEvent> eventResolver;
+
+    private final byte[] eventDelimiterBytes;
+
+    private final Supplier<JsonTemplateLayoutSerializationContext> serializationContextSupplier;
+
+    private final ThreadLocal<JsonTemplateLayoutSerializationContext> serializationContextRef;
+
+    private JsonTemplateLayout(final Builder builder) {
+        this.charset = builder.charset;
+        this.contentType = "application/json; charset=" + charset;
+        this.emptyObjectJsonBytes = "{}".getBytes(charset);
+        this.eventDelimiterBytes = builder.eventDelimiter.getBytes(charset);
+        final ObjectMapper objectMapper = createObjectMapper(builder.objectMapperFactoryMethod);
+        final StrSubstitutor substitutor = builder.configuration.getStrSubstitutor();
+        final TemplateResolver<StackTraceElement> stackTraceElementObjectResolver =
+                builder.stackTraceEnabled
+                        ? createStackTraceElementResolver(builder, objectMapper, substitutor)
+                        : null;
+        this.eventResolver = createEventResolver(builder, objectMapper, substitutor, stackTraceElementObjectResolver);
+        this.serializationContextSupplier = createSerializationContextSupplier(builder, objectMapper);
+        this.serializationContextRef = Constants.ENABLE_THREADLOCALS
+                ? ThreadLocal.withInitial(serializationContextSupplier)
+                : null;
+    }
+
+    private static ObjectMapper createObjectMapper(final String objectMapperFactoryMethod) {
+        try {
+            final int splitterIndex = objectMapperFactoryMethod.lastIndexOf('.');
+            final String className = objectMapperFactoryMethod.substring(0, splitterIndex);
+            final String methodName = objectMapperFactoryMethod.substring(splitterIndex + 1);
+            final Class<?> clazz = Class.forName(className);
+            if ("new".equals(methodName)) {
+                return (ObjectMapper) clazz.newInstance();
+            } else {
+                final Method method = clazz.getMethod(methodName);
+                return (ObjectMapper) method.invoke(null);
+            }
+        } catch (final Exception error) {
+            throw new RuntimeException(error);
+        }
+    }
+
+    private static TemplateResolver<StackTraceElement> createStackTraceElementResolver(
+            final Builder builder,
+            final ObjectMapper objectMapper,
+            final StrSubstitutor substitutor) {
+        final StackTraceElementObjectResolverContext stackTraceElementObjectResolverContext =
+                StackTraceElementObjectResolverContext
+                        .newBuilder()
+                        .setObjectMapper(objectMapper)
+                        .setSubstitutor(substitutor)
+                        .setBlankPropertyExclusionEnabled(builder.blankPropertyExclusionEnabled)
+                        .build();
+        final String stackTraceElementTemplate = readStackTraceElementTemplate(builder);
+        return TemplateResolvers.ofTemplate(stackTraceElementObjectResolverContext, stackTraceElementTemplate);
+    }
+
+    private TemplateResolver<LogEvent> createEventResolver(
+            final Builder builder,
+            final ObjectMapper objectMapper,
+            final StrSubstitutor substitutor,
+            final TemplateResolver<StackTraceElement> stackTraceElementObjectResolver) {
+        final String eventTemplate = readEventTemplate(builder);
+        final int writerCapacity = builder.maxStringLength > 0
+                ? builder.maxStringLength
+                : builder.maxByteCount;
+        final Locale locale = readLocale(builder.locale);
+        final FastDateFormat timestampFormat =
+                FastDateFormat.getInstance(
+                        builder.dateTimeFormatPattern, builder.timeZone, locale);
+        final EventResolverContext resolverContext = EventResolverContext
+                .newBuilder()
+                .setObjectMapper(objectMapper)
+                .setSubstitutor(substitutor)
+                .setWriterCapacity(writerCapacity)
+                .setTimeZone(builder.timeZone)
+                .setLocale(locale)
+                .setTimestampFormat(timestampFormat)
+                .setLocationInfoEnabled(builder.locationInfoEnabled)
+                .setStackTraceEnabled(builder.stackTraceEnabled)
+                .setStackTraceElementObjectResolver(stackTraceElementObjectResolver)
+                .setBlankPropertyExclusionEnabled(builder.blankPropertyExclusionEnabled)
+                .setMdcKeyPattern(builder.mdcKeyPattern)
+                .setNdcPattern(builder.ndcPattern)
+                .setEventTemplateAdditionalFields(builder.eventTemplateAdditionalFields.additionalFields)
+                .setMapMessageFormatterIgnored(builder.mapMessageFormatterIgnored)
+                .build();
+        return TemplateResolvers.ofTemplate(resolverContext, eventTemplate);
+    }
+
+    private static Supplier<JsonTemplateLayoutSerializationContext>
+    createSerializationContextSupplier(
+            final Builder builder,
+            final ObjectMapper objectMapper) {
+        return JsonTemplateLayoutSerializationContexts.createSupplier(
+                objectMapper,
+                builder.maxByteCount,
+                builder.prettyPrintEnabled,
+                builder.blankPropertyExclusionEnabled,
+                builder.maxStringLength);
+    }
+
+    private static String readEventTemplate(final Builder builder) {
+        return readTemplate(builder.eventTemplate, builder.eventTemplateUri);
+    }
+
+    private static String readStackTraceElementTemplate(final Builder builder) {
+        return readTemplate(builder.stackTraceElementTemplate, builder.stackTraceElementTemplateUri);
+    }
+
+    private static String readTemplate(final String template, final String templateUri) {
+        return Strings.isBlank(template)
+                ? Uris.readUri(templateUri)
+                : template;
+    }
+
+    private static Locale readLocale(final String locale) {
+        if (locale == null) {
+            return Locale.getDefault();
+        }
+        final String[] localeFields = locale.split("_", 3);
+        switch (localeFields.length) {
+            case 1: return new Locale(localeFields[0]);
+            case 2: return new Locale(localeFields[0], localeFields[1]);
+            case 3: return new Locale(localeFields[0], localeFields[1], localeFields[2]);
+        }
+        throw new IllegalArgumentException("invalid locale: " + locale);
+    }
+
+    @Override
+    public String toSerializable(final LogEvent event) {
+        final JsonTemplateLayoutSerializationContext context = getResetSerializationContext();
+        try {
+            encode(event, context);
+            return context.getOutputStream().toString(charset);
+        } catch (final Exception error) {
+            reloadSerializationContext(error, context);
+            throw new RuntimeException("failed serializing JSON", error);
+        }
+    }
+
+    @Override
+    public byte[] toByteArray(final LogEvent event) {
+        final JsonTemplateLayoutSerializationContext context = getResetSerializationContext();
+        try {
+            encode(event, context);
+            return context.getOutputStream().toByteArray();
+        } catch (final Exception error) {
+            reloadSerializationContext(error, context);
+            throw new RuntimeException("failed serializing JSON", error);
+        }
+    }
+
+    @Override
+    public void encode(final LogEvent event, final ByteBufferDestination destination) {
+        final JsonTemplateLayoutSerializationContext context = getResetSerializationContext();
+        try {
+            encode(event, context);
+            final ByteBuffer byteBuffer = context.getOutputStream().getByteBuffer();
+            byteBuffer.flip();
+            // noinspection SynchronizationOnLocalVariableOrMethodParameter
+            synchronized (destination) {
+                ByteBufferDestinationHelper.writeToUnsynchronized(byteBuffer, destination);
+            }
+        } catch (final Exception error) {
+            reloadSerializationContext(error, context);
+            throw new RuntimeException("failed serializing JSON", error);
+        }
+    }
+
+    // Visible for tests.
+    JsonTemplateLayoutSerializationContext getSerializationContext() {
+        return Constants.ENABLE_THREADLOCALS
+                ? serializationContextRef.get()
+                : serializationContextSupplier.get();
+    }
+
+    private JsonTemplateLayoutSerializationContext getResetSerializationContext() {
+        JsonTemplateLayoutSerializationContext context;
+        if (Constants.ENABLE_THREADLOCALS) {
+            context = serializationContextRef.get();
+            context.reset();
+        } else {
+            context = serializationContextSupplier.get();
+        }
+        return context;
+    }
+
+    private void reloadSerializationContext(
+            final Exception cause,
+            final JsonTemplateLayoutSerializationContext oldContext) {
+        try {
+            oldContext.close();
+        } catch (final Exception error) {
+            System.err.println("context close attempt suppressing the error:");
+            cause.printStackTrace(System.err);
 
 Review comment:
   The StatusLogger is likely what you want here, it's a meta-logger for reporting issues with the logging implementation.

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services