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

Event body serializer #86

Merged
merged 8 commits into from
Oct 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ LOGGER.info("hello world");
#### Message Format
An event message format could be configured for HTTP event appender in logging framework configuration. It could have one of the two possible values - text, json. It is an optional property with default value as 'text'. Message format 'json' is used where the event message could be in json format.

It is also possible to use a custom event body serializer for the HTTP event adapter, to format the logging event however you please. Simply create a class implementing `com.splunk.logging.EventBodySerializer`, and add the full class name as a property (`eventBodySerializer`) to the adapter. Default will be a JSON event body containing message, severity, and other properties.

For more information, see http://dev.splunk.com/view/SP-CAAAE2K.

# License
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/com/splunk/logging/EventBodySerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.splunk.logging;

import java.io.Serializable;
import java.util.Map;
import org.json.simple.JSONObject;

public interface EventBodySerializer {

String serializeEventBody(
HttpEventCollectorEventInfo eventInfo,
Object formattedMessage
);

class Default implements EventBodySerializer {

@Override
public String serializeEventBody(
final HttpEventCollectorEventInfo eventInfo,
final Object formattedMessage
) {
final JSONObject body = new JSONObject();
HttpEventCollectorSender.putIfPresent(body, "severity", eventInfo.getSeverity());
HttpEventCollectorSender.putIfPresent(body, "message", formattedMessage);
HttpEventCollectorSender.putIfPresent(body, "logger", eventInfo.getLoggerName());
HttpEventCollectorSender.putIfPresent(body, "thread", eventInfo.getThreadName());
// add an exception record if and only if there is one
// in practice, the message also has the exception information attached
if (eventInfo.getExceptionMessage() != null) {
HttpEventCollectorSender.putIfPresent(body, "exception", eventInfo.getExceptionMessage());
}

// add properties if and only if there are any
final Map<String, String> props = eventInfo.getProperties();
if (props != null && !props.isEmpty()) {
body.put("properties", props);
}
// add marker if and only if there is one
final Serializable marker = eventInfo.getMarker();
if (marker != null) {
HttpEventCollectorSender.putIfPresent(body, "marker", marker.toString());
}

return body.toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ private HttpEventCollectorLog4jAppender(final String name,
long retriesOnError,
String sendMode,
String middleware,
final String disableCertificateValidation)
final String disableCertificateValidation,
final String eventBodySerializer)
{
super(name, filter, layout, ignoreExceptions);
Dictionary<String, String> metadata = new Hashtable<String, String>();
Expand All @@ -84,7 +85,13 @@ private HttpEventCollectorLog4jAppender(final String name,
if (middleware != null && !middleware.isEmpty()) {
try {
this.sender.addMiddleware((HttpEventCollectorMiddleware.HttpSenderMiddleware)(Class.forName(middleware).newInstance()));
} catch (Exception e) {}
} catch (Exception ignored) {}
}

if (eventBodySerializer != null && !eventBodySerializer.isEmpty()) {
try {
this.sender.setEventBodySerializer((EventBodySerializer) Class.forName(eventBodySerializer).newInstance());
} catch (final Exception ignored) {}
}

// plug resend middleware
Expand Down Expand Up @@ -128,6 +135,7 @@ public static HttpEventCollectorLog4jAppender createAppender(
@PluginAttribute("send_mode") final String sendMode,
@PluginAttribute("middleware") final String middleware,
@PluginAttribute("disableCertificateValidation") final String disableCertificateValidation,
@PluginAttribute("eventBodySerializer") final String eventBodySerializer,
@PluginAttribute(value = "includeLoggerName", defaultBoolean = true) final boolean includeLoggerName,
@PluginAttribute(value = "includeThreadName", defaultBoolean = true) final boolean includeThreadName,
@PluginAttribute(value = "includeMDC", defaultBoolean = true) final boolean includeMDC,
Expand Down Expand Up @@ -174,7 +182,9 @@ public static HttpEventCollectorLog4jAppender createAppender(
parseInt(retriesOnError, 0),
sendMode,
middleware,
disableCertificateValidation);
disableCertificateValidation,
eventBodySerializer
);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class HttpEventCollectorLogbackAppender<E> extends AppenderBase<E> {
private String _type;
private String _disableCertificateValidation;
private String _middleware;
private String _eventBodySerializer;
private long _batchInterval = 0;
private long _batchCount = 0;
private long _batchSize = 0;
Expand Down Expand Up @@ -81,7 +82,13 @@ public void start() {
if (_middleware != null && !_middleware.isEmpty()) {
try {
this.sender.addMiddleware((HttpEventCollectorMiddleware.HttpSenderMiddleware)(Class.forName(_middleware).newInstance()));
} catch (Exception e) {}
} catch (Exception ignored) {}
}

if (_eventBodySerializer != null && !_eventBodySerializer.isEmpty()) {
try {
this.sender.setEventBodySerializer((EventBodySerializer) Class.forName(_eventBodySerializer).newInstance());
} catch (final Exception ignored) {}
}

// plug resend middleware
Expand Down Expand Up @@ -120,7 +127,7 @@ private void sendEvent(ILoggingEvent event) {
}

MarkerConverter c = new MarkerConverter();
if (event != null && started) {
if (this.started) {
this.sender.send(
event.getLevel().toString(),
_layout.doLayout((E) event),
Expand Down Expand Up @@ -257,6 +264,10 @@ public String getIndex() {
return this._index;
}

public String getEventBodySerializer() {
return _eventBodySerializer;
}

public void setDisableCertificateValidation(String disableCertificateValidation) {
this._disableCertificateValidation = disableCertificateValidation;
}
Expand Down Expand Up @@ -289,6 +300,10 @@ public String getDisableCertificateValidation() {
return _disableCertificateValidation;
}

public void setEventBodySerializer(String eventBodySerializer) {
this._eventBodySerializer = eventBodySerializer;
}

private static long parseLong(String string, int defaultValue) {
try {
return Long.parseLong(string);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ public HttpEventCollectorLoggingHandler() {
long retriesOnError = getConfigurationNumericProperty(RetriesOnErrorTag, 0);
String sendMode = getConfigurationProperty(SendModeTag, "sequential");
String middleware = getConfigurationProperty(MiddlewareTag, "");
String eventBodySerializer = getConfigurationProperty("eventBodySerializer", "");

includeLoggerName = getConfigurationBooleanProperty(IncludeLoggerNameConfTag, true);
includeThreadName = getConfigurationBooleanProperty(IncludeThreadNameConfTag, true);
Expand All @@ -162,7 +163,16 @@ public HttpEventCollectorLoggingHandler() {
if (middleware != null && !middleware.isEmpty()) {
try {
this.sender.addMiddleware((HttpEventCollectorMiddleware.HttpSenderMiddleware)(Class.forName(middleware).newInstance()));
} catch (Exception e) {}
} catch (Exception ignored) {}
}

if (eventBodySerializer != null && !eventBodySerializer.isEmpty()) {
try {
this.sender.setEventBodySerializer((EventBodySerializer) Class.forName(eventBodySerializer).newInstance());
} catch (final Exception ex) {
//output error msg but not fail, it will default to use the default EventBodySerializer
System.out.println(ex);
}
}

// plug retries middleware
Expand Down
42 changes: 14 additions & 28 deletions src/main/java/com/splunk/logging/HttpEventCollectorSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
/**
* This is an internal helper class that sends logging events to Splunk http event collector.
*/
final class HttpEventCollectorSender extends TimerTask implements HttpEventCollectorMiddleware.IHttpSender {
public class HttpEventCollectorSender extends TimerTask implements HttpEventCollectorMiddleware.IHttpSender {
public static final String MetadataTimeTag = "time";
public static final String MetadataHostTag = "host";
public static final String MetadataIndexTag = "index";
Expand Down Expand Up @@ -99,6 +99,7 @@ public enum SendMode
private SendMode sendMode = SendMode.Sequential;
private HttpEventCollectorMiddleware middleware = new HttpEventCollectorMiddleware();
private final MessageFormat messageFormat;
private EventBodySerializer eventBodySerializer;

/**
* Initialize HttpEventCollectorSender
Expand Down Expand Up @@ -135,12 +136,12 @@ public HttpEventCollectorSender(
this.maxEventsBatchCount = maxEventsBatchCount;
this.maxEventsBatchSize = maxEventsBatchSize;
this.metadata = metadata;

final String format = metadata.get(MetadataMessageFormatTag);
// Get MessageFormat enum from format string. Do this once per instance in constructor to avoid expensive operation in
// each event sender call
this.messageFormat = MessageFormat.fromFormat(format);

if (sendModeStr != null) {
if (sendModeStr.equals(SendModeSequential))
this.sendMode = SendMode.Sequential;
Expand Down Expand Up @@ -239,8 +240,12 @@ public void disableCertificateValidation() {
disableCertificateValidation = true;
}

public void setEventBodySerializer(EventBodySerializer eventBodySerializer) {
this.eventBodySerializer = eventBodySerializer;
}

@SuppressWarnings("unchecked")
private static void putIfPresent(JSONObject collection, String tag, Object value) {
public static void putIfPresent(JSONObject collection, String tag, Object value) {
if (value != null) {
if (value instanceof String && ((String) value).length() == 0) {
// Do not add blank string
Expand All @@ -263,34 +268,15 @@ private String serializeEventInfo(HttpEventCollectorEventInfo eventInfo) {
putIfPresent(event, MetadataIndexTag, metadata.get(MetadataIndexTag));
putIfPresent(event, MetadataSourceTag, metadata.get(MetadataSourceTag));
putIfPresent(event, MetadataSourceTypeTag, metadata.get(MetadataSourceTypeTag));

// Parse message on the basis of format
final Object parsedMessage = this.messageFormat.parse(eventInfo.getMessage());

// event body
JSONObject body = new JSONObject();
putIfPresent(body, "severity", eventInfo.getSeverity());
putIfPresent(body, "message", parsedMessage);
putIfPresent(body, "logger", eventInfo.getLoggerName());
putIfPresent(body, "thread", eventInfo.getThreadName());
// add an exception record if and only if there is one
// in practice, the message also has the exception information attached
if (eventInfo.getExceptionMessage() != null) {
putIfPresent(body, "exception", eventInfo.getExceptionMessage());
}

// add properties if and only if there are any
final Map<String,String> props = eventInfo.getProperties();
if (props != null && !props.isEmpty()) {
body.put("properties", props);
if (eventBodySerializer == null) {
eventBodySerializer = new EventBodySerializer.Default();
}
// add marker if and only if there is one
final Serializable marker = eventInfo.getMarker();
if (marker != null) {
putIfPresent(body, "marker", marker.toString());
}
// join event and body
event.put("event", body);

event.put("event", eventBodySerializer.serializeEventBody(eventInfo, parsedMessage));
return event.toString();
}

Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/splunk/logging/MessageFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ enum MessageFormat {
*
*
* @param message the message string
* @param format the message format
*
* @return parsed message object based on format
*/
Expand Down
5 changes: 4 additions & 1 deletion src/test/java/HttpEventCollectorUnitTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public void log4j_simple() throws Exception {
userInputs.put("user_middleware", "HttpEventCollectorUnitTestMiddleware");
userInputs.put("user_batch_size_count", "1");
userInputs.put("user_batch_size_bytes", "0");
userInputs.put("user_eventBodySerializer", "DoesNotExistButShouldNotCrashTest");
TestUtil.resetLog4j2Configuration("log4j2_template.xml", "log4j2.xml", userInputs);
org.apache.logging.log4j.Logger LOG4J = org.apache.logging.log4j.LogManager.getLogger(loggerName);

Expand Down Expand Up @@ -71,6 +72,7 @@ public void logback_simple() throws Exception {
userInputs.put("user_logger_name", loggerName);
userInputs.put("user_httpEventCollector_token", "11111111-2222-3333-4444-555555555555");
userInputs.put("user_middleware", "HttpEventCollectorUnitTestMiddleware");
userInputs.put("user_eventBodySerializer", "DoesNotExistButShouldNotCrashTest");
TestUtil.resetLogbackConfiguration("logback_template.xml", "logback.xml", userInputs);
org.slf4j.Logger LOGBACK = org.slf4j.LoggerFactory.getLogger(loggerName);

Expand Down Expand Up @@ -101,7 +103,8 @@ public void java_util_logger_simple() {
"com.splunk.logging.HttpEventCollectorLoggingHandler.batch_size_count=0\n" +
"com.splunk.logging.HttpEventCollectorLoggingHandler.batch_size_bytes=0\n" +
"com.splunk.logging.HttpEventCollectorLoggingHandler.batch_interval=0\n" +
"com.splunk.logging.HttpEventCollectorLoggingHandler.middleware=HttpEventCollectorUnitTestMiddleware\n"
"com.splunk.logging.HttpEventCollectorLoggingHandler.middleware=HttpEventCollectorUnitTestMiddleware\n" +
"com.splunk.logging.HttpEventCollectorLoggingHandler.eventBodySerializer=DoesNotExistButShouldNotCrashTest\n"
);

// send 3 events
Expand Down
Loading