package com.lucidworks.storm.io;
import com.codahale.metrics.Counter;
import com.lucidworks.storm.io.parsers.LogLineParser;
import com.lucidworks.storm.io.parsers.NoOpLogLineParser;
import com.lucidworks.storm.spring.NamedValues;
import com.lucidworks.storm.spring.StreamingDataProvider;
import java.io.File;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import com.ryantenney.metrics.annotation.Metric;
import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListenerAdapter;
import org.apache.log4j.Logger;
public class FileTailingDataProvider extends TailerListenerAdapter implements StreamingDataProvider {
public static final Logger log = Logger.getLogger(FileTailingDataProvider.class);
@Metric
public Counter linesOutput;
@Metric
public Counter linesRead;
protected File fileToParse;
protected String fileToParseName;
protected long tailerDelayMs = 500;
protected Tailer tailer;
protected int maxQueueSize = 100000;
protected BlockingQueue<Map> parsedLineQueue;
protected LogLineParser lineParser;
protected int pollQueueTimeMs = 50;
protected String idFieldName = "id";
protected int currentLine;
public FileTailingDataProvider(File fileToParse) {
if (fileToParse == null)
throw new IllegalStateException("Must specify which file to tail!");
if (!fileToParse.isFile())
throw new IllegalStateException(fileToParse.getAbsolutePath()+" not found!");
this.fileToParse = fileToParse;
}
public void open(Map stormConf) {
fileToParseName = fileToParse.getAbsolutePath();
parsedLineQueue = new LinkedBlockingQueue<Map>(maxQueueSize);
currentLine = 0;
if (lineParser == null)
lineParser = new NoOpLogLineParser();
tailer = Tailer.create(fileToParse, this, tailerDelayMs);
log.info("Started tailing "+fileToParseName);
}
public void handle(String line) {
++currentLine;
if (line == null || line.isEmpty())
return;
try {
handleLine(line);
} catch (Exception exc) {
log.error("Failed to parse line "+currentLine+" in "+
fileToParse.getAbsolutePath()+" due to: "+exc+"; line=["+line+"]", exc);
}
}
protected void handleLine(String line) throws Exception {
if (linesRead != null)
linesRead.inc();
Map parsedLine = lineParser.parseLine(fileToParseName, currentLine, line);
if (parsedLine != null) {
if (parsedLine.get(idFieldName) == null) {
parsedLine.put(idFieldName, String.format("%s-%d", fileToParseName, currentLine));
}
parsedLineQueue.put(parsedLine); // must block until queue space becomes available ...
}
}
public boolean next(NamedValues record) throws Exception {
Map nextLine = parsedLineQueue.poll(pollQueueTimeMs, TimeUnit.MILLISECONDS);
if (nextLine != null) {
record.set("id", (String)nextLine.get(idFieldName));
record.set("line", nextLine);
if (linesOutput != null)
linesOutput.inc();
return true;
}
return false;
}
public long getTailerDelayMs() {
return tailerDelayMs;
}
public void setTailerDelayMs(long tailerDelayMs) {
this.tailerDelayMs = tailerDelayMs;
}
public int getMaxQueueSize() {
return maxQueueSize;
}
public void setMaxQueueSize(int maxQueueSize) {
this.maxQueueSize = maxQueueSize;
}
public LogLineParser getLineParser() {
return lineParser;
}
public void setLineParser(LogLineParser lineParser) {
this.lineParser = lineParser;
}
public int getPollQueueTimeMs() {
return pollQueueTimeMs;
}
public void setPollQueueTimeMs(int pollQueueTimeMs) {
this.pollQueueTimeMs = pollQueueTimeMs;
}
}