Skip to content

Commit ab616f1

Browse files
author
pktxu
committed
add support for field type conversion Moocar#30
1 parent 70c017e commit ab616f1

File tree

8 files changed

+210
-19
lines changed

8 files changed

+210
-19
lines changed

README.md

Lines changed: 19 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
8385
* **staticAdditionalFields**: See static additional fields below. Defaults to empty
8486
* **includeFullMDC**: See additional fields below. Defaults to false
8587

@@ -136,10 +138,27 @@ 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. Currently supported types are ``int``, ``long``,
145+
``float`` and ``double``.
146+
147+
<appender name="GELF" class="me.moocar.logbackgelf.GelfAppender">
148+
...
149+
<fieldType>_request_id:long</fieldType>
150+
...
151+
</appender>
152+
...
153+
154+
logback-gelf will leave the field value alone (i.e.: send it as String) and print the stacktrace if the conversion fails.
155+
156+
139157
Change Log
140158
--------------------------------------
141159

142160
* Development version 0.11-SNAPSHOT (current Git `master`)
161+
* Added field type conversion [#30](../../issues/30)
143162
* Release [0.10p2] on 2014-Jan-12
144163
* Added hostName property [#28](../../issues/28)
145164
* 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: 63 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,64 @@ 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").put("_LongField", "Long")
58+
.put("_floatField", "float").put("_FloatField", "Float")
59+
.put("_doubleField", "double")
60+
.put("_DoubleField", "Double").put("_foo", "foo")
61+
.build();
62+
63+
MDC.put("intField", "123");
64+
MDC.put("IntegerField", "456");
65+
MDC.put("longField", Long.toString((long) Integer.MAX_VALUE + 1));
66+
MDC.put("LongField", Long.toString((long) Integer.MAX_VALUE + 2));
67+
MDC.put("floatField", "3.14159");
68+
MDC.put("FloatField", "2.71828");
69+
MDC.put("doubleField", "3E-50");
70+
MDC.put("DoubleField", "3E50");
71+
MDC.put("foo", "bar");
72+
73+
LoggingEvent event = new LoggingEvent("my.class.name", logger, Level.INFO, "hello", null, null);
74+
75+
GelfConverter converter;
76+
Map<String, Object> additionalFields;
77+
78+
// test including full MDC
79+
converter = new GelfConverter("facilty", false, false, false,
80+
new HashMap<String, String>(), fieldTypes,
81+
new HashMap<String, String>(), 10, "host", "%m%rEx", null,
82+
true);
83+
84+
additionalFields = new HashMap<String, Object>();
85+
converter.additionalFields(additionalFields, event);
86+
validateFieldTypes(additionalFields);
87+
88+
// test without including full MDC
89+
converter = new GelfConverter("facilty", false, false, false,
90+
new HashMap<String, String>(), fieldTypes,
91+
new HashMap<String, String>(), 10, "host", "%m%rEx", null,
92+
false);
93+
converter.additionalFields(additionalFields, event);
94+
validateFieldTypes(additionalFields);
95+
}
96+
97+
private void validateFieldTypes(Map<String, Object> fields) {
98+
assertEquals(123, ((Integer) fields.get("_intField")).intValue());
99+
assertEquals(456, ((Integer) fields.get("_IntegerField")).intValue());
100+
assertEquals((long) Integer.MAX_VALUE + 1, ((Long) fields.get("_longField")).longValue());
101+
assertEquals((long) Integer.MAX_VALUE + 2, ((Long) fields.get("_LongField")).longValue());
102+
assertEquals((float) 3.14159, ((Float) fields.get("_floatField")).floatValue(), 0);
103+
assertEquals((float) 2.71828, ((Float) fields.get("_FloatField")).floatValue(), 0);
104+
assertEquals((double) 3e-50, ((Double) fields.get("_doubleField")).doubleValue(), 0);
105+
assertEquals((double) 3e50, ((Double) fields.get("_DoubleField")).doubleValue(), 0);
106+
assertEquals("bar", ((String) fields.get("_foo")));
107+
}
108+
47109
private void testForMarker(boolean useMarker) {
48110
ILoggingEvent event = createEvent(useMarker);
49111
GelfConverter converter = createGelfConverter(useMarker);
@@ -63,7 +125,7 @@ private LoggingEvent createEvent(boolean addMarker) {
63125
}
64126

65127
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);
128+
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);
67129
}
68130

69131
}

0 commit comments

Comments
 (0)