flume 自定义source,sink,channel,拦截器

按照以往的惯例,还是需求驱动学习,有位网友在我的flume学习五中留言提了一个问题如下:

我想实现一个功能,就在读一个文件的时候,将文件的名字和文件生成的日期作为event的header传到hdfs上时,不同的event存到不同的目录下,如一个文件是a.log.2014-07-25在hdfs上是存到/a/2014-07-25目录下,a.log.2014-07-26存到/a/2014-07-26目录下,就是每个文件对应自己的目录,这个要怎么实现。


带着这个问题,我又重新翻看了官方的文档,发现一个spooling directory source跟这个需求稍微有点吻合:它监视指定的文件夹下面有没有写入新的文件,有的话,就会把该文件内容传递给sink,然后将该文件后缀标示为.complete,表示已处理。提供了参数可以将文件名和文件全路径名添加到event的header中去。


现有的功能不能满足我们的需求,但是至少提供了一个方向:它能将文件名放入header!

当时就在祈祷源代码不要太复杂,这样我们在这个地方稍微修改修改,把文件名拆分一下,然后再放入header,这样就完成了我们想要的功能了。

于是就打开了源代码,果然不复杂,代码结构非常清晰,按照我的思路,稍微改了一下,就实现了这个功能,主要修改了与spooling directory source代码相关的三个类,分别是:ReliableSpoolingFileEventExtReader,SpoolDirectorySourceConfigurationExtConstants,SpoolDirectoryExtSource(在原类名的基础上增加了:Ext)代码如下:

首先,要根据flume ng提供的接口来实现自定义source,需要我们依赖flume ng的配置,我们引入两个配置flume-ng-core和flume-ng-configuration,具体的maven配置如下:

        <dependency>
            <groupId>org.apache.flumegroupId>
            <artifactId>flume-ng-coreartifactId>
            <version>1.6.0version>
        dependency>
        <dependency>
            <groupId>org.apache.flumegroupId>
            <artifactId>flume-ng-configurationartifactId>
            <version>1.6.0version>
        dependency>

[java]  view plain  copy
  1. /* 
  2.  * Licensed to the Apache Software Foundation (ASF) under one 
  3.  * or more contributor license agreements.  See the NOTICE file 
  4.  * distributed with this work for additional information 
  5.  * regarding copyright ownership.  The ASF licenses this file 
  6.  * to you under the Apache License, Version 2.0 (the 
  7.  * "License"); you may not use this file except in compliance 
  8.  * with the License.  You may obtain a copy of the License at 
  9.  * 
  10.  * http://www.apache.org/licenses/LICENSE-2.0 
  11.  * 
  12.  * Unless required by applicable law or agreed to in writing, 
  13.  * software distributed under the License is distributed on an 
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
  15.  * KIND, either express or implied.  See the License for the 
  16.  * specific language governing permissions and limitations 
  17.  * under the License. 
  18.  */  
  19.   
  20. package com.besttone.flume;  
  21.   
  22. import java.io.File;  
  23. import java.io.FileFilter;  
  24. import java.io.FileNotFoundException;  
  25. import java.io.IOException;  
  26. import java.nio.charset.Charset;  
  27. import java.util.Arrays;  
  28. import java.util.Collections;  
  29. import java.util.Comparator;  
  30. import java.util.List;  
  31. import java.util.regex.Matcher;  
  32. import java.util.regex.Pattern;  
  33.   
  34. import org.apache.flume.Context;  
  35. import org.apache.flume.Event;  
  36. import org.apache.flume.FlumeException;  
  37. import org.apache.flume.annotations.InterfaceAudience;  
  38. import org.apache.flume.annotations.InterfaceStability;  
  39. import org.apache.flume.client.avro.ReliableEventReader;  
  40. import org.apache.flume.serialization.DecodeErrorPolicy;  
  41. import org.apache.flume.serialization.DurablePositionTracker;  
  42. import org.apache.flume.serialization.EventDeserializer;  
  43. import org.apache.flume.serialization.EventDeserializerFactory;  
  44. import org.apache.flume.serialization.PositionTracker;  
  45. import org.apache.flume.serialization.ResettableFileInputStream;  
  46. import org.apache.flume.serialization.ResettableInputStream;  
  47. import org.apache.flume.tools.PlatformDetect;  
  48. import org.joda.time.DateTime;  
  49. import org.joda.time.format.DateTimeFormat;  
  50. import org.joda.time.format.DateTimeFormatter;  
  51. import org.slf4j.Logger;  
  52. import org.slf4j.LoggerFactory;  
  53.   
  54. import com.google.common.base.Charsets;  
  55. import com.google.common.base.Optional;  
  56. import com.google.common.base.Preconditions;  
  57. import com.google.common.io.Files;  
  58.   
  59. /** 
  60.  * 

     

  61.  * A {@link ReliableEventReader} which reads log data from files stored in a 
  62.  * spooling directory and renames each file once all of its data has been read 
  63.  * (through {@link EventDeserializer#readEvent()} calls). The user must 
  64.  * {@link #commit()} each read, to indicate that the lines have been fully 
  65.  * processed. 
  66.  * 

     

  67.  * Read calls will return no data if there are no files left to read. This 
  68.  * class, in general, is not thread safe. 
  69.  *  
  70.  * 

     

  71.  * This reader assumes that files with unique file names are left in the 
  72.  * spooling directory and not modified once they are placed there. Any user 
  73.  * behavior which violates these assumptions, when detected, will result in a 
  74.  * FlumeException being thrown. 
  75.  *  
  76.  * 

     

  77.  * This class makes the following guarantees, if above assumptions are met: 
  78.  * 
       
    •  * 
    • Once a log file has been renamed with the {@link #completedSuffix}, all 
    •  * of its records have been read through the 
    •  * {@link EventDeserializer#readEvent()} function and {@link #commit()}ed at 
    •  * least once. 
    •  * 
    • All files in the spooling directory will eventually be opened and 
    •  * delivered to a {@link #readEvents(int)} caller. 
    •  * 
     
  79.  */  
  80. @InterfaceAudience.Private  
  81. @InterfaceStability.Evolving  
  82. public class ReliableSpoolingFileEventExtReader implements ReliableEventReader {  
  83.   
  84.     private static final Logger logger = LoggerFactory  
  85.             .getLogger(ReliableSpoolingFileEventExtReader.class);  
  86.   
  87.     static final String metaFileName = ".flumespool-main.meta";  
  88.   
  89.     private final File spoolDirectory;  
  90.     private final String completedSuffix;  
  91.     private final String deserializerType;  
  92.     private final Context deserializerContext;  
  93.     private final Pattern ignorePattern;  
  94.     private final File metaFile;  
  95.     private final boolean annotateFileName;  
  96.     private final boolean annotateBaseName;  
  97.     private final String fileNameHeader;  
  98.     private final String baseNameHeader;  
  99.   
  100.     // 添加参数开始  
  101.     private final boolean annotateFileNameExtractor;  
  102.     private final String fileNameExtractorHeader;  
  103.     private final Pattern fileNameExtractorPattern;  
  104.     private final boolean convertToTimestamp;  
  105.     private final String dateTimeFormat;  
  106.   
  107.     private final boolean splitFileName;  
  108.     private final String splitBy;  
  109.     private final String splitBaseNameHeader;  
  110.     // 添加参数结束  
  111.   
  112.     private final String deletePolicy;  
  113.     private final Charset inputCharset;  
  114.     private final DecodeErrorPolicy decodeErrorPolicy;  
  115.   
  116.     private Optional currentFile = Optional.absent();  
  117.     /** Always contains the last file from which lines have been read. **/  
  118.     private Optional lastFileRead = Optional.absent();  
  119.     private boolean committed = true;  
  120.   
  121.     /** 
  122.      * Create a ReliableSpoolingFileEventReader to watch the given directory. 
  123.      */  
  124.     private ReliableSpoolingFileEventExtReader(File spoolDirectory,  
  125.             String completedSuffix, String ignorePattern,  
  126.             String trackerDirPath, boolean annotateFileName,  
  127.             String fileNameHeader, boolean annotateBaseName,  
  128.             String baseNameHeader, String deserializerType,  
  129.             Context deserializerContext, String deletePolicy,  
  130.             String inputCharset, DecodeErrorPolicy decodeErrorPolicy,  
  131.             boolean annotateFileNameExtractor, String fileNameExtractorHeader,  
  132.             String fileNameExtractorPattern, boolean convertToTimestamp,  
  133.             String dateTimeFormat, boolean splitFileName, String splitBy,  
  134.             String splitBaseNameHeader) throws IOException {  
  135.   
  136.         // Sanity checks  
  137.         Preconditions.checkNotNull(spoolDirectory);  
  138.         Preconditions.checkNotNull(completedSuffix);  
  139.         Preconditions.checkNotNull(ignorePattern);  
  140.         Preconditions.checkNotNull(trackerDirPath);  
  141.         Preconditions.checkNotNull(deserializerType);  
  142.         Preconditions.checkNotNull(deserializerContext);  
  143.         Preconditions.checkNotNull(deletePolicy);  
  144.         Preconditions.checkNotNull(inputCharset);  
  145.   
  146.         // validate delete policy  
  147.         if (!deletePolicy.equalsIgnoreCase(DeletePolicy.NEVER.name())  
  148.                 && !deletePolicy  
  149.                         .equalsIgnoreCase(DeletePolicy.IMMEDIATE.name())) {  
  150.             throw new IllegalArgumentException("Delete policies other than "  
  151.                     + "NEVER and IMMEDIATE are not yet supported");  
  152.         }  
  153.   
  154.         if (logger.isDebugEnabled()) {  
  155.             logger.debug("Initializing {} with directory={}, metaDir={}, "  
  156.                     + "deserializer={}"new Object[] {  
  157.                     ReliableSpoolingFileEventExtReader.class.getSimpleName(),  
  158.                     spoolDirectory, trackerDirPath, deserializerType });  
  159.         }  
  160.   
  161.         // Verify directory exists and is readable/writable  
  162.         Preconditions  
  163.                 .checkState(  
  164.                         spoolDirectory.exists(),  
  165.                         "Directory does not exist: "  
  166.                                 + spoolDirectory.getAbsolutePath());  
  167.         Preconditions.checkState(spoolDirectory.isDirectory(),  
  168.                 "Path is not a directory: " + spoolDirectory.getAbsolutePath());  
  169.   
  170.         // Do a canary test to make sure we have access to spooling directory  
  171.         try {  
  172.             File canary = File.createTempFile("flume-spooldir-perm-check-",  
  173.                     ".canary", spoolDirectory);  
  174.             Files.write("testing flume file permissions\n", canary,  
  175.                     Charsets.UTF_8);  
  176.             List lines = Files.readLines(canary, Charsets.UTF_8);  
  177.             Preconditions.checkState(!lines.isEmpty(), "Empty canary file %s",  
  178.                     canary);  
  179.             if (!canary.delete()) {  
  180.                 throw new IOException("Unable to delete canary file " + canary);  
  181.             }  
  182.             logger.debug("Successfully created and deleted canary file: {}",  
  183.                     canary);  
  184.         } catch (IOException e) {  
  185.             throw new FlumeException("Unable to read and modify files"  
  186.                     + " in the spooling directory: " + spoolDirectory, e);  
  187.         }  
  188.   
  189.         this.spoolDirectory = spoolDirectory;  
  190.         this.completedSuffix = completedSuffix;  
  191.         this.deserializerType = deserializerType;  
  192.         this.deserializerContext = deserializerContext;  
  193.         this.annotateFileName = annotateFileName;  
  194.         this.fileNameHeader = fileNameHeader;  
  195.         this.annotateBaseName = annotateBaseName;  
  196.         this.baseNameHeader = baseNameHeader;  
  197.         this.ignorePattern = Pattern.compile(ignorePattern);  
  198.         this.deletePolicy = deletePolicy;  
  199.         this.inputCharset = Charset.forName(inputCharset);  
  200.         this.decodeErrorPolicy = Preconditions.checkNotNull(decodeErrorPolicy);  
  201.   
  202.         // 增加代码开始  
  203.         this.annotateFileNameExtractor = annotateFileNameExtractor;  
  204.         this.fileNameExtractorHeader = fileNameExtractorHeader;  
  205.         this.fileNameExtractorPattern = Pattern  
  206.                 .compile(fileNameExtractorPattern);  
  207.         this.convertToTimestamp = convertToTimestamp;  
  208.         this.dateTimeFormat = dateTimeFormat;  
  209.   
  210.         this.splitFileName = splitFileName;  
  211.         this.splitBy = splitBy;  
  212.         this.splitBaseNameHeader = splitBaseNameHeader;  
  213.         // 增加代码结束  
  214.   
  215.         File trackerDirectory = new File(trackerDirPath);  
  216.   
  217.         // if relative path, treat as relative to spool directory  
  218.         if (!trackerDirectory.isAbsolute()) {  
  219.             trackerDirectory = new File(spoolDirectory, trackerDirPath);  
  220.         }  
  221.   
  222.         // ensure that meta directory exists  
  223.         if (!trackerDirectory.exists()) {  
  224.             if (!trackerDirectory.mkdir()) {  
  225.                 throw new IOException(  
  226.                         "Unable to mkdir nonexistent meta directory "  
  227.                                 + trackerDirectory);  
  228.             }  
  229.         }  
  230.   
  231.         // ensure that the meta directory is a directory  
  232.         if (!trackerDirectory.isDirectory()) {  
  233.             throw new IOException("Specified meta directory is not a directory"  
  234.                     + trackerDirectory);  
  235.         }  
  236.   
  237.         this.metaFile = new File(trackerDirectory, metaFileName);  
  238.     }  
  239.   
  240.     /** 
  241.      * Return the filename which generated the data from the last successful 
  242.      * {@link #readEvents(int)} call. Returns null if called before any file 
  243.      * contents are read. 
  244.      */  
  245.     public String getLastFileRead() {  
  246.         if (!lastFileRead.isPresent()) {  
  247.             return null;  
  248.         }  
  249.         return lastFileRead.get().getFile().getAbsolutePath();  
  250.     }  
  251.   
  252.     // public interface  
  253.     public Event readEvent() throws IOException {  
  254.         List events = readEvents(1);  
  255.         if (!events.isEmpty()) {  
  256.             return events.get(0);  
  257.         } else {  
  258.             return null;  
  259.         }  
  260.     }  
  261.   
  262.     public List readEvents(int numEvents) throws IOException {  
  263.         if (!committed) {  
  264.             if (!currentFile.isPresent()) {  
  265.                 throw new IllegalStateException("File should not roll when "  
  266.                         + "commit is outstanding.");  
  267.             }  
  268.             logger.info("Last read was never committed - resetting mark position.");  
  269.             currentFile.get().getDeserializer().reset();  
  270.         } else {  
  271.             // Check if new files have arrived since last call  
  272.             if (!currentFile.isPresent()) {  
  273.                 currentFile = getNextFile();  
  274.             }  
  275.             // Return empty list if no new files  
  276.             if (!currentFile.isPresent()) {  
  277.                 return Collections.emptyList();  
  278.             }  
  279.         }  
  280.   
  281.         EventDeserializer des = currentFile.get().getDeserializer();  
  282.         List events = des.readEvents(numEvents);  
  283.   
  284.         /* 
  285.          * It's possible that the last read took us just up to a file boundary. 
  286.          * If so, try to roll to the next file, if there is one. 
  287.          */  
  288.         if (events.isEmpty()) {  
  289.             retireCurrentFile();  
  290.             currentFile = getNextFile();  
  291.             if (!currentFile.isPresent()) {  
  292.                 return Collections.emptyList();  
  293.             }  
  294.             events = currentFile.get().getDeserializer().readEvents(numEvents);  
  295.         }  
  296.   
  297.         if (annotateFileName) {  
  298.             String filename = currentFile.get().getFile().getAbsolutePath();  
  299.             for (Event event : events) {  
  300.                 event.getHeaders().put(fileNameHeader, filename);  
  301.             }  
  302.         }  
  303.   
  304.         if (annotateBaseName) {  
  305.             String basename = currentFile.get().getFile().getName();  
  306.             for (Event event : events) {  
  307.                 event.getHeaders().put(baseNameHeader, basename);  
  308.             }  
  309.         }  
  310.   
  311.         // 增加代码开始  
  312.   
  313.         // 按正则抽取文件名的内容  
  314.         if (annotateFileNameExtractor) {  
  315.   
  316.             Matcher matcher = fileNameExtractorPattern.matcher(currentFile  
  317.                     .get().getFile().getName());  
  318.   
  319.             if (matcher.find()) {  
  320.                 String value = matcher.group();  
  321.                 if (convertToTimestamp) {  
  322.                     DateTimeFormatter formatter = DateTimeFormat  
  323.                             .forPattern(dateTimeFormat);  
  324.                     DateTime dateTime = formatter.parseDateTime(value);  
  325.   
  326.                     value = Long.toString(dateTime.getMillis());  
  327.                 }  
  328.   
  329.                 for (Event event : events) {  
  330.                     event.getHeaders().put(fileNameExtractorHeader, value);  
  331.                 }  
  332.             }  
  333.   
  334.         }  
  335.   
  336.         // 按分隔符拆分文件名  
  337.         if (splitFileName) {  
  338.             String[] splits = currentFile.get().getFile().getName()  
  339.                     .split(splitBy);  
  340.   
  341.             for (Event event : events) {  
  342.                 for (int i = 0; i < splits.length; i++) {  
  343.                     event.getHeaders().put(splitBaseNameHeader + i, splits[i]);  
  344.                 }  
  345.   
  346.             }  
  347.   
  348.         }  
  349.   
  350.         // 增加代码结束  
  351.   
  352.         committed = false;  
  353.         lastFileRead = currentFile;  
  354.         return events;  
  355.     }  
  356.   
  357.     @Override  
  358.     public void close() throws IOException {  
  359.         if (currentFile.isPresent()) {  
  360.             currentFile.get().getDeserializer().close();  
  361.             currentFile = Optional.absent();  
  362.         }  
  363.     }  
  364.   
  365.     /** Commit the last lines which were read. */  
  366.     @Override  
  367.     public void commit() throws IOException {  
  368.         if (!committed && currentFile.isPresent()) {  
  369.             currentFile.get().getDeserializer().mark();  
  370.             committed = true;  
  371.         }  
  372.     }  
  373.   
  374.     /** 
  375.      * Closes currentFile and attempt to rename it. 
  376.      *  
  377.      * If these operations fail in a way that may cause duplicate log entries, 
  378.      * an error is logged but no exceptions are thrown. If these operations fail 
  379.      * in a way that indicates potential misuse of the spooling directory, a 
  380.      * FlumeException will be thrown. 
  381.      *  
  382.      * @throws FlumeException 
  383.      *             if files do not conform to spooling assumptions 
  384.      */  
  385.     private void retireCurrentFile() throws IOException {  
  386.         Preconditions.checkState(currentFile.isPresent());  
  387.   
  388.         File fileToRoll = new File(currentFile.get().getFile()  
  389.                 .getAbsolutePath());  
  390.   
  391.         currentFile.get().getDeserializer().close();  
  392.   
  393.         // Verify that spooling assumptions hold  
  394.         if (fileToRoll.lastModified() != currentFile.get().getLastModified()) {  
  395.             String message = "File has been modified since being read: "  
  396.                     + fileToRoll;  
  397.             throw new IllegalStateException(message);  
  398.         }  
  399.         if (fileToRoll.length() != currentFile.get().getLength()) {  
  400.             String message = "File has changed size since being read: "  
  401.                     + fileToRoll;  
  402.             throw new IllegalStateException(message);  
  403.         }  
  404.   
  405.         if (deletePolicy.equalsIgnoreCase(DeletePolicy.NEVER.name())) {  
  406.             rollCurrentFile(fileToRoll);  
  407.         } else if (deletePolicy.equalsIgnoreCase(DeletePolicy.IMMEDIATE.name())) {  
  408.             deleteCurrentFile(fileToRoll);  
  409.         } else {  
  410.             // TODO: implement delay in the future  
  411.             throw new IllegalArgumentException("Unsupported delete policy: "  
  412.                     + deletePolicy);  
  413.         }  
  414.     }  
  415.   
  416.     /** 
  417.      * Rename the given spooled file 
  418.      *  
  419.      * @param fileToRoll 
  420.      * @throws IOException 
  421.      */  
  422.     private void rollCurrentFile(File fileToRoll) throws IOException {  
  423.   
  424.         File dest = new File(fileToRoll.getPath() + completedSuffix);  
  425.         logger.info("Preparing to move file {} to {}", fileToRoll, dest);  
  426.   
  427.         // Before renaming, check whether destination file name exists  
  428.         if (dest.exists() && PlatformDetect.isWindows()) {  
  429.             /* 
  430.              * If we are here, it means the completed file already exists. In 
  431.              * almost every case this means the user is violating an assumption 
  432.              * of Flume (that log files are placed in the spooling directory 
  433.              * with unique names). However, there is a corner case on Windows 
  434.              * systems where the file was already rolled but the rename was not 
  435.              * atomic. If that seems likely, we let it pass with only a warning. 
  436.              */  
  437.             if (Files.equal(currentFile.get().getFile(), dest)) {  
  438.                 logger.warn("Completed file " + dest  
  439.                         + " already exists, but files match, so continuing.");  
  440.                 boolean deleted = fileToRoll.delete();  
  441.                 if (!deleted) {  
  442.                     logger.error("Unable to delete file "  
  443.                             + fileToRoll.getAbsolutePath()  
  444.                             + ". It will likely be ingested another time.");  
  445.                 }  
  446.             } else {  
  447.                 String message = "File name has been re-used with different"  
  448.                         + " files. Spooling assumptions violated for " + dest;  
  449.                 throw new IllegalStateException(message);  
  450.             }  
  451.   
  452.             // Dest file exists and not on windows  
  453.         } else if (dest.exists()) {  
  454.             String message = "File name has been re-used with different"  
  455.                     + " files. Spooling assumptions violated for " + dest;  
  456.             throw new IllegalStateException(message);  
  457.   
  458.             // Destination file does not already exist. We are good to go!  
  459.         } else {  
  460.             boolean renamed = fileToRoll.renameTo(dest);  
  461.             if (renamed) {  
  462.                 logger.debug("Successfully rolled file {} to {}", fileToRoll,  
  463.                         dest);  
  464.   
  465.                 // now we no longer need the meta file  
  466.                 deleteMetaFile();  
  467.             } else {  
  468.                 /* 
  469.                  * If we are here then the file cannot be renamed for a reason 
  470.                  * other than that the destination file exists (actually, that 
  471.                  * remains possible w/ small probability due to TOC-TOU 
  472.                  * conditions). 
  473.                  */  
  474.                 String message = "Unable to move "  
  475.                         + fileToRoll  
  476.                         + " to "  
  477.                         + dest  
  478.                         + ". This will likely cause duplicate events. Please verify that "  
  479.                         + "flume has sufficient permissions to perform these operations.";  
  480.                 throw new FlumeException(message);  
  481.             }  
  482.         }  
  483.     }  
  484.   
  485.     /** 
  486.      * Delete the given spooled file 
  487.      *  
  488.      * @param fileToDelete 
  489.      * @throws IOException 
  490.      */  
  491.     private void deleteCurrentFile(File fileToDelete) throws IOException {  
  492.         logger.info("Preparing to delete file {}", fileToDelete);  
  493.         if (!fileToDelete.exists()) {  
  494.             logger.warn("Unable to delete nonexistent file: {}", fileToDelete);  
  495.             return;  
  496.         }  
  497.         if (!fileToDelete.delete()) {  
  498.             throw new IOException("Unable to delete spool file: "  
  499.                     + fileToDelete);  
  500.         }  
  501.         // now we no longer need the meta file  
  502.         deleteMetaFile();  
  503.     }  
  504.   
  505.     /** 
  506.      * Find and open the oldest file in the chosen directory. If two or more 
  507.      * files are equally old, the file name with lower lexicographical value is 
  508.      * returned. If the directory is empty, this will return an absent option. 
  509.      */  
  510.     private Optional getNextFile() {  
  511.         /* Filter to exclude finished or hidden files */  
  512.         FileFilter filter = new FileFilter() {  
  513.             public boolean accept(File candidate) {  
  514.                 String fileName = candidate.getName();  
  515.                 if ((candidate.isDirectory())  
  516.                         || (fileName.endsWith(completedSuffix))  
  517.                         || (fileName.startsWith("."))  
  518.                         || ignorePattern.matcher(fileName).matches()) {  
  519.                     return false;  
  520.                 }  
  521.                 return true;  
  522.             }  
  523.         };  
  524.         List candidateFiles = Arrays.asList(spoolDirectory  
  525.                 .listFiles(filter));  
  526.         if (candidateFiles.isEmpty()) {  
  527.             return Optional.absent();  
  528.         } else {  
  529.             Collections.sort(candidateFiles, new Comparator() {  
  530.                 public int compare(File a, File b) {  
  531.                     int timeComparison = new Long(a.lastModified())  
  532.                             .compareTo(new Long(b.lastModified()));  
  533.                     if (timeComparison != 0) {  
  534.                         return timeComparison;  
  535.                     } else {  
  536.                         return a.getName().compareTo(b.getName());  
  537.                     }  
  538.                 }  
  539.             });  
  540.             File nextFile = candidateFiles.get(0);  
  541.             try {  
  542.                 // roll the meta file, if needed  
  543.                 String nextPath = nextFile.getPath();  
  544.                 PositionTracker tracker = DurablePositionTracker.getInstance(  
  545.                         metaFile, nextPath);  
  546.                 if (!tracker.getTarget().equals(nextPath)) {  
  547.                     tracker.close();  
  548.                     deleteMetaFile();  
  549.                     tracker = DurablePositionTracker.getInstance(metaFile,  
  550.                             nextPath);  
  551.                 }  
  552.   
  553.                 // sanity check  
  554.                 Preconditions  
  555.                         .checkState(  
  556.                                 tracker.getTarget().equals(nextPath),  
  557.                                 "Tracker target %s does not equal expected filename %s",  
  558.                                 tracker.getTarget(), nextPath);  
  559.   
  560.                 ResettableInputStream in = new ResettableFileInputStream(  
  561.                         nextFile, tracker,  
  562.                         ResettableFileInputStream.DEFAULT_BUF_SIZE,  
  563.                         inputCharset, decodeErrorPolicy);  
  564.                 EventDeserializer deserializer = EventDeserializerFactory  
  565.                         .getInstance(deserializerType, deserializerContext, in);  
  566.   
  567.                 return Optional.of(new FileInfo(nextFile, deserializer));  
  568.             } catch (FileNotFoundException e) {  
  569.                 // File could have been deleted in the interim  
  570.                 logger.warn("Could not find file: " + nextFile, e);  
  571.                 return Optional.absent();  
  572.             } catch (IOException e) {  
  573.                 logger.error("Exception opening file: " + nextFile, e);  
  574.                 return Optional.absent();  
  575.             }  
  576.         }  
  577.     }  
  578.   
  579.     private void deleteMetaFile() throws IOException {  
  580.         if (metaFile.exists() && !metaFile.delete()) {  
  581.             throw new IOException("Unable to delete old meta file " + metaFile);  
  582.         }  
  583.     }  
  584.   
  585.     /** An immutable class with information about a file being processed. */  
  586.     private static class FileInfo {  
  587.         private final File file;  
  588.         private final long length;  
  589.         private final long lastModified;  
  590.         private final EventDeserializer deserializer;  
  591.   
  592.         public FileInfo(File file, EventDeserializer deserializer) {  
  593.             this.file = file;  
  594.             this.length = file.length();  
  595.             this.lastModified = file.lastModified();  
  596.             this.deserializer = deserializer;  
  597.         }  
  598.   
  599.         public long getLength() {  
  600.             return length;  
  601.         }  
  602.   
  603.         public long getLastModified() {  
  604.             return lastModified;  
  605.         }  
  606.   
  607.         public EventDeserializer getDeserializer() {  
  608.             return deserializer;  
  609.         }  
  610.   
  611.         public File getFile() {  
  612.             return file;  
  613.         }  
  614.     }  
  615.   
  616.     @InterfaceAudience.Private  
  617.     @InterfaceStability.Unstable  
  618.     static enum DeletePolicy {  
  619.         NEVER, IMMEDIATE, DELAY  
  620.     }  
  621.   
  622.     /** 
  623.      * Special builder class for ReliableSpoolingFileEventReader 
  624.      */  
  625.     public static class Builder {  
  626.         private File spoolDirectory;  
  627.         private String completedSuffix = SpoolDirectorySourceConfigurationExtConstants.SPOOLED_FILE_SUFFIX;  
  628.         private String ignorePattern = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_IGNORE_PAT;  
  629.         private String trackerDirPath = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_TRACKER_DIR;  
  630.         private Boolean annotateFileName = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILE_HEADER;  
  631.         private String fileNameHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_HEADER_KEY;  
  632.         private Boolean annotateBaseName = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_BASENAME_HEADER;  
  633.         private String baseNameHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_BASENAME_HEADER_KEY;  
  634.         private String deserializerType = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_DESERIALIZER;  
  635.         private Context deserializerContext = new Context();  
  636.         private String deletePolicy = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_DELETE_POLICY;  
  637.         private String inputCharset = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_INPUT_CHARSET;  
  638.         private DecodeErrorPolicy decodeErrorPolicy = DecodeErrorPolicy  
  639.                 .valueOf(SpoolDirectorySourceConfigurationExtConstants.DEFAULT_DECODE_ERROR_POLICY  
  640.                         .toUpperCase());  
  641.   
  642.         // 增加代码开始  
  643.   
  644.         private Boolean annotateFileNameExtractor = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR;  
  645.         private String fileNameExtractorHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_HEADER_KEY;  
  646.         private String fileNameExtractorPattern = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_PATTERN;  
  647.         private Boolean convertToTimestamp = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP;  
  648.   
  649.         private String dateTimeFormat = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_DATETIME_FORMAT;  
  650.   
  651.         private Boolean splitFileName = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_SPLIT_FILENAME;  
  652.         private String splitBy = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_SPLITY_BY;  
  653.         private String splitBaseNameHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_SPLIT_BASENAME_HEADER;  
  654.   
  655.         public Builder annotateFileNameExtractor(  
  656.                 Boolean annotateFileNameExtractor) {  
  657.             this.annotateFileNameExtractor = annotateFileNameExtractor;  
  658.             return this;  
  659.         }  
  660.   
  661.         public Builder fileNameExtractorHeader(String fileNameExtractorHeader) {  
  662.             this.fileNameExtractorHeader = fileNameExtractorHeader;  
  663.             return this;  
  664.         }  
  665.   
  666.         public Builder fileNameExtractorPattern(String fileNameExtractorPattern) {  
  667.             this.fileNameExtractorPattern = fileNameExtractorPattern;  
  668.             return this;  
  669.         }  
  670.   
  671.         public Builder convertToTimestamp(Boolean convertToTimestamp) {  
  672.             this.convertToTimestamp = convertToTimestamp;  
  673.             return this;  
  674.         }  
  675.   
  676.         public Builder dateTimeFormat(String dateTimeFormat) {  
  677.             this.dateTimeFormat = dateTimeFormat;  
  678.             return this;  
  679.         }  
  680.   
  681.         public Builder splitFileName(Boolean splitFileName) {  
  682.             this.splitFileName = splitFileName;  
  683.             return this;  
  684.         }  
  685.   
  686.         public Builder splitBy(String splitBy) {  
  687.             this.splitBy = splitBy;  
  688.             return this;  
  689.         }  
  690.   
  691.         public Builder splitBaseNameHeader(String splitBaseNameHeader) {  
  692.             this.splitBaseNameHeader = splitBaseNameHeader;  
  693.             return this;  
  694.         }  
  695.   
  696.         // 增加代码结束  
  697.   
  698.         public Builder spoolDirectory(File directory) {  
  699.             this.spoolDirectory = directory;  
  700.             return this;  
  701.         }  
  702.   
  703.         public Builder completedSuffix(String completedSuffix) {  
  704.             this.completedSuffix = completedSuffix;  
  705.             return this;  
  706.         }  
  707.   
  708.         public Builder ignorePattern(String ignorePattern) {  
  709.             this.ignorePattern = ignorePattern;  
  710.             return this;  
  711.         }  
  712.   
  713.         public Builder trackerDirPath(String trackerDirPath) {  
  714.             this.trackerDirPath = trackerDirPath;  
  715.             return this;  
  716.         }  
  717.   
  718.         public Builder annotateFileName(Boolean annotateFileName) {  
  719.             this.annotateFileName = annotateFileName;  
  720.             return this;  
  721.         }  
  722.   
  723.         public Builder fileNameHeader(String fileNameHeader) {  
  724.             this.fileNameHeader = fileNameHeader;  
  725.             return this;  
  726.         }  
  727.   
  728.         public Builder annotateBaseName(Boolean annotateBaseName) {  
  729.             this.annotateBaseName = annotateBaseName;  
  730.             return this;  
  731.         }  
  732.   
  733.         public Builder baseNameHeader(String baseNameHeader) {  
  734.             this.baseNameHeader = baseNameHeader;  
  735.             return this;  
  736.         }  
  737.   
  738.         public Builder deserializerType(String deserializerType) {  
  739.             this.deserializerType = deserializerType;  
  740.             return this;  
  741.         }  
  742.   
  743.         public Builder deserializerContext(Context deserializerContext) {  
  744.             this.deserializerContext = deserializerContext;  
  745.             return this;  
  746.         }  
  747.   
  748.         public Builder deletePolicy(String deletePolicy) {  
  749.             this.deletePolicy = deletePolicy;  
  750.             return this;  
  751.         }  
  752.   
  753.         public Builder inputCharset(String inputCharset) {  
  754.             this.inputCharset = inputCharset;  
  755.             return this;  
  756.         }  
  757.   
  758.         public Builder decodeErrorPolicy(DecodeErrorPolicy decodeErrorPolicy) {  
  759.             this.decodeErrorPolicy = decodeErrorPolicy;  
  760.             return this;  
  761.         }  
  762.   
  763.         public ReliableSpoolingFileEventExtReader build() throws IOException {  
  764.             return new ReliableSpoolingFileEventExtReader(spoolDirectory,  
  765.                     completedSuffix, ignorePattern, trackerDirPath,  
  766.                     annotateFileName, fileNameHeader, annotateBaseName,  
  767.                     baseNameHeader, deserializerType, deserializerContext,  
  768.                     deletePolicy, inputCharset, decodeErrorPolicy,  
  769.                     annotateFileNameExtractor, fileNameExtractorHeader,  
  770.                     fileNameExtractorPattern, convertToTimestamp,  
  771.                     dateTimeFormat, splitFileName, splitBy, splitBaseNameHeader);  
  772.         }  
  773.     }  
  774.   
  775. }  

[java]  view plain  copy
  1. /* 
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more 
  3.  * contributor license agreements. See the NOTICE file distributed with this 
  4.  * work for additional information regarding copyright ownership. The ASF 
  5.  * licenses this file to you under the Apache License, Version 2.0 (the 
  6.  * "License"); you may not use this file except in compliance with the License. 
  7.  * You may obtain a copy of the License at 
  8.  * 
  9.  * http://www.apache.org/licenses/LICENSE-2.0 
  10.  * 
  11.  * Unless required by applicable law or agreed to in writing, software 
  12.  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
  13.  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
  14.  * License for the specific language governing permissions and limitations under 
  15.  * the License. 
  16.  */  
  17.   
  18. package com.besttone.flume;  
  19.   
  20. import org.apache.flume.serialization.DecodeErrorPolicy;  
  21.   
  22. public class SpoolDirectorySourceConfigurationExtConstants {  
  23.     /** Directory where files are deposited. */  
  24.     public static final String SPOOL_DIRECTORY = "spoolDir";  
  25.   
  26.     /** Suffix appended to files when they are finished being sent. */  
  27.     public static final String SPOOLED_FILE_SUFFIX = "fileSuffix";  
  28.     public static final String DEFAULT_SPOOLED_FILE_SUFFIX = ".COMPLETED";  
  29.   
  30.     /** Header in which to put absolute path filename. */  
  31.     public static final String FILENAME_HEADER_KEY = "fileHeaderKey";  
  32.     public static final String DEFAULT_FILENAME_HEADER_KEY = "file";  
  33.   
  34.     /** Whether to include absolute path filename in a header. */  
  35.     public static final String FILENAME_HEADER = "fileHeader";  
  36.     public static final boolean DEFAULT_FILE_HEADER = false;  
  37.   
  38.     /** Header in which to put the basename of file. */  
  39.     public static final String BASENAME_HEADER_KEY = "basenameHeaderKey";  
  40.     public static final String DEFAULT_BASENAME_HEADER_KEY = "basename";  
  41.   
  42.     /** Whether to include the basename of a file in a header. */  
  43.     public static final String BASENAME_HEADER = "basenameHeader";  
  44.     public static final boolean DEFAULT_BASENAME_HEADER = false;  
  45.   
  46.     /** What size to batch with before sending to ChannelProcessor. */  
  47.     public static final String BATCH_SIZE = "batchSize";  
  48.     public static final int DEFAULT_BATCH_SIZE = 100;  
  49.   
  50.     /** Maximum number of lines to buffer between commits. */  
  51.     @Deprecated  
  52.     public static final String BUFFER_MAX_LINES = "bufferMaxLines";  
  53.     @Deprecated  
  54.     public static final int DEFAULT_BUFFER_MAX_LINES = 100;  
  55.   
  56.     /** Maximum length of line (in characters) in buffer between commits. */  
  57.     @Deprecated  
  58.     public static final String BUFFER_MAX_LINE_LENGTH = "bufferMaxLineLength";  
  59.     @Deprecated  
  60.     public static final int DEFAULT_BUFFER_MAX_LINE_LENGTH = 5000;  
  61.   
  62.     /** Pattern of files to ignore */  
  63.     public static final String IGNORE_PAT = "ignorePattern";  
  64.     public static final String DEFAULT_IGNORE_PAT = "^$"// no effect  
  65.   
  66.     /** Directory to store metadata about files being processed */  
  67.     public static final String TRACKER_DIR = "trackerDir";  
  68.     public static final String DEFAULT_TRACKER_DIR = ".flumespool";  
  69.   
  70.     /** Deserializer to use to parse the file data into Flume Events */  
  71.     public static final String DESERIALIZER = "deserializer";  
  72.     public static final String DEFAULT_DESERIALIZER = "LINE";  
  73.   
  74.     public static final String DELETE_POLICY = "deletePolicy";  
  75.     public static final String DEFAULT_DELETE_POLICY = "never";  
  76.   
  77.     /** Character set used when reading the input. */  
  78.     public static final String INPUT_CHARSET = "inputCharset";  
  79.     public static final String DEFAULT_INPUT_CHARSET = "UTF-8";  
  80.   
  81.     /** What to do when there is a character set decoding error. */  
  82.     public static final String DECODE_ERROR_POLICY = "decodeErrorPolicy";  
  83.     public static final String DEFAULT_DECODE_ERROR_POLICY = DecodeErrorPolicy.FAIL  
  84.             .name();  
  85.   
  86.     public static final String MAX_BACKOFF = "maxBackoff";  
  87.   
  88.     public static final Integer DEFAULT_MAX_BACKOFF = 4000;  
  89.   
  90.     // 增加代码开始  
  91.     public static final String FILENAME_EXTRACTOR = "fileExtractor";  
  92.     public static final boolean DEFAULT_FILENAME_EXTRACTOR = false;  
  93.   
  94.     public static final String FILENAME_EXTRACTOR_HEADER_KEY = "fileExtractorHeaderKey";  
  95.     public static final String DEFAULT_FILENAME_EXTRACTOR_HEADER_KEY = "fileExtractorHeader";  
  96.   
  97.     public static final String FILENAME_EXTRACTOR_PATTERN = "fileExtractorPattern";  
  98.     public static final String DEFAULT_FILENAME_EXTRACTOR_PATTERN = "(.)*";  
  99.   
  100.     public static final String FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP = "convertToTimestamp";  
  101.     public static final boolean DEFAULT_FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP = false;  
  102.   
  103.     public static final String FILENAME_EXTRACTOR_DATETIME_FORMAT = "dateTimeFormat";  
  104.     public static final String DEFAULT_FILENAME_EXTRACTOR_DATETIME_FORMAT = "yyyy-MM-dd";  
  105.   
  106.   
  107.     public static final String SPLIT_FILENAME = "splitFileName";  
  108.     public static final boolean DEFAULT_SPLIT_FILENAME = false;  
  109.   
  110.     public static final String SPLITY_BY = "splitBy";  
  111.     public static final String DEFAULT_SPLITY_BY = "\\.";  
  112.   
  113.     public static final String SPLIT_BASENAME_HEADER = "splitBaseNameHeader";  
  114.     public static final String DEFAULT_SPLIT_BASENAME_HEADER = "fileNameSplit";  
  115.     // 增加代码结束  
  116.   
  117. }  

[java]  view plain  copy
  1. package com.besttone.flume;  
  2.   
  3. import static com.besttone.flume.SpoolDirectorySourceConfigurationExtConstants.*;  
  4.   
  5.   
  6. import java.io.File;  
  7. import java.io.IOException;  
  8. import java.util.List;  
  9. import java.util.concurrent.Executors;  
  10. import java.util.concurrent.ScheduledExecutorService;  
  11. import java.util.concurrent.TimeUnit;  
  12.   
  13. import org.apache.flume.ChannelException;  
  14. import org.apache.flume.Context;  
  15. import org.apache.flume.Event;  
  16. import org.apache.flume.EventDrivenSource;  
  17. import org.apache.flume.FlumeException;  
  18. import org.apache.flume.conf.Configurable;  
  19. import org.apache.flume.instrumentation.SourceCounter;  
  20. import org.apache.flume.serialization.DecodeErrorPolicy;  
  21. import org.apache.flume.serialization.LineDeserializer;  
  22. import org.apache.flume.source.AbstractSource;  
  23. import org.slf4j.Logger;  
  24. import org.slf4j.LoggerFactory;  
  25.   
  26. import com.google.common.annotations.VisibleForTesting;  
  27. import com.google.common.base.Preconditions;  
  28. import com.google.common.base.Throwables;  
  29.   
  30. public class SpoolDirectoryExtSource extends AbstractSource implements  
  31.         Configurable, EventDrivenSource {  
  32.   
  33.     private static final Logger logger = LoggerFactory  
  34.             .getLogger(SpoolDirectoryExtSource.class);  
  35.   
  36.     // Delay used when polling for new files  
  37.     private static final int POLL_DELAY_MS = 500;  
  38.   
  39.     /* Config options */  
  40.     private String completedSuffix;  
  41.     private String spoolDirectory;  
  42.     private boolean fileHeader;  
  43.     private String fileHeaderKey;  
  44.     private boolean basenameHeader;  
  45.     private String basenameHeaderKey;  
  46.     private int batchSize;  
  47.     private String ignorePattern;  
  48.     private String trackerDirPath;  
  49.     private String deserializerType;  
  50.     private Context deserializerContext;  
  51.     private String deletePolicy;  
  52.     private String inputCharset;  
  53.     private DecodeErrorPolicy decodeErrorPolicy;  
  54.     private volatile boolean hasFatalError = false;  
  55.   
  56.     private SourceCounter sourceCounter;  
  57.     ReliableSpoolingFileEventExtReader reader;  
  58.     private ScheduledExecutorService executor;  
  59.     private boolean backoff = true;  
  60.     private boolean hitChannelException = false;  
  61.     private int maxBackoff;  
  62.   
  63.     // 增加代码开始  
  64.   
  65.     private Boolean annotateFileNameExtractor;  
  66.     private String fileNameExtractorHeader;  
  67.     private String fileNameExtractorPattern;  
  68.     private Boolean convertToTimestamp;  
  69.   
  70.     private String dateTimeFormat;  
  71.       
  72.     private boolean splitFileName;  
  73.     private  String splitBy;  
  74.     private  String splitBaseNameHeader;  
  75.   
  76.     // 增加代码结束  
  77.   
  78.     @Override  
  79.     public synchronized void start() {  
  80.         logger.info("SpoolDirectorySource source starting with directory: {}",  
  81.                 spoolDirectory);  
  82.   
  83.         executor = Executors.newSingleThreadScheduledExecutor();  
  84.   
  85.         File directory = new File(spoolDirectory);  
  86.         try {  
  87.             reader = new ReliableSpoolingFileEventExtReader.Builder()  
  88.                     .spoolDirectory(directory).completedSuffix(completedSuffix)  
  89.                     .ignorePattern(ignorePattern)  
  90.                     .trackerDirPath(trackerDirPath)  
  91.                     .annotateFileName(fileHeader).fileNameHeader(fileHeaderKey)  
  92.                     .annotateBaseName(basenameHeader)  
  93.                     .baseNameHeader(basenameHeaderKey)  
  94.                     .deserializerType(deserializerType)  
  95.                     .deserializerContext(deserializerContext)  
  96.                     .deletePolicy(deletePolicy).inputCharset(inputCharset)  
  97.                     .decodeErrorPolicy(decodeErrorPolicy)  
  98.                     .annotateFileNameExtractor(annotateFileNameExtractor)  
  99.                     .fileNameExtractorHeader(fileNameExtractorHeader)  
  100.                     .fileNameExtractorPattern(fileNameExtractorPattern)  
  101.                     .convertToTimestamp(convertToTimestamp)  
  102.                     .dateTimeFormat(dateTimeFormat)  
  103.                     .splitFileName(splitFileName).splitBy(splitBy)  
  104.                     .splitBaseNameHeader(splitBaseNameHeader).build();  
  105.         } catch (IOException ioe) {  
  106.             throw new FlumeException(  
  107.                     "Error instantiating spooling event parser", ioe);  
  108.         }  
  109.   
  110.         Runnable runner = new SpoolDirectoryRunnable(reader, sourceCounter);  
  111.         executor.scheduleWithFixedDelay(runner, 0, POLL_DELAY_MS,  
  112.                 TimeUnit.MILLISECONDS);  
  113.   
  114.         super.start();  
  115.         logger.debug("SpoolDirectorySource source started");  
  116.         sourceCounter.start();  
  117.     }  
  118.   
  119.     @Override  
  120.     public synchronized void stop() {  
  121.         executor.shutdown();  
  122.         try {  
  123.             executor.awaitTermination(10L, TimeUnit.SECONDS);  
  124.         } catch (InterruptedException ex) {  
  125.             logger.info("Interrupted while awaiting termination", ex);  
  126.         }  
  127.         executor.shutdownNow();  
  128.   
  129.         super.stop();  
  130.         sourceCounter.stop();  
  131.         logger.info("SpoolDir source {} stopped. Metrics: {}", getName(),  
  132.                 sourceCounter);  
  133.     }  
  134.   
  135.     @Override  
  136.     public String toString() {  
  137.         return "Spool Directory source " + getName() + ": { spoolDir: "  
  138.                 + spoolDirectory + " }";  
  139.     }  
  140.   
  141.     @Override  
  142.     public synchronized void configure(Context context) {  
  143.         spoolDirectory = context.getString(SPOOL_DIRECTORY);  
  144.         Preconditions.checkState(spoolDirectory != null,  
  145.                 "Configuration must specify a spooling directory");  
  146.   
  147.         completedSuffix = context.getString(SPOOLED_FILE_SUFFIX,  
  148.                 DEFAULT_SPOOLED_FILE_SUFFIX);  
  149.         deletePolicy = context.getString(DELETE_POLICY, DEFAULT_DELETE_POLICY);  
  150.         fileHeader = context.getBoolean(FILENAME_HEADER, DEFAULT_FILE_HEADER);  
  151.         fileHeaderKey = context.getString(FILENAME_HEADER_KEY,  
  152.                 DEFAULT_FILENAME_HEADER_KEY);  
  153.         basenameHeader = context.getBoolean(BASENAME_HEADER,  
  154.                 DEFAULT_BASENAME_HEADER);  
  155.         basenameHeaderKey = context.getString(BASENAME_HEADER_KEY,  
  156.                 DEFAULT_BASENAME_HEADER_KEY);  
  157.         batchSize = context.getInteger(BATCH_SIZE, DEFAULT_BATCH_SIZE);  
  158.         inputCharset = context.getString(INPUT_CHARSET, DEFAULT_INPUT_CHARSET);  
  159.         decodeErrorPolicy = DecodeErrorPolicy  
  160.                 .valueOf(context.getString(DECODE_ERROR_POLICY,  
  161.                         DEFAULT_DECODE_ERROR_POLICY).toUpperCase());  
  162.   
  163.         ignorePattern = context.getString(IGNORE_PAT, DEFAULT_IGNORE_PAT);  
  164.         trackerDirPath = context.getString(TRACKER_DIR, DEFAULT_TRACKER_DIR);  
  165.   
  166.         deserializerType = context  
  167.                 .getString(DESERIALIZER, DEFAULT_DESERIALIZER);  
  168.         deserializerContext = new Context(context.getSubProperties(DESERIALIZER  
  169.                 + "."));  
  170.   
  171.         // "Hack" to support backwards compatibility with previous generation of  
  172.         // spooling directory source, which did not support deserializers  
  173.         Integer bufferMaxLineLength = context  
  174.                 .getInteger(BUFFER_MAX_LINE_LENGTH);  
  175.         if (bufferMaxLineLength != null && deserializerType != null  
  176.                 && deserializerType.equalsIgnoreCase(DEFAULT_DESERIALIZER)) {  
  177.             deserializerContext.put(LineDeserializer.MAXLINE_KEY,  
  178.                     bufferMaxLineLength.toString());  
  179.         }  
  180.   
  181.         maxBackoff = context.getInteger(MAX_BACKOFF, DEFAULT_MAX_BACKOFF);  
  182.         if (sourceCounter == null) {  
  183.             sourceCounter = new SourceCounter(getName());  
  184.         }  
  185.           
  186.         //增加代码开始  
  187.   
  188.         annotateFileNameExtractor=context.getBoolean(FILENAME_EXTRACTOR, DEFAULT_FILENAME_EXTRACTOR);  
  189.         fileNameExtractorHeader=context.getString(FILENAME_EXTRACTOR_HEADER_KEY, DEFAULT_FILENAME_EXTRACTOR_HEADER_KEY);  
  190.         fileNameExtractorPattern=context.getString(FILENAME_EXTRACTOR_PATTERN,DEFAULT_FILENAME_EXTRACTOR_PATTERN);  
  191.         convertToTimestamp=context.getBoolean(FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP, DEFAULT_FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP);  
  192.         dateTimeFormat=context.getString(FILENAME_EXTRACTOR_DATETIME_FORMAT, DEFAULT_FILENAME_EXTRACTOR_DATETIME_FORMAT);  
  193.           
  194.         splitFileName=context.getBoolean(SPLIT_FILENAME, DEFAULT_SPLIT_FILENAME);  
  195.         splitBy=context.getString(SPLITY_BY, DEFAULT_SPLITY_BY);  
  196.         splitBaseNameHeader=context.getString(SPLIT_BASENAME_HEADER, DEFAULT_SPLIT_BASENAME_HEADER);  
  197.           
  198.           
  199.           
  200.         //增加代码结束   
  201.     }  
  202.   
  203.     @VisibleForTesting  
  204.     protected boolean hasFatalError() {  
  205.         return hasFatalError;  
  206.     }  
  207.   
  208.     /** 
  209.      * The class always backs off, this exists only so that we can test without 
  210.      * taking a really long time. 
  211.      *  
  212.      * @param backoff 
  213.      *            - whether the source should backoff if the channel is full 
  214.      */  
  215.     @VisibleForTesting  
  216.     protected void setBackOff(boolean backoff) {  
  217.         this.backoff = backoff;  
  218.     }  
  219.   
  220.     @VisibleForTesting  
  221.     protected boolean hitChannelException() {  
  222.         return hitChannelException;  
  223.     }  
  224.   
  225.     @VisibleForTesting  
  226.     protected SourceCounter getSourceCounter() {  
  227.         return sourceCounter;  
  228.     }  
  229.   
  230.     private class SpoolDirectoryRunnable implements Runnable {  
  231.         private ReliableSpoolingFileEventExtReader reader;  
  232.         private SourceCounter sourceCounter;  
  233.   
  234.         public SpoolDirectoryRunnable(  
  235.                 ReliableSpoolingFileEventExtReader reader,  
  236.                 SourceCounter sourceCounter) {  
  237.             this.reader = reader;  
  238.             this.sourceCounter = sourceCounter;  
  239.         }  
  240.   
  241.         @Override  
  242.         public void run() {  
  243.             int backoffInterval = 250;  
  244.             try {  
  245.                 while (!Thread.interrupted()) {  
  246.                     List events = reader.readEvents(batchSize);  
  247.                     if (events.isEmpty()) {  
  248.                         break;  
  249.                     }  
  250.                     sourceCounter.addToEventReceivedCount(events.size());  
  251.                     sourceCounter.incrementAppendBatchReceivedCount();  
  252.   
  253.                     try {  
  254.                         getChannelProcessor().processEventBatch(events);  
  255.                         reader.commit();  
  256.                     } catch (ChannelException ex) {  
  257.                         logger.warn("The channel is full, and cannot write data now. The "  
  258.                                 + "source will try again after "  
  259.                                 + String.valueOf(backoffInterval)  
  260.                                 + " milliseconds");  
  261.                         hitChannelException = true;  
  262.                         if (backoff) {  
  263.                             TimeUnit.MILLISECONDS.sleep(backoffInterval);  
  264.                             backoffInterval = backoffInterval << 1;  
  265.                             backoffInterval = backoffInterval >= maxBackoff ? maxBackoff  
  266.                                     : backoffInterval;  
  267.                         }  
  268.                         continue;  
  269.                     }  
  270.                     backoffInterval = 250;  
  271.                     sourceCounter.addToEventAcceptedCount(events.size());  
  272.                     sourceCounter.incrementAppendBatchAcceptedCount();  
  273.                 }  
  274.                 logger.info("Spooling Directory Source runner has shutdown.");  
  275.             } catch (Throwable t) {  
  276.                 logger.error(  
  277.                         "FATAL: "  
  278.                                 + SpoolDirectoryExtSource.this.toString()  
  279.                                 + ": "  
  280.                                 + "Uncaught exception in SpoolDirectorySource thread. "  
  281.                                 + "Restart or reconfigure Flume to continue processing.",  
  282.                         t);  
  283.                 hasFatalError = true;  
  284.                 Throwables.propagate(t);  
  285.             }  
  286.         }  
  287.     }  
  288. }  

主要提供了如下几个设置参数:

tier1.sources.source1.type=com.besttone.flume.SpoolDirectoryExtSource   #写类的全路径名
tier1.sources.source1.spoolDir=/opt/logs   #监控的目录
tier1.sources.source1.splitFileName=true   #是否分隔文件名,并把分割后的内容添加到header中,默认false
tier1.sources.source1.splitBy=\\.                   #以什么符号分隔,默认是"."分隔
tier1.sources.source1.splitBaseNameHeader=fileNameSplit  #分割后写入header的key的前缀,比如a.log.2014-07-31,按“."分隔,

则header中有fileNameSplit0=a,fileNameSplit1=log,fileNameSplit2=2014-07-31


(其中还有扩展一个通过正则表达式抽取文件名的功能也在里面,我们这里不用到,就不介绍了)

扩展了这个source之后,前面的那个需求就很容易实现了,只需要:

tier1.sinks.sink1.hdfs.path=hdfs://master68:8020/flume/events/%{fileNameSplit0}/%{fileNameSplit2}

a.log.2014-07-31这个文件的内容就会保存到hdfs://master68:8020/flume/events/a/2014-07-31目录下面去了。


接下来我们说说如何部署这个我们扩展的自定义的spooling directory source(基于CM的设置)。

首先,我们把上面三个类打成JAR包:SpoolDirectoryExtSource.jar
CM的flume插件目录为:/var/lib/flume-ng/plugins.d

然后再你需要使用这个source的agent上的/var/lib/flume-ng/plugins.d目录下面创建SpoolDirectoryExtSource目录以及子目录lib,libext,native,lib是放插件JAR的目录,libext是放插件的依赖JAR的目录,native放使用到的原生库(如果有用到的话)。

我们这里没有使用到其他的依赖,于是就把SpoolDirectoryExtSource.jar放到lib目录下就好了,最终的目录结构:

plugins.d/
plugins.d/SpoolDirectoryExtSource/
plugins.d/SpoolDirectoryExtSource/lib/SpoolDirectoryExtSource.jar
plugins.d/SpoolDirectoryExtSource/libext/
plugins.d/SpoolDirectoryExtSource/native/
重新启动flume agent,flume就会自动装载我们的插件,这样在flume.conf中就可以使用全路径类名配置type属性了。

最终flume.conf配置如下:

[plain]  view plain  copy
  1. tier1.sources=source1  
  2. tier1.channels=channel1  
  3. tier1.sinks=sink1  
  4. tier1.sources.source1.type=com.besttone.flume.SpoolDirectoryExtSource  
  5. tier1.sources.source1.spoolDir=/opt/logs  
  6. tier1.sources.source1.splitFileName=true  
  7. tier1.sources.source1.splitBy=\\.  
  8. tier1.sources.source1.splitBaseNameHeader=fileNameSplit  
  9. tier1.sources.source1.channels=channel1  
  10. tier1.sinks.sink1.type=hdfs  
  11. tier1.sinks.sink1.channel=channel1  
  12. tier1.sinks.sink1.hdfs.path=hdfs://master68:8020/flume/events/%{fileNameSplit0}/%{fileNameSplit2}  
  13. tier1.sinks.sink1.hdfs.round=true  
  14. tier1.sinks.sink1.hdfs.roundValue=10  
  15. tier1.sinks.sink1.hdfs.roundUnit=minute  
  16. tier1.sinks.sink1.hdfs.fileType=DataStream  
  17. tier1.sinks.sink1.hdfs.writeFormat=Text  
  18. tier1.sinks.sink1.hdfs.rollInterval=0  
  19. tier1.sinks.sink1.hdfs.rollSize=10240  
  20. tier1.sinks.sink1.hdfs.rollCount=0  
  21. tier1.sinks.sink1.hdfs.idleTimeout=60  
  22. tier1.channels.channel1.type=memory  
  23. tier1.channels.channel1.capacity=10000  
  24. tier1.channels.channel1.transactionCapacity=1000  
  25. tier1.channels.channel1.keep-alive=30  

附上一张用logger作为sink的查看日志文件的截图:flume 自定义source,sink,channel,拦截器_第1张图片



问题导读:
1.如何实现flume端自定一个sink,来按照我们的规则来保存日志?
2.想从flume的配置文件中获取rootPath的值,该如何配置?






最近需要利用flume来做收集远端日志,所以学习一些flume最基本的用法。这里仅作记录。

远端日志收集的整体思路是远端自定义实现log4j的appender把消息发送到flume端,flume端自定义实现一个sink来按照我们的规则保存日志。

自定义Sink代码:

  1. public class LocalFileLogSink extends AbstractSink implements Configurable {
  2.      private static final Logger logger = LoggerFactory
  3.               . getLogger(LocalFileLogSink .class );
  4.             private static final String PROP_KEY_ROOTPATH = "rootPath";
  5.       private String rootPath;
  6.      @Override
  7.      public void configure(Context context) {
  8.           String rootPath = context.getString(PROP_KEY_ROOTPATH );
  9.           setRootPath(rootPath);
  10.      }
  11.            
  12.           @Override
  13.           public Status process() throws EventDeliveryException {
  14.            logger .debug("Do process" );
  15.        }
  16. }
复制代码
实现Configurable接口,即可在初始化时,通过configure方法从context中获取配置的参数的值。这里,我们是想从flume的配置文件中获取rootPath的值,也就是日志保存的根路径。在flume-conf.properties中配置如下:
  1. agent.sinks = loggerSink
  2. agent.sinks.loggerSink.rootPath = ./logs
复制代码

loggerSink是自定义sink的名称,我们取值时的key,只需要loggerSink后面的部分即可,即这里的rootPath。

实际业务逻辑的执行,是通过继承复写AbstractSink中的process方法实现。从基类的getChannel方法中获取信道,从中取出Event处理即可。

  1. Channel ch = getChannel();
  2.            Transaction txn = ch.getTransaction();
  3.          txn.begin();
  4.           try {
  5.               logger .debug("Get event." );
  6.              Event event = ch.take();
  7.              txn.commit();
  8.              status = Status. READY ;
  9.              return status;
  10.                    }finally {
  11.              Log. info( "trx close.");
  12.              txn.close();
  13.          }

以下是我的自定义kafka sink插件的pom文件,编译成jar包丢到flume的lib下即可使用

[html]  view plain  copy
  1. xml version="1.0" encoding="UTF-8"?>  
  2.   
  3.   
  4. <project xmlns="http://maven.apache.org/POM/4.0.0"  
  5.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  7.   <modelVersion>4.0.0modelVersion>  
  8.     
  9.   <groupId>flume-sinksgroupId>  
  10.   <artifactId>cmcc-kafka-sinkartifactId>  
  11.   <name>Flume Kafka Sinkname>  
  12.   <version>1.0.0version>  
  13.   <build>  
  14.     <plugins>  
  15.       <plugin>  
  16.         <groupId>org.apache.maven.pluginsgroupId>  
  17.         <artifactId>maven-jar-pluginartifactId>  
  18.       plugin>  
  19.     plugins>  
  20.   build>  

你可能感兴趣的:(大数据)