mirror of
https://github.com/elastic/logstash.git
synced 2025-04-24 06:37:19 -04:00
more sprintf java impl, relates to #4191
First the case with single fieldref Add support for +%s => epoch Small comments concerning the caching mechanism and the memory usage Make the #sprintf method work with Array and Hash
This commit is contained in:
parent
a8185a2043
commit
731d5dbb00
11 changed files with 129 additions and 29 deletions
|
@ -4,6 +4,9 @@ import org.joda.time.DateTimeZone;
|
|||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by ph on 15-05-22.
|
||||
*/
|
||||
|
@ -15,7 +18,7 @@ public class DateNode implements TemplateNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String evaluate(Event event) {
|
||||
public String evaluate(Event event) throws IOException {
|
||||
return event.getTimestamp().getTime().toString(this.formatter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.logstash;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by ph on 15-05-22.
|
||||
*/
|
||||
|
@ -7,9 +9,7 @@ public class EpochNode implements TemplateNode {
|
|||
public EpochNode(){ }
|
||||
|
||||
@Override
|
||||
public String evaluate(Event event) {
|
||||
// TODO: Change this for the right call
|
||||
Long epoch = 1L;
|
||||
return String.valueOf(epoch);
|
||||
public String evaluate(Event event) throws IOException {
|
||||
return String.valueOf(event.getTimestamp().getTime().getMillis() / 1000);
|
||||
}
|
||||
}
|
|
@ -42,5 +42,5 @@ public interface Event {
|
|||
|
||||
Event append(Event e);
|
||||
|
||||
String sprintf(String s);
|
||||
String sprintf(String s) throws IOException;
|
||||
}
|
||||
|
|
|
@ -133,9 +133,8 @@ public class EventImpl implements Event, Cloneable, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String sprintf(String s) {
|
||||
// TODO: implement sprintf
|
||||
return s;
|
||||
public String sprintf(String s) throws IOException {
|
||||
return StringInterpolation.getInstance().evaluate(this, s);
|
||||
}
|
||||
|
||||
public Event clone() {
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
package com.logstash;
|
||||
|
||||
import org.codehaus.jackson.JsonGenerationException;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by ph on 15-05-22.
|
||||
*/
|
||||
|
@ -15,11 +22,19 @@ public class KeyNode implements TemplateNode {
|
|||
leverage jackson lib to do the actual.
|
||||
*/
|
||||
@Override
|
||||
public String evaluate(Event event) {
|
||||
public String evaluate(Event event) throws IOException {
|
||||
Object value = event.getField(this.key);
|
||||
|
||||
if (value != null) {
|
||||
return String.valueOf(event.getField(this.key));
|
||||
if (value instanceof List) {
|
||||
return String.join(",", (List) value);
|
||||
} else if (value instanceof Map) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.writeValueAsString((Map<String, Object>)value);
|
||||
} else {
|
||||
return event.getField(this.key).toString();
|
||||
}
|
||||
|
||||
} else {
|
||||
return "%{" + this.key + "}";
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.logstash;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by ph on 15-05-22.
|
||||
*/
|
||||
|
@ -11,7 +13,7 @@ public class StaticNode implements TemplateNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String evaluate(Event event) {
|
||||
public String evaluate(Event event) throws IOException {
|
||||
return this.content;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
package com.logstash;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -15,11 +17,19 @@ public class StringInterpolation {
|
|||
}
|
||||
|
||||
private StringInterpolation() {
|
||||
// TODO: this may need some tweaking for the concurrency level to get better memory usage.
|
||||
// TODO:
|
||||
// This may need some tweaking for the concurrency level to get better memory usage.
|
||||
// The current implementation doesn't allow the keys to expire, I think under normal usage
|
||||
// the keys will converge to a fixed number.
|
||||
//
|
||||
// If this code make logstash goes OOM, we have the following options:
|
||||
// - If the key doesn't contains a `%` do not cache it, this will reduce the key size at a performance cost.
|
||||
// - Use some kind LRU cache
|
||||
// - Create a new data structure that use weakref or use Google Guava for the cache https://code.google.com/p/guava-libraries/
|
||||
this.cache = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public String evaluate(Event event, String template) {
|
||||
public String evaluate(Event event, String template) throws IOException {
|
||||
TemplateNode compiledTemplate = (TemplateNode) this.cache.get(template);
|
||||
|
||||
if(compiledTemplate == null) {
|
||||
|
@ -46,11 +56,11 @@ public class StringInterpolation {
|
|||
while (matcher.find()) {
|
||||
if (matcher.start() > 0) {
|
||||
compiledTemplate.add(new StaticNode(template.substring(pos, matcher.start())));
|
||||
pos = matcher.end();
|
||||
}
|
||||
|
||||
tag = matcher.group(1);
|
||||
compiledTemplate.add(identifyTag(tag));
|
||||
pos = matcher.end();
|
||||
}
|
||||
|
||||
if(pos < template.length() - 1) {
|
||||
|
@ -67,11 +77,12 @@ public class StringInterpolation {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: add support for array, hash, float and epoch
|
||||
public TemplateNode identifyTag(String tag) {
|
||||
// Doesnt support parsing the float yet
|
||||
if(tag.charAt(0) == '+') {
|
||||
return new DateNode(tag.substring(1));
|
||||
if(tag.equals("+%s")) {
|
||||
return new EpochNode();
|
||||
} else if(tag.charAt(0) == '+') {
|
||||
return new DateNode(tag.substring(1));
|
||||
|
||||
} else {
|
||||
return new KeyNode(tag);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.logstash;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -20,7 +21,7 @@ public class Template implements TemplateNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String evaluate(Event event) {
|
||||
public String evaluate(Event event) throws IOException {
|
||||
StringBuffer results = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
|
|
12
src/main/java/com/logstash/TemplateNode.java
Normal file
12
src/main/java/com/logstash/TemplateNode.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package com.logstash;
|
||||
|
||||
import org.codehaus.jackson.JsonGenerationException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by ph on 15-05-22.
|
||||
*/
|
||||
public interface TemplateNode {
|
||||
String evaluate(Event event) throws IOException;
|
||||
}
|
|
@ -193,8 +193,7 @@ public class JrubyEventExtLibrary implements Library {
|
|||
}
|
||||
|
||||
@JRubyMethod(name = "sprintf", required = 1)
|
||||
public IRubyObject ruby_sprintf(ThreadContext context, IRubyObject format)
|
||||
{
|
||||
public IRubyObject ruby_sprintf(ThreadContext context, IRubyObject format) throws IOException {
|
||||
return RubyString.newString(context.runtime, event.sprintf(format.toString()));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package com.logstash;
|
||||
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -11,7 +14,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
public class StringInterpolationTest {
|
||||
@Test
|
||||
public void testCompletelyStaticTemplate() {
|
||||
public void testCompletelyStaticTemplate() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "/full/path/awesome";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
|
@ -20,7 +23,7 @@ public class StringInterpolationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testOneLevelField() {
|
||||
public void testOneLevelField() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "/full/%{bar}/awesome";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
|
@ -29,7 +32,7 @@ public class StringInterpolationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleLevelField() {
|
||||
public void testMultipleLevelField() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "/full/%{bar}/%{awesome}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
|
@ -38,7 +41,7 @@ public class StringInterpolationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMissingKey() {
|
||||
public void testMissingKey() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "/full/%{do-not-exist}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
|
@ -47,7 +50,7 @@ public class StringInterpolationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDateFormater() {
|
||||
public void testDateFormater() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "/full/%{+YYYY}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
|
@ -56,7 +59,7 @@ public class StringInterpolationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void TestMixDateAndFields() {
|
||||
public void TestMixDateAndFields() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "/full/%{+YYYY}/weeee/%{bar}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
|
@ -65,7 +68,7 @@ public class StringInterpolationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUnclosedTag() {
|
||||
public void testUnclosedTag() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "/full/%{+YYY/web";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
|
@ -73,10 +76,65 @@ public class StringInterpolationTest {
|
|||
assertEquals("/full/%{+YYY/web", si.evaluate(event, path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestStringIsOneDateTag() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "%{+YYYY}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
assertEquals("2015", si.evaluate(event, path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestFieldRef() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "%{[j][k1]}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
assertEquals("v", si.evaluate(event, path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestEpoch() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
String path = "%{+%s}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
assertEquals("1443672000", si.evaluate(event, path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestValueIsArray() throws IOException {
|
||||
ArrayList l = new ArrayList();
|
||||
l.add("Hello");
|
||||
l.add("world");
|
||||
|
||||
Event event = getTestEvent();
|
||||
event.setField("message", l);
|
||||
|
||||
String path = "%{message}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
assertEquals("Hello,world", si.evaluate(event, path));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestValueIsHash() throws IOException {
|
||||
Event event = getTestEvent();
|
||||
|
||||
String path = "%{j}";
|
||||
StringInterpolation si = StringInterpolation.getInstance();
|
||||
assertEquals("{\"k1\":\"v\"}", si.evaluate(event, path));
|
||||
}
|
||||
|
||||
public Event getTestEvent() {
|
||||
Map data = new HashMap();
|
||||
Map inner = new HashMap();
|
||||
|
||||
inner.put("k1", "v");
|
||||
|
||||
data.put("bar", "foo");
|
||||
data.put("awesome", "logstash");
|
||||
data.put("j", inner);
|
||||
data.put("@timestamp", new DateTime(2015, 10, 1, 0, 0, 0));
|
||||
|
||||
|
||||
Event event = new EventImpl(data);
|
||||
|
||||
return event;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue