Skip to content

Commit

Permalink
Manually include PR internetitem#65
Browse files Browse the repository at this point in the history
  • Loading branch information
cgoIT committed Jun 19, 2020
1 parent d17d541 commit fcf8f0b
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ In your `logback.xml`:
<includeMdc>false</includeMdc> <!-- optional (default false) -->
<maxMessageSize>100</maxMessageSize> <!-- optional (default -1 -->
<authentication class="com.internetitem.logback.elasticsearch.config.BasicAuthentication" /> <!-- optional -->
<enableContextMap>false</enableContextMap><!-- optional (default false) -->
<properties>
<property>
<name>host</name>
Expand Down Expand Up @@ -110,6 +111,7 @@ Configuration Reference
* `includeMdc` (optional, default false): If set to `true`, then all [MDC](http://www.slf4j.org/api/org/slf4j/MDC.html) values will be mapped to properties on the JSON payload.
* `maxMessageSize` (optional, default -1): If set to a number greater than 0, truncate messages larger than this length, then append "`..`" to denote that the message was truncated
* `authentication` (optional): Add the ability to send authentication headers (see below)
* `enableContextMap` (optional): If the latest parameter in logger call is of type java.util.Map then all content of it will be traversed and written with prefix `context.*`. For event-specific custom fields.

The fields `@timestamp` and `message` are always sent and can not currently be configured. Additional fields can be sent by adding `<property>` elements to the `<properties>` set.

Expand Down Expand Up @@ -154,3 +156,17 @@ Included is also an Elasticsearch appender for Logback Access. The configuration

* The Appender class name is `com.internetitem.logback.elasticsearch.ElasticsearchAccessAppender`
* The `value` for each `property` uses the [Logback Access conversion words](http://logback.qos.ch/manual/layouts.html#logback-access).

Event-specific custom fields
============================
Log line:

log.info("Service started in {} seconds", duration/1000, Collections.singletonMap("duration", duration));

Result:

{
"@timestamp": "2014-06-04T15:26:14.464+02:00",
"message": "Service started in 12 seconds",
"duration": 12368,
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,8 @@ public void setAuthentication(Authentication auth) {
public void setMaxMessageSize(int maxMessageSize) {
settings.setMaxMessageSize(maxMessageSize);
}

public void setEnableContextMap(boolean enableContextMap) {
settings.setEnableContextMap(enableContextMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
import com.internetitem.logback.elasticsearch.config.Settings;
import com.internetitem.logback.elasticsearch.util.AbstractPropertyAndEncoder;
import com.internetitem.logback.elasticsearch.util.ClassicPropertyAndEncoder;
import com.internetitem.logback.elasticsearch.util.ContextMapWriter;
import com.internetitem.logback.elasticsearch.util.ErrorReporter;

import java.io.IOException;
import java.util.Map;

public class ClassicElasticsearchPublisher extends AbstractElasticsearchPublisher<ILoggingEvent> {

protected ContextMapWriter contextMapWriter;

public ClassicElasticsearchPublisher(Context context, ErrorReporter errorReporter, Settings settings, ElasticsearchProperties properties, HttpRequestHeaders headers) throws IOException {
super(context, errorReporter, settings, properties, headers);
contextMapWriter = new ContextMapWriter();
}

@Override
Expand Down Expand Up @@ -49,5 +53,9 @@ protected void serializeCommonFields(JsonGenerator gen, ILoggingEvent event) thr
gen.writeObjectField(entry.getKey(), entry.getValue());
}
}

if (settings.isEnableContextMap()) {
contextMapWriter.writeContextMap(gen, event);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class Settings {
private int maxQueueSize = 100 * 1024 * 1024;
private Authentication authentication;
private int maxMessageSize = -1;
private boolean enableContextMap;

public String getIndex() {
return index;
Expand Down Expand Up @@ -162,4 +163,12 @@ public int getMaxMessageSize() {
public void setMaxMessageSize(int maxMessageSize) {
this.maxMessageSize = maxMessageSize;
}

public boolean isEnableContextMap() {
return enableContextMap;
}

public void setEnableContextMap(boolean enableContextMap) {
this.enableContextMap = enableContextMap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.internetitem.logback.elasticsearch.util;

import ch.qos.logback.classic.spi.ILoggingEvent;
import com.fasterxml.jackson.core.JsonGenerator;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

public class ContextMapWriter {

public void writeContextMap(JsonGenerator gen, ILoggingEvent event) throws IOException {
Object[] arguments = event.getArgumentArray();
if (arguments == null || arguments.length == 0) {
return;
}
Object lastElement = arguments[arguments.length - 1];
if (lastElement instanceof Map) {
Map<String, Object> indexes = traverseMap(new HashMap<>(), "context", (Map<Object, Object>)lastElement);
for (Map.Entry<String, Object> entry : indexes.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
gen.writeObjectField(key, value);
}
}
}

static void traverseObject(Map<String, Object> accumulator, String context, Object object) {
if (object == null) {
return;
}
if (object instanceof Map) {
traverseMap(accumulator, context, (Map<Object, Object>) object);
} else if (object instanceof Collection) {
traverseCollection(accumulator, context, (Collection) object);
} else if (object instanceof Number) {
accumulator.put(context, object);
} else {
accumulator.put(context, Objects.toString(object));
}
}

static void traverseCollection(Map<String, Object> accumulator, String context, Collection<Object> object) {
Iterator<Object> iterator = object.iterator();
int i = 0;
while (iterator.hasNext()) {
Object v = iterator.next();
traverseObject(accumulator, context + "." + i, v);
i++;
}
}

static Map<String, Object> traverseMap(Map<String, Object> accumulator, String context, Map<Object, Object> object) {
for (Map.Entry<Object, Object> entry : object.entrySet()) {
traverseObject(accumulator, context + "." + entry.getKey(), entry.getValue());
}
return accumulator;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ public void should_delegate_setters_to_settings() throws MalformedURLException {
int aSleepTime = 10000;
int readTimeout = 10000;
int connectTimeout = 5000;
boolean enableContextMap = true;

appender.setIncludeCallerData(includeCallerData);
appender.setSleepTime(aSleepTime);
Expand All @@ -205,6 +206,7 @@ public void should_delegate_setters_to_settings() throws MalformedURLException {
appender.setConnectTimeout(connectTimeout);
appender.setRawJsonMessage(rawJsonMessage);
appender.setIncludeMdc(includeMdc);
appender.setEnableContextMap(enableContextMap);

verify(settings, times(1)).setReadTimeout(readTimeout);
verify(settings, times(1)).setSleepTime(aSleepTime);
Expand All @@ -221,6 +223,7 @@ public void should_delegate_setters_to_settings() throws MalformedURLException {
verify(settings, times(1)).setConnectTimeout(connectTimeout);
verify(settings, times(1)).setRawJsonMessage(rawJsonMessage);
verify(settings, times(1)).setIncludeMdc(includeMdc);
verify(settings, times(1)).setEnableContextMap(enableContextMap);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.internetitem.logback.elasticsearch.util;

import ch.qos.logback.classic.spi.LoggingEvent;
import com.fasterxml.jackson.core.JsonGenerator;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;

import java.io.IOException;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;

@RunWith(MockitoJUnitRunner.class)
public class ContextMapWriterTest {

@Mock
private JsonGenerator jsonGenerator;


private ContextMapWriter contextMapWriter;

@Before
public void setup() throws IOException {
contextMapWriter = new ContextMapWriter();
}

@Test
public void should_write_if_last_element_is_map() throws IOException {
LoggingEvent event = new LoggingEvent();
event.setArgumentArray(new Object[] {"123", ImmutableMap.of("test", 123, "test2", "foo")});
contextMapWriter.writeContextMap(jsonGenerator, event);
verify(jsonGenerator, times(1)).writeObjectField("context.test", 123);
verify(jsonGenerator, times(1)).writeObjectField("context.test2", "foo");
}

@Test
public void should_not_write_if_arguments_null_or_empty() throws IOException {
LoggingEvent event = new LoggingEvent();
contextMapWriter.writeContextMap(jsonGenerator, event);
event.setArgumentArray(new Object[]{});
contextMapWriter.writeContextMap(jsonGenerator, event);
verifyZeroInteractions(jsonGenerator);
}

@Test
public void should_not_write_if_last_element_not_map() throws IOException {
LoggingEvent event = new LoggingEvent();
event.setArgumentArray(new Object[]{"23", 3243});
contextMapWriter.writeContextMap(jsonGenerator, event);
verifyZeroInteractions(jsonGenerator);
}
}

0 comments on commit fcf8f0b

Please sign in to comment.