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

biz.paluch.logging.gelf.log4j2.GelfLogAppender Maven / Gradle / Ivy

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

import static biz.paluch.logging.gelf.LogMessageField.NamedLogField.*;
import static org.apache.logging.log4j.core.layout.PatternLayout.newBuilder;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.*;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;

import biz.paluch.logging.RuntimeContainer;
import biz.paluch.logging.gelf.*;
import biz.paluch.logging.gelf.intern.*;

/**
 * Logging-Handler for GELF (Graylog Extended Logging Format). This Java-Util-Logging Handler creates GELF Messages and posts
 * them using UDP (default) or TCP. Following parameters are supported/needed:
 * 
    *
  • host (Mandatory): Hostname/IP-Address of the Logstash Host *
      *
    • (the host) for UDP, e.g. 127.0.0.1 or some.host.com
    • *
    • See docs for more details
    • *
    *
  • *
  • port (Optional): Port, default 12201
  • *
  • version (Optional): GELF Version 1.0 or 1.1, default 1.0
  • *
  • originHost (Optional): Originating Hostname, default FQDN Hostname
  • *
  • extractStackTrace (Optional): Post Stack-Trace to StackTrace field (true/false/throwable reference [0 = throwable, 1 = * throwable.cause, -1 = root cause]), default false
  • *
  • filterStackTrace (Optional): Perform Stack-Trace filtering (true/false), default false
  • *
  • mdcProfiling (Optional): Perform Profiling (Call-Duration) based on MDC Data. See MDC * Profiling, default false
  • *
  • facility (Optional): Name of the Facility, default gelf-java
  • *
  • additionalFieldTypes (Optional): Type specification for additional and MDC fields. Supported types: String, long, Long, * double, Double and discover (default if not specified, discover field type on parseability). Eg. field=String,field2=double
  • *
  • ignoreExceptions (Optional): The default is true, causing exceptions encountered while appending events to * be internally logged and then ignored. When set to false exceptions will be propagated to the caller, instead. * You must set this to false when wrapping this Appender in a FailoverAppender.
  • *
* *

Fields

* *

* Log4j v2 supports an extensive and flexible configuration in contrast to other log frameworks (JUL, log4j v1). This allows * you to specify your needed fields you want to use in the GELF message. An empty field configuration results in a message * containing only *

* *
    *
  • timestamp
  • *
  • level (syslog level)
  • *
  • host
  • *
  • facility
  • *
  • message
  • *
  • short_message
  • *
* *

* You can add different fields: *

* * * * In order to do so, use nested Field elements below the Appender element. * *

Static Literals

<Field name="fieldName1" literal="your literal value" /> * * *

MDC Fields

<Field name="fieldName1" mdc="name of the MDC entry" /> * * *

Dynamic MDC Fields

<DynamicMdcFields regex="mdc.*" /> * * * *

Log-Event fields

*

* See also: Pattern Layout *

* *

* You can use all built-in Pattern Fields: *

* <Field name="simpleClassName" pattern="%C{1}" /> <Field name="timestamp" pattern="%d{dd MMM yyyy HH:mm:ss,SSS}" /> <Field name="level" pattern="%level" /> * *

* Additionally, you can add the host-Field, which can supply you either the FQDN hostname, the simple hostname * or the local address. *

* * * * * * * * * *
OptionDescription
host   {["fqdn"
*   |"simple"
*   |"address"]}
*

* Outputs either the FQDN hostname, the simple hostname or the local address. *

*

* You can follow the throwable conversion word with an option in the form %host{option}. *

*

* %host{fqdn} default setting, outputs the FQDN hostname, e.g. www.you.host.name.com. *

*

* %host{simple} outputs simple hostname, e.g. www. *

*

* %host{address} outputs the local IP address of the found hostname, e.g. 1.2.3.4 or affe:affe:affe::1. *

*
* *

MDC Field Typing

In some cases, it's required to use a fixed type for fields transported using GELF. MDC is a * dynamic value source and since types can vary, so also data types in the GELF JSON vary. You can define * {@code DynamicMdcFieldType} rules to declare types with Regex {@link java.util.regex.Pattern}-based rules. *

* >DynamicMdcFieldType regex="business\..*\.field" type="double" /< * * * *

MDC Profiling

*

* MDC Profiling allows to calculate the runtime from request start up to the time until the log message was generated. You must * set one value in the MDC: *

    *
  • profiling.requestStart.millis: Time Millis of the Request-Start (Long or String)
  • *
*

* Two values are set by the Log Appender: *

*
    *
  • profiling.requestEnd: End-Time of the Request-End in Date.toString-representation
  • *
  • profiling.requestDuration: Duration of the request (e.g. 205ms, 16sec)
  • *
* * The {@link #append(LogEvent)} method is thread-safe and may be called by different threads at any time. * * @author Mark Paluch * @author Thomas Herzog */ @Plugin(name = "Gelf", category = "Core", elementType = "appender", printObject = true) public class GelfLogAppender extends AbstractAppender { private static final Logger LOGGER = StatusLogger.getLogger(); private static final ErrorReporter ERROR_REPORTER = new ErrorReporter() { @Override public void reportError(String message, Exception e) { LOGGER.error(message, e, 0); } }; private final ErrorReporter PROPAGATING_ERROR_REPORTER = new ErrorReporter() { @Override public void reportError(String message, Exception e) { if (e != null) { throw new AppenderLoggingException(e); } LOGGER.error(message, null, 0); } }; protected GelfSender gelfSender; private final MdcGelfMessageAssembler gelfMessageAssembler; private final ErrorReporter errorReporter; public GelfLogAppender(String name, Filter filter, MdcGelfMessageAssembler gelfMessageAssembler, boolean ignoreExceptions) { super(name, filter, null, ignoreExceptions); this.gelfMessageAssembler = gelfMessageAssembler; ErrorReporter errorReporter = getErrorReporter(ignoreExceptions); this.errorReporter = new MessagePostprocessingErrorReporter(errorReporter); } private ErrorReporter getErrorReporter(boolean ignoreExceptions) { return ignoreExceptions ? ERROR_REPORTER : PROPAGATING_ERROR_REPORTER; } @PluginFactory public static GelfLogAppender createAppender(@PluginConfiguration final Configuration config, @PluginAttribute("name") String name, @PluginElement("Filter") Filter filter, @PluginElement("Field") final GelfLogField[] fields, @PluginElement("DynamicMdcFields") final GelfDynamicMdcLogFields[] dynamicFieldArray, @PluginElement("DynamicMdcFieldTypes") final GelfDynamicMdcFieldType[] dynamicFieldTypeArray, @PluginAttribute("graylogHost") String graylogHost, @PluginAttribute("host") String host, @PluginAttribute("graylogPort") String graylogPort, @PluginAttribute("port") String port, @PluginAttribute("version") String version, @PluginAttribute("extractStackTrace") String extractStackTrace, @PluginAttribute("originHost") String originHost, @PluginAttribute("includeFullMdc") String includeFullMdc, @PluginAttribute("facility") String facility, @PluginAttribute("filterStackTrace") String filterStackTrace, @PluginAttribute("mdcProfiling") String mdcProfiling, @PluginAttribute("maximumMessageSize") String maximumMessageSize, @PluginAttribute("additionalFieldTypes") String additionalFieldTypes, @PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) boolean ignoreExceptions) { RuntimeContainer.initialize(ERROR_REPORTER); MdcGelfMessageAssembler mdcGelfMessageAssembler = new MdcGelfMessageAssembler(); if (name == null) { LOGGER.error("No name provided for {}", GelfLogAppender.class.getSimpleName()); return null; } if (Strings.isEmpty(host) && Strings.isEmpty(graylogHost)) { LOGGER.error("No host provided for {}", GelfLogAppender.class.getSimpleName()); return null; } if (Strings.isNotEmpty(host)) { mdcGelfMessageAssembler.setHost(host); } if (Strings.isNotEmpty(graylogHost)) { mdcGelfMessageAssembler.setHost(graylogHost); } if (Strings.isNotEmpty(port)) { mdcGelfMessageAssembler.setPort(Integer.parseInt(port)); } if (Strings.isNotEmpty(graylogPort)) { mdcGelfMessageAssembler.setPort(Integer.parseInt(graylogPort)); } if (Strings.isNotEmpty(version)) { mdcGelfMessageAssembler.setVersion(version); } if (Strings.isNotEmpty(originHost)) { PatternLayout patternLayout = newBuilder().withPattern(originHost).withConfiguration(config) .withNoConsoleNoAnsi(false).withAlwaysWriteExceptions(false).build(); mdcGelfMessageAssembler.setOriginHost(patternLayout.toSerializable(new Log4jLogEvent())); } if (facility != null) { mdcGelfMessageAssembler.setFacility(facility); } if (extractStackTrace != null) { mdcGelfMessageAssembler.setExtractStackTrace(extractStackTrace); } if (filterStackTrace != null) { mdcGelfMessageAssembler.setFilterStackTrace("true".equals(filterStackTrace)); } if (mdcProfiling != null) { mdcGelfMessageAssembler.setMdcProfiling("true".equals(mdcProfiling)); } if (includeFullMdc != null) { mdcGelfMessageAssembler.setIncludeFullMdc("true".equals(includeFullMdc)); } if (maximumMessageSize != null) { mdcGelfMessageAssembler.setMaximumMessageSize(Integer.parseInt(maximumMessageSize)); } if (additionalFieldTypes != null) { ConfigurationSupport.setAdditionalFieldTypes(additionalFieldTypes, mdcGelfMessageAssembler); } if (dynamicFieldTypeArray != null) { for (GelfDynamicMdcFieldType gelfDynamicMdcFieldType : dynamicFieldTypeArray) { mdcGelfMessageAssembler.setDynamicMdcFieldType(gelfDynamicMdcFieldType.getPattern(), gelfDynamicMdcFieldType.getType()); } } configureFields(mdcGelfMessageAssembler, fields, dynamicFieldArray); return new GelfLogAppender(name, filter, mdcGelfMessageAssembler, ignoreExceptions); } /** * Configure fields (literals, MDC, layout). * * @param mdcGelfMessageAssembler the assembler * @param fields static field array * @param dynamicFieldArray dynamic field array */ private static void configureFields(MdcGelfMessageAssembler mdcGelfMessageAssembler, GelfLogField[] fields, GelfDynamicMdcLogFields[] dynamicFieldArray) { if (fields == null || fields.length == 0) { mdcGelfMessageAssembler.addFields(LogMessageField.getDefaultMapping(Time, Severity, ThreadName, SourceClassName, SourceMethodName, SourceLineNumber, SourceSimpleClassName, LoggerName, Marker)); return; } for (GelfLogField field : fields) { if (Strings.isNotEmpty(field.getMdc())) { mdcGelfMessageAssembler.addField(new MdcMessageField(field.getName(), field.getMdc())); } if (Strings.isNotEmpty(field.getLiteral())) { mdcGelfMessageAssembler.addField(new StaticMessageField(field.getName(), field.getLiteral())); } if (field.getPatternLayout() != null) { mdcGelfMessageAssembler.addField(new PatternLogMessageField(field.getName(), null, field.getPatternLayout())); } } if (dynamicFieldArray != null) { for (GelfDynamicMdcLogFields gelfDynamicMdcLogFields : dynamicFieldArray) { mdcGelfMessageAssembler.addField(new DynamicMdcMessageField(gelfDynamicMdcLogFields.getRegex())); } } } @Override public void append(LogEvent event) { if (event == null) { return; } try { GelfMessage message = createGelfMessage(event); if (!message.isValid()) { reportError("GELF Message is invalid: " + message.toJson(), null); return; } if (null == gelfSender || !gelfSender.sendMessage(message)) { reportError("Could not send GELF message", null); } } catch (AppenderLoggingException e) { throw e; } catch (Exception e) { reportError("Could not send GELF message: " + e.getMessage(), e); } } protected GelfMessage createGelfMessage(final LogEvent logEvent) { return gelfMessageAssembler.createGelfMessage(new Log4j2LogEvent(logEvent)); } public void reportError(String message, Exception exception) { errorReporter.reportError(message, exception); } @Override protected boolean stop(long timeout, TimeUnit timeUnit, boolean changeLifeCycleState) { if (null != gelfSender) { Closer.close(gelfSender); gelfSender = null; } return super.stop(timeout, timeUnit, changeLifeCycleState); } @Override public void start() { if (null == gelfSender) { gelfSender = createGelfSender(); } super.start(); } protected GelfSender createGelfSender() { return GelfSenderFactory.createSender(gelfMessageAssembler, errorReporter, Collections.emptyMap()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy