import org.apache.commons.io.FileUtils;
import org.apache.flume.Context;
import org.apache.flume.Event;
import org.apache.flume.EventDrivenSource;
import org.apache.flume.channel.ChannelProcessor;
import org.apache.flume.conf.Configurable;
import org.apache.flume.event.EventBuilder;
import org.apache.flume.source.AbstractSource;
import org.apache.flume.source.ExecSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
-
自定义source源
*/
public class TailFileSourceDemo extends AbstractSource implements Configurable, EventDrivenSource {
//方便错误日志打印
private static final Logger logger = LoggerFactory.getLogger(ExecSource.class);private String filePath;
private String posiFile;
private Long interval;
private String charset;private ExecutorService executorService;
private FileRunner fileRunner;//先调用configure 方法
@Override
public void configure(Context context) {
//日志路径
filePath = context.getString(“filePath”);
//偏移量保持文件
posiFile = context.getString(“posiFile”);
//多长时间读取一次
interval = context.getLong(“interval”);
//字符集
charset = context.getString(“charset”, “UTF-8”);
}//在configure方法调用之后之后调用,只调用一次
@Override
public synchronized void start() {
//创建一个线程池(只有一个线程)
executorService = Executors.newSingleThreadExecutor();//调用父类的方法拿到channelProcessor ChannelProcessor channelProcessor = getChannelProcessor(); //创建一个实现Runnable接口的实现类 fileRunner = new FileRunner(filePath,posiFile,interval,charset,channelProcessor); //提交到线程线池 executorService.submit(fileRunner); //执行父类的start方法 super.start();
}
@Override
public synchronized void stop() {
//停掉线程
fileRunner.setFlag(false);
//释放线程池
executorService.shutdown();super.stop();
}
private class FileRunner implements Runnable {
private String filePath;
private String posiFile;
private Long interval;
private String charset;
private ChannelProcessor channelProcessor;private Long offset = 0L; private RandomAccessFile raf; private File offFile; private Boolean flag = true; public void setFlag(Boolean flag) { this.flag = flag; } public FileRunner(String filePath, String posiFile, Long interval, String charset, ChannelProcessor channelProcessor) { this.filePath = filePath; this.posiFile = posiFile; this.interval = interval; this.charset = charset; this.channelProcessor = channelProcessor; //判断是否有偏移量文件 offFile = new File(posiFile); if(!offFile.exists()){ //没有就创建 try { offFile.createNewFile(); } catch (IOException e) { logger.error("offFile error ....",e); } } //如果有偏移量,接着读 try { String offsetStr = FileUtils.readFileToString(offFile); if(offsetStr != null && "".equals(offsetStr)){ //转换成Long类型 offset = Long.parseLong(offsetStr); } } catch (IOException e) { logger.error("offset error ....",e); } //RandomAccessFile的seek跳转到指定的偏移量 try { raf = new RandomAccessFile(filePath, "r"); //指定偏移量 raf.seek(offset); } catch (FileNotFoundException e) { logger.error("raf error ....",e); } catch (IOException e) { logger.error("seek error ....",e); } } @Override public void run() { //不停监听一个文件 while (flag){ //读取数据封装成Event try { String line = raf.readLine(); //读到内容 if(line != null){ //转码解决中文乱码问题 line = new String(line.getBytes("ISO-8859-1"),charset); //封装成Event Event event = EventBuilder.withBody(line.getBytes()); //用ChannelProcessor将数据发送到Channel channelProcessor.processEvent(event); //获取最新偏移量。然后写入到偏移量文件中 offset = raf.getFilePointer(); FileUtils.writeStringToFile(offFile,offset+""); }else{ //没读到睡觉 Thread.sleep(interval); } } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
}
}