Skip to content
This repository was archived by the owner on May 31, 2021. It is now read-only.

Commit 7ba0316

Browse files
committed
Merge pull request #31 from pktxu/master
add support for field type conversion #30
2 parents 70c017e + 2dc099d commit 7ba0316

File tree

8 files changed

+214
-19
lines changed

8 files changed

+214
-19
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Add the following to your logback.xml configuration file.
4444
<shortMessagePattern>%.-100(%m%rEx)</shortMessagePattern>
4545
<additionalField>ipAddress:_ip_address</additionalField>
4646
<additionalField>requestId:_request_id</additionalField>
47+
<fieldType>_request_id:long</fieldType>
4748
<staticAdditionalField>_node_name:www013</staticAdditionalField>
4849
<includeFullMDC>true</includeFullMDC>
4950
</appender>
@@ -80,6 +81,7 @@ marker.toString() will be added to the gelf message as the field "_marker". Def
8081
[PatternLayout](http://logback.qos.ch/manual/layouts.html#conversionWord). Defaults to none which means the message will
8182
be truncated to create the short message
8283
* **additionalFields**: See additional fields below. Defaults to empty
84+
* **fieldType**: See field type conversion below. Defaults to empty (fields sent as string)
8385
* **staticAdditionalFields**: See static additional fields below. Defaults to empty
8486
* **includeFullMDC**: See additional fields below. Defaults to false
8587

@@ -136,10 +138,28 @@ E.g in the appender configuration:
136138
</appender>
137139
...
138140

141+
Field type conversion
142+
-----------------
143+
144+
You can configure a specific field to be converted to a numeric type. Key is the additional field key (and should thus
145+
begin with an underscore), value is the type to convert to. Currently supported types are ``int``, ``long``, ``float``
146+
and ``double``.
147+
148+
<appender name="GELF" class="me.moocar.logbackgelf.GelfAppender">
149+
...
150+
<fieldType>_request_id:long</fieldType>
151+
...
152+
</appender>
153+
...
154+
155+
logback-gelf will leave the field value alone (i.e.: send it as String) and print the stacktrace if the conversion fails.
156+
157+
139158
Change Log
140159
--------------------------------------
141160

142161
* Development version 0.11-SNAPSHOT (current Git `master`)
162+
* Added field type conversion [#30](../../issues/30)
143163
* Release [0.10p2] on 2014-Jan-12
144164
* Added hostName property [#28](../../issues/28)
145165
* Reverted Windows timeout [#29](../../issues/29)

src/main/java/me/moocar/logbackgelf/GelfAppender.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class GelfAppender extends AppenderBase<ILoggingEvent> {
3232
private String shortMessagePattern = null;
3333
private Map<String, String> additionalFields = new HashMap<String, String>();
3434
private Map<String, String> staticAdditionalFields = new HashMap<String, String>();
35+
private Map<String, String> fieldTypes = new HashMap<String, String>();
3536
private boolean includeFullMDC;
3637
private String hostName;
3738

@@ -103,7 +104,7 @@ private void initExecutor() {
103104
new ChunkFactory(chunkedGelfId, padSeq));
104105

105106
GelfConverter converter = new GelfConverter(facility, useLoggerName, useThreadName, useMarker, additionalFields,
106-
staticAdditionalFields, shortMessageLength, hostName, messagePattern, shortMessagePattern,
107+
fieldTypes, staticAdditionalFields, shortMessageLength, hostName, messagePattern, shortMessagePattern,
107108
includeFullMDC);
108109

109110
appenderExecutor = new AppenderExecutor(transport, payloadChunker, converter, new Zipper(), chunkThreshold);
@@ -286,6 +287,30 @@ public void addStaticAdditionalField(String keyValue) {
286287
staticAdditionalFields.put(splitted[0], splitted[1]);
287288
}
288289

290+
public void addFieldType(String keyValue) {
291+
String[] splitted = keyValue.split(":");
292+
293+
if (splitted.length != 2 ||
294+
!GelfConverter.primitiveTypes.containsKey(splitted[1])) {
295+
throw new IllegalArgumentException(
296+
"fieldType must be of the format key:value, where key is the " +
297+
"field key, and value is the type to convert to (one of " +
298+
GelfConverter.primitiveTypes.keySet() +
299+
")");
300+
}
301+
302+
fieldTypes.put(splitted[0], splitted[1]);
303+
304+
}
305+
306+
public Map<String, String> getFieldTypes() {
307+
return fieldTypes;
308+
}
309+
310+
public void setFieldTypes(final Map<String, String> fieldTypes) {
311+
this.fieldTypes = fieldTypes;
312+
}
313+
289314
/**
290315
* The length of the message to truncate to
291316
*/

src/main/java/me/moocar/logbackgelf/GelfConverter.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package me.moocar.logbackgelf;
22

3+
import java.lang.reflect.InvocationTargetException;
4+
import java.lang.reflect.Method;
35
import java.util.HashMap;
46
import java.util.Map;
57
import java.util.Map.Entry;
@@ -26,6 +28,7 @@ public class GelfConverter {
2628
private final boolean useThreadName;
2729
private final boolean useMarker;
2830
private final Map<String, String> additionalFields;
31+
private final Map<String, String> fieldTypes;
2932
private final Map<String, String> staticAdditionalFields;
3033
private final int shortMessageLength;
3134
private final String hostname;
@@ -34,11 +37,30 @@ public class GelfConverter {
3437
private final PatternLayout shortPatternLayout;
3538
private boolean includeFullMDC;
3639

40+
static Map<String, Method> primitiveTypes;
41+
42+
static {
43+
primitiveTypes = new HashMap<String, Method>();
44+
try {
45+
primitiveTypes.put("int", Integer.class.getDeclaredMethod("parseInt", String.class));
46+
primitiveTypes.put("Integer", Integer.class.getDeclaredMethod("parseInt", String.class));
47+
primitiveTypes.put("long", Long.class.getDeclaredMethod("parseLong", String.class));
48+
primitiveTypes.put("Long", Long.class.getDeclaredMethod("parseLong", String.class));
49+
primitiveTypes.put("float", Float.class.getDeclaredMethod("parseFloat", String.class));
50+
primitiveTypes.put("Float", Float.class.getDeclaredMethod("parseFloat", String.class));
51+
primitiveTypes.put("double", Double.class.getDeclaredMethod("parseDouble", String.class));
52+
primitiveTypes.put("Double", Double.class.getDeclaredMethod("parseDouble", String.class));
53+
} catch (NoSuchMethodException e) {
54+
e.printStackTrace();
55+
}
56+
}
57+
3758
public GelfConverter(String facility,
3859
boolean useLoggerName,
3960
boolean useThreadName,
4061
boolean useMarker,
4162
Map<String, String> additionalFields,
63+
Map<String, String> fieldTypes,
4264
Map<String, String> staticAdditionalFields,
4365
int shortMessageLength,
4466
String hostname,
@@ -51,6 +73,7 @@ public GelfConverter(String facility,
5173
this.useMarker = useMarker;
5274
this.useThreadName = useThreadName;
5375
this.additionalFields = additionalFields;
76+
this.fieldTypes = fieldTypes;
5477
this.staticAdditionalFields = staticAdditionalFields;
5578
this.shortMessageLength = shortMessageLength;
5679
this.hostname = hostname;
@@ -146,7 +169,7 @@ private void stackTraceField(Map<String, Object> map, ILoggingEvent eventObject)
146169
* @param map The map of additional fields
147170
* @param eventObject The Logging event that we are converting to GELF
148171
*/
149-
private void additionalFields(Map<String, Object> map, ILoggingEvent eventObject) {
172+
/* allow testing */ void additionalFields(Map<String, Object> map, ILoggingEvent eventObject) {
150173

151174
if (useLoggerName) {
152175
map.put("_loggerName", eventObject.getLoggerName());
@@ -167,22 +190,34 @@ private void additionalFields(Map<String, Object> map, ILoggingEvent eventObject
167190
if (includeFullMDC) {
168191
for (Entry<String, String> e : mdc.entrySet()) {
169192
if (additionalFields.containsKey(e.getKey())) {
170-
map.put(additionalFields.get(e.getKey()), e.getValue());
193+
map.put(additionalFields.get(e.getKey()), convertFieldType(e.getValue(), additionalFields.get(e.getKey())));
171194
} else {
172-
map.put("_" + e.getKey(), e.getValue());
195+
map.put("_" + e.getKey(), convertFieldType(e.getValue(), "_" + e.getKey()));
173196
}
174197
}
175198
} else {
176199
for (String key : additionalFields.keySet()) {
177200
String field = mdc.get(key);
178201
if (field != null) {
179-
map.put(additionalFields.get(key), field);
202+
map.put(additionalFields.get(key), convertFieldType(field, key));
180203
}
181204
}
182205
}
183206
}
184207
}
185208

209+
private Object convertFieldType(Object value, final String type) {
210+
if (primitiveTypes.containsKey(fieldTypes.get(type))) {
211+
try {
212+
value = primitiveTypes.get(fieldTypes.get(type)).invoke(null,
213+
value);
214+
} catch (Exception e1) {
215+
e1.printStackTrace();
216+
}
217+
}
218+
return value;
219+
}
220+
186221
private boolean eventHasMarker(ILoggingEvent eventObject) {
187222
return eventObject.getMarker() != null;
188223
}

src/main/java/me/moocar/logbackgelf/GelfLayout.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class GelfLayout extends LayoutBase<ILoggingEvent> {
2020
private String messagePattern = "%m%rEx";
2121
private String shortMessagePattern = null;
2222
private Map<String, String> additionalFields = new HashMap<String, String>();
23+
private Map<String, String> fieldTypes = new HashMap<String, String>();
2324
private Map<String, String> staticAdditionalFields = new HashMap<String, String>();
2425
private boolean includeFullMDC;
2526

@@ -161,7 +162,7 @@ private void createConverter() {
161162
hostName = getLocalHostName();
162163
}
163164

164-
this.converter = new GelfConverter(facility, useLoggerName, useThreadName, useMarker, additionalFields, staticAdditionalFields, shortMessageLength, hostName, messagePattern, shortMessagePattern, includeFullMDC);
165+
this.converter = new GelfConverter(facility, useLoggerName, useThreadName, useMarker, additionalFields, fieldTypes, staticAdditionalFields, shortMessageLength, hostName, messagePattern, shortMessagePattern, includeFullMDC);
165166
} catch (Exception e) {
166167
throw new RuntimeException("Unable to initialize converter", e);
167168
}

src/test/java/me/moocar/logbackgelf/GelfConverterTest.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@
55
import ch.qos.logback.classic.LoggerContext;
66
import ch.qos.logback.classic.spi.ILoggingEvent;
77
import ch.qos.logback.classic.spi.LoggingEvent;
8+
import com.google.common.collect.ImmutableMap;
89
import org.junit.Test;
10+
import org.slf4j.MDC;
911
import org.slf4j.Marker;
1012
import org.slf4j.MarkerFactory;
1113

1214
import java.util.HashMap;
15+
import java.util.Map;
1316

1417
import static org.hamcrest.CoreMatchers.equalTo;
1518
import static org.hamcrest.CoreMatchers.is;
19+
import static org.junit.Assert.assertEquals;
1620
import static org.junit.Assert.assertThat;
1721

1822

@@ -44,6 +48,67 @@ public void testForNoMarkerWhenUseMarkerIsTrueButNoMarkerIsSet() {
4448
assertThat(gelfString.contains(markerString), is(equalTo(false)));
4549
}
4650

51+
@Test
52+
public void testNumericConversion() throws NoSuchMethodException {
53+
Map<String, String> fieldTypes =
54+
ImmutableMap.<String, String>builder()
55+
.put("_intField", "int")
56+
.put("_IntegerField", "Integer")
57+
.put("_longField", "long")
58+
.put("_LongField", "Long")
59+
.put("_floatField", "float")
60+
.put("_FloatField", "Float")
61+
.put("_doubleField", "double")
62+
.put("_DoubleField", "Double")
63+
.put("_foo", "foo")
64+
.build();
65+
66+
MDC.put("intField", "123");
67+
MDC.put("IntegerField", "456");
68+
MDC.put("longField", Long.toString((long) Integer.MAX_VALUE + 1));
69+
MDC.put("LongField", Long.toString((long) Integer.MAX_VALUE + 2));
70+
MDC.put("floatField", "3.14159");
71+
MDC.put("FloatField", "2.71828");
72+
MDC.put("doubleField", "3E-50");
73+
MDC.put("DoubleField", "3E50");
74+
MDC.put("foo", "bar");
75+
76+
LoggingEvent event = new LoggingEvent("my.class.name", logger, Level.INFO, "hello", null, null);
77+
78+
GelfConverter converter;
79+
Map<String, Object> additionalFields;
80+
81+
// test including full MDC
82+
converter = new GelfConverter("facilty", false, false, false,
83+
new HashMap<String, String>(), fieldTypes,
84+
new HashMap<String, String>(), 10, "host", "%m%rEx", null,
85+
true);
86+
87+
additionalFields = new HashMap<String, Object>();
88+
converter.additionalFields(additionalFields, event);
89+
validateFieldTypes(additionalFields);
90+
91+
// test without including full MDC
92+
converter = new GelfConverter("facilty", false, false, false,
93+
new HashMap<String, String>(), fieldTypes,
94+
new HashMap<String, String>(), 10, "host", "%m%rEx", null,
95+
false);
96+
converter.additionalFields(additionalFields, event);
97+
validateFieldTypes(additionalFields);
98+
}
99+
100+
private void validateFieldTypes(Map<String, Object> fields) {
101+
assertEquals(123, ((Integer) fields.get("_intField")).intValue());
102+
assertEquals(456, ((Integer) fields.get("_IntegerField")).intValue());
103+
assertEquals((long) Integer.MAX_VALUE + 1, ((Long) fields.get("_longField")).longValue());
104+
assertEquals((long) Integer.MAX_VALUE + 2, ((Long) fields.get("_LongField")).longValue());
105+
assertEquals((float) 3.14159, ((Float) fields.get("_floatField")).floatValue(), 0);
106+
assertEquals((float) 2.71828, ((Float) fields.get("_FloatField")).floatValue(), 0);
107+
assertEquals((double) 3e-50, ((Double) fields.get("_doubleField")).doubleValue(), 0);
108+
assertEquals((double) 3e50, ((Double) fields.get("_DoubleField")).doubleValue(), 0);
109+
assertEquals("bar", ((String) fields.get("_foo")));
110+
}
111+
47112
private void testForMarker(boolean useMarker) {
48113
ILoggingEvent event = createEvent(useMarker);
49114
GelfConverter converter = createGelfConverter(useMarker);
@@ -63,7 +128,7 @@ private LoggingEvent createEvent(boolean addMarker) {
63128
}
64129

65130
private GelfConverter createGelfConverter(boolean useMarker) {
66-
return new GelfConverter("facilty", false, false, useMarker, new HashMap<String, String>(), new HashMap<String, String>(), 10, "host", "%m%rEx", null, false);
131+
return new GelfConverter("facilty", false, false, useMarker, new HashMap<String, String>(), new HashMap<String, String>(), new HashMap<String, String>(), 10, "host", "%m%rEx", null, false);
67132
}
68133

69134
}

0 commit comments

Comments
 (0)