All Downloads are FREE. Search and download functionalities are using the official Maven repository.

biz.paluch.logging.gelf.GelfMessageAssembler Maven / Gradle / Ivy

The newest version!
package biz.paluch.logging.gelf;

import static biz.paluch.logging.gelf.GelfMessageBuilder.newInstance;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

import biz.paluch.logging.RuntimeContainer;
import biz.paluch.logging.StackTraceFilter;
import biz.paluch.logging.gelf.LogMessageField.NamedLogField;
import biz.paluch.logging.gelf.intern.GelfMessage;
import biz.paluch.logging.gelf.intern.HostAndPortProvider;
import biz.paluch.logging.gelf.intern.PoolingGelfMessageBuilder;

/**
 * Creates {@link GelfMessage} based on various {@link LogEvent}. A {@link LogEvent} encapsulates log-framework specifics and
 * exposes commonly used details of log events.
 *
 * @author Mark Paluch
 * @author Thomas Herzog
 * @since 26.09.13 15:05
 */
public class GelfMessageAssembler implements HostAndPortProvider {

    /**
     * @deprecated see {@link PoolingGelfMessageBuilder#PROPERTY_USE_POOLING}.
     */
    @Deprecated
    public static final String PROPERTY_USE_POOLING = "logstash-gelf.message.pooling";

    public static final String FIELD_MESSAGE_PARAM = "MessageParam";
    public static final String FIELD_STACK_TRACE = "StackTrace";

    private static final int MAX_SHORT_MESSAGE_LENGTH = 250;
    private static final int MAX_PORT_NUMBER = 65535;
    private static final int MAX_MESSAGE_SIZE = Integer.MAX_VALUE;
    private static final Set SOURCE_FIELDS = EnumSet.of(NamedLogField.SourceClassName,
            NamedLogField.SourceSimpleClassName, NamedLogField.SourceMethodName, NamedLogField.SourceLineNumber);

    private String host;
    private String version = GelfMessage.GELF_VERSION;
    private String originHost;
    private int port;
    private String facility;
    private boolean includeLogMessageParameters = true;
    private boolean includeLocation = true;
    private StackTraceExtraction stackTraceExtraction = StackTraceExtraction.OFF;
    private int maximumMessageSize = 8192;

    private List fields = new ArrayList<>();
    private Map additionalFieldTypes = new HashMap<>();
    private Map dynamicMdcFieldTypes = new LinkedHashMap<>();

    private String timestampPattern = "yyyy-MM-dd HH:mm:ss,SSS";

    private final ThreadLocal builders;

    public GelfMessageAssembler() {

        if (PoolingGelfMessageBuilder.usePooling()) {

            builders = new ThreadLocal() {

                @Override
                protected PoolingGelfMessageBuilder initialValue() {
                    return PoolingGelfMessageBuilder.newInstance();
                }
            };
        } else {
            builders = null;
        }
    }

    /**
     * Initialize the {@link GelfMessageAssembler} from a property provider.
     *
     * @param propertyProvider property provider to obtain configuration properties
     */
    public void initialize(PropertyProvider propertyProvider) {

        host = propertyProvider.getProperty(PropertyProvider.PROPERTY_HOST);
        if (host == null) {
            host = propertyProvider.getProperty(PropertyProvider.PROPERTY_GRAYLOG_HOST);
        }

        String port = propertyProvider.getProperty(PropertyProvider.PROPERTY_PORT);
        if (port == null) {
            port = propertyProvider.getProperty(PropertyProvider.PROPERTY_GRAYLOG_PORT);
        }

        if (port != null && !"".equals(port)) {
            this.port = Integer.parseInt(port);
        }

        originHost = propertyProvider.getProperty(PropertyProvider.PROPERTY_ORIGIN_HOST);
        setExtractStackTrace(propertyProvider.getProperty(PropertyProvider.PROPERTY_EXTRACT_STACKTRACE));
        setFilterStackTrace("true".equalsIgnoreCase(propertyProvider.getProperty(PropertyProvider.PROPERTY_FILTER_STACK_TRACE)));

        String includeLogMessageParameters = propertyProvider
                .getProperty(PropertyProvider.PROPERTY_INCLUDE_LOG_MESSAGE_PARAMETERS);
        if (includeLogMessageParameters != null && !includeLogMessageParameters.trim().equals("")) {
            setIncludeLogMessageParameters("true".equalsIgnoreCase(includeLogMessageParameters));
        }

        setupStaticFields(propertyProvider);
        setupAdditionalFieldTypes(propertyProvider);
        setupDynamicMdcFieldTypes(propertyProvider);
        facility = propertyProvider.getProperty(PropertyProvider.PROPERTY_FACILITY);
        String version = propertyProvider.getProperty(PropertyProvider.PROPERTY_VERSION);

        if (version != null && !"".equals(version)) {
            this.version = version;
        }

        String messageSize = propertyProvider.getProperty(PropertyProvider.PROPERTY_MAX_MESSAGE_SIZE);
        if (messageSize != null) {
            maximumMessageSize = Integer.parseInt(messageSize);
        }

        String timestampPattern = propertyProvider.getProperty(PropertyProvider.PROPERTY_TIMESTAMP_PATTERN);
        if (timestampPattern != null && !"".equals(timestampPattern)) {
            this.timestampPattern = timestampPattern;
        }
    }

    /**
     * Produce a {@link GelfMessage}.
     *
     * @param logEvent the log event
     * @return a new GelfMessage
     */
    public GelfMessage createGelfMessage(LogEvent logEvent) {

        GelfMessageBuilder builder = builders != null ? builders.get().recycle() : newInstance();

        Throwable throwable = logEvent.getThrowable();
        String message = logEvent.getMessage();

        if (GelfMessage.isEmpty(message) && throwable != null) {
            message = throwable.toString();
        }

        String shortMessage = message;
        if (message.length() > MAX_SHORT_MESSAGE_LENGTH) {
            shortMessage = message.substring(0, MAX_SHORT_MESSAGE_LENGTH - 1);
        }

        builder.withShortMessage(shortMessage).withFullMessage(message).withJavaTimestamp(logEvent.getLogTimestamp());
        builder.withLevel(logEvent.getSyslogLevel());
        builder.withVersion(getVersion());
        builder.withAdditionalFieldTypes(additionalFieldTypes);
        builder.withDynamicMdcFieldTypes(dynamicMdcFieldTypes);

        for (MessageField field : fields) {

            if (!isIncludeLocation() && field instanceof LogMessageField) {

                LogMessageField messageField = (LogMessageField) field;
                if (SOURCE_FIELDS.contains(messageField.getNamedLogField())) {
                    continue;
                }
            }

            Values values = getValues(logEvent, field);
            if (values == null || !values.hasValues()) {
                continue;
            }

            for (String entryName : values.getEntryNames()) {
                String value = values.getValue(entryName);
                if (value == null) {
                    continue;

                }
                builder.withField(entryName, value);
            }
        }

        if (stackTraceExtraction.isEnabled() && throwable != null) {
            addStackTrace(throwable, builder);
        }

        if (includeLogMessageParameters && logEvent.getParameters() != null) {
            for (int i = 0; i < logEvent.getParameters().length; i++) {
                Object param = logEvent.getParameters()[i];
                builder.withField(FIELD_MESSAGE_PARAM + i, "" + param);
            }
        }

        builder.withHost(getOriginHost());

        if (null != facility) {
            builder.withFacility(facility);
        }

        builder.withMaximumMessageSize(maximumMessageSize);
        return builder.build();
    }

    private Values getValues(LogEvent logEvent, MessageField field) {

        if (field instanceof StaticMessageField) {
            return new Values(field.getName(), getValue((StaticMessageField) field));
        }

        if (field instanceof LogMessageField) {
            LogMessageField logMessageField = (LogMessageField) field;
            if (logMessageField.getNamedLogField() == NamedLogField.Time) {
                SimpleDateFormat dateFormat = new SimpleDateFormat(timestampPattern);
                return new Values(field.getName(), dateFormat.format(new Date(logEvent.getLogTimestamp())));
            }

            if (logMessageField.getNamedLogField() == NamedLogField.Server) {
                return new Values(field.getName(), getOriginHost());
            }
        }

        return logEvent.getValues(field);
    }

    private String getValue(StaticMessageField field) {
        return field.getValue();
    }

    private void addStackTrace(Throwable thrown, GelfMessageBuilder builder) {
        if (stackTraceExtraction.isFilter()) {
            builder.withField(FIELD_STACK_TRACE, StackTraceFilter.getFilteredStackTrace(thrown, stackTraceExtraction.getRef()));
        } else {
            final StringWriter sw = new StringWriter();
            StackTraceFilter.getThrowable(thrown, stackTraceExtraction.getRef()).printStackTrace(new PrintWriter(sw));
            builder.withField(FIELD_STACK_TRACE, sw.toString());
        }
    }

    private void setupStaticFields(PropertyProvider propertyProvider) {
        int fieldNumber = 0;
        while (true) {
            final String property = propertyProvider.getProperty(PropertyProvider.PROPERTY_ADDITIONAL_FIELD + fieldNumber);
            if (null == property) {
                break;
            }
            final int index = property.indexOf('=');
            if (-1 != index) {

                StaticMessageField field = new StaticMessageField(property.substring(0, index), property.substring(index + 1));
                addField(field);
            }

            fieldNumber++;
        }
    }

    private void setupAdditionalFieldTypes(PropertyProvider propertyProvider) {
        int fieldNumber = 0;
        while (true) {
            final String property = propertyProvider.getProperty(PropertyProvider.PROPERTY_ADDITIONAL_FIELD_TYPE + fieldNumber);
            if (null == property) {
                break;
            }
            final int index = property.indexOf('=');
            if (-1 != index) {

                String field = property.substring(0, index);
                String type = property.substring(index + 1);
                setAdditionalFieldType(field, type);
            }

            fieldNumber++;
        }
    }

    private void setupDynamicMdcFieldTypes(PropertyProvider propertyProvider) {
        int fieldNumber = 0;
        while (true) {
            String property = propertyProvider.getProperty(PropertyProvider.PROPERTY_DYNAMIC_MDC_FIELD_TYPES + fieldNumber);
            if (null == property) {
                break;
            }
            int index = property.indexOf('=');
            if (-1 != index) {

                String field = property.substring(0, index);
                String type = property.substring(index + 1);
                setDynamicMdcFieldType(field, type);
            }

            fieldNumber++;
        }
    }

    public void setAdditionalFieldType(String field, String type) {
        additionalFieldTypes.put(field, type);
    }

    public void setDynamicMdcFieldType(String fieldPattern, String type) {
        Pattern pattern = Pattern.compile(fieldPattern);
        setDynamicMdcFieldType(pattern, type);
    }

    public void setDynamicMdcFieldType(Pattern fieldPattern, String type) {
        dynamicMdcFieldTypes.put(fieldPattern, type);
    }
    public void addField(MessageField field) {
        if (!fields.contains(field)) {
            this.fields.add(field);
        }
    }

    public void addFields(Collection fields) {
        this.fields.addAll(fields);
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getOriginHost() {
        if (null == originHost) {
            originHost = RuntimeContainer.FQDN_HOSTNAME;
        }
        return originHost;
    }

    public void setOriginHost(String originHost) {
        this.originHost = originHost;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        if (port > MAX_PORT_NUMBER || port < 1) {
            throw new IllegalArgumentException("Invalid port number: " + port + ", supported range: 1-" + MAX_PORT_NUMBER);
        }
        this.port = port;
    }

    public String getFacility() {
        return facility;
    }

    public void setFacility(String facility) {
        this.facility = facility;
    }

    public boolean isExtractStackTrace() {
        return stackTraceExtraction.isEnabled();
    }

    public String getExtractStackTrace() {

        if (stackTraceExtraction.isEnabled()) {

            if (stackTraceExtraction.getRef() == 0) {
                return "true";
            }
            return Integer.toString(stackTraceExtraction.getRef());
        }

        return "false";
    }

    public void setExtractStackTrace(boolean extractStackTrace) {
        this.stackTraceExtraction = stackTraceExtraction.applyExtaction("" + extractStackTrace);
    }

    public void setExtractStackTrace(String value) {
        this.stackTraceExtraction = stackTraceExtraction.applyExtaction(value);
    }

    public boolean isFilterStackTrace() {
        return stackTraceExtraction.isEnabled();
    }

    public void setFilterStackTrace(boolean filterStackTrace) {
        this.stackTraceExtraction = stackTraceExtraction.applyFilter(filterStackTrace);
    }

    public boolean isIncludeLogMessageParameters() {
        return includeLogMessageParameters;
    }

    public void setIncludeLogMessageParameters(boolean includeLogMessageParameters) {
        this.includeLogMessageParameters = includeLogMessageParameters;
    }

    public boolean isIncludeLocation() {
        return includeLocation;
    }

    public void setIncludeLocation(boolean includeLocation) {
        this.includeLocation = includeLocation;
    }

    public String getTimestampPattern() {
        return timestampPattern;
    }

    public void setTimestampPattern(String timestampPattern) {
        this.timestampPattern = timestampPattern;
    }

    public int getMaximumMessageSize() {
        return maximumMessageSize;
    }

    public void setMaximumMessageSize(int maximumMessageSize) {

        if (maximumMessageSize > MAX_MESSAGE_SIZE || maximumMessageSize < 1) {
            throw new IllegalArgumentException(
                    "Invalid maximum message size: " + maximumMessageSize + ", supported range: 1-" + MAX_MESSAGE_SIZE);
        }

        this.maximumMessageSize = maximumMessageSize;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {

        if (!GelfMessage.GELF_VERSION_1_0.equals(version) && !GelfMessage.GELF_VERSION_1_1.equals(version)) {
            throw new IllegalArgumentException("Invalid GELF version: " + version + ", supported range: "
                    + GelfMessage.GELF_VERSION_1_0 + ", " + GelfMessage.GELF_VERSION_1_1);
        }

        this.version = version;
    }

    static class StackTraceExtraction {

        private static final StackTraceExtraction OFF = new StackTraceExtraction(false, false, 0);
        private static final StackTraceExtraction ON = new StackTraceExtraction(true, false, 0);
        private static final StackTraceExtraction FILTERED = new StackTraceExtraction(true, true, 0);
        private final boolean enabled;
        private final boolean filter;
        private final int ref;

        private StackTraceExtraction(boolean enabled, boolean filter, int ref) {
            this.enabled = enabled;
            this.filter = filter;
            this.ref = ref;
        }

        /**
         * Parse the stack trace filtering value.
         *
         * @param value
         * @return
         */
        public static StackTraceExtraction from(String value, boolean filter) {

            if (value == null) {
                return OFF;
            }

            boolean enabled = Boolean.parseBoolean(value);

            int ref = 0;
            if (!value.equalsIgnoreCase("false") && !value.trim().isEmpty()) {
                try {
                    ref = Integer.parseInt(value);
                    enabled = true;
                } catch (NumberFormatException e) {
                    ref = 0;
                }
            }

            return new StackTraceExtraction(enabled, filter, ref);
        }

        public StackTraceExtraction applyExtaction(String value) {

            StackTraceExtraction parsed = from(value, isFilter());
            return new StackTraceExtraction(parsed.isEnabled(), parsed.isFilter(), parsed.getRef());
        }

        public StackTraceExtraction applyFilter(boolean filterStackTrace) {
            return new StackTraceExtraction(isEnabled(), filterStackTrace, getRef());
        }

        public boolean isEnabled() {
            return enabled;
        }

        public boolean isFilter() {
            return filter;
        }

        public int getRef() {
            return ref;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy