flume學習(七)、(八):如何使用event header中的key值以及自定義source

問題導讀

1.如何使用event header中的key值?
2.如何部署擴展自定義的spooling directory source?







前面我們已經說到我們在header中添加了一個key爲:flume.client.log4j.logger.source  ,然後有兩個應用程序,一個設置爲app1,一個設置爲app2。

現在有這麼一個需求,要將app1的日誌輸出到hdfs://master68:8020/flume/events/app1目錄下面,app2的日誌輸出到hdfs://master68:8020/flume/events/app2目錄下面,未來也可能有更多的應用程序的日誌輸出,也即每個程序的日誌輸出到各自自己的目錄下面。

有了前面的頭信息之後,我們可以非常簡單的實現這個需求:

tier1.sinks.sink1.hdfs.path=hdfs://master68:8020/flume/events/%{flume.client.log4j.logger.source}

只需要改一下這一行,用%{flume.client.log4j.logger.source},來替換具體的app日誌目錄即可。

按照以往的慣例,還是需求驅動學習,有位網友在我的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)代碼如下:
  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. package com.besttone.flume;

  20. import java.io.File;
  21. import java.io.FileFilter;
  22. import java.io.FileNotFoundException;
  23. import java.io.IOException;
  24. import java.nio.charset.Charset;
  25. import java.util.Arrays;
  26. import java.util.Collections;
  27. import java.util.Comparator;
  28. import java.util.List;
  29. import java.util.regex.Matcher;
  30. import java.util.regex.Pattern;

  31. import org.apache.flume.Context;
  32. import org.apache.flume.Event;
  33. import org.apache.flume.FlumeException;
  34. import org.apache.flume.annotations.InterfaceAudience;
  35. import org.apache.flume.annotations.InterfaceStability;
  36. import org.apache.flume.client.avro.ReliableEventReader;
  37. import org.apache.flume.serialization.DecodeErrorPolicy;
  38. import org.apache.flume.serialization.DurablePositionTracker;
  39. import org.apache.flume.serialization.EventDeserializer;
  40. import org.apache.flume.serialization.EventDeserializerFactory;
  41. import org.apache.flume.serialization.PositionTracker;
  42. import org.apache.flume.serialization.ResettableFileInputStream;
  43. import org.apache.flume.serialization.ResettableInputStream;
  44. import org.apache.flume.tools.PlatformDetect;
  45. import org.joda.time.DateTime;
  46. import org.joda.time.format.DateTimeFormat;
  47. import org.joda.time.format.DateTimeFormatter;
  48. import org.slf4j.Logger;
  49. import org.slf4j.LoggerFactory;

  50. import com.google.common.base.Charsets;
  51. import com.google.common.base.Optional;
  52. import com.google.common.base.Preconditions;
  53. import com.google.common.io.Files;

  54. /**
  55. * <p/>
  56. * A {@link ReliableEventReader} which reads log data from files stored in a
  57. * spooling directory and renames each file once all of its data has been read
  58. * (through {@link EventDeserializer#readEvent()} calls). The user must
  59. * {@link #commit()} each read, to indicate that the lines have been fully
  60. * processed.
  61. * <p/>
  62. * Read calls will return no data if there are no files left to read. This
  63. * class, in general, is not thread safe.
  64. * 
  65. * <p/>
  66. * This reader assumes that files with unique file names are left in the
  67. * spooling directory and not modified once they are placed there. Any user
  68. * behavior which violates these assumptions, when detected, will result in a
  69. * FlumeException being thrown.
  70. * 
  71. * <p/>
  72. * This class makes the following guarantees, if above assumptions are met:
  73. * <ul>
  74. * <li>Once a log file has been renamed with the {@link #completedSuffix}, all
  75. * of its records have been read through the
  76. * {@link EventDeserializer#readEvent()} function and {@link #commit()}ed at
  77. * least once.
  78. * <li>All files in the spooling directory will eventually be opened and
  79. * delivered to a {@link #readEvents(int)} caller.
  80. * </ul>
  81. */
  82. @InterfaceAudience.Private
  83. @InterfaceStability.Evolving
  84. public class ReliableSpoolingFileEventExtReader implements ReliableEventReader {

  85.         private static final Logger logger = LoggerFactory
  86.                         .getLogger(ReliableSpoolingFileEventExtReader.class);

  87.         static final String metaFileName = ".flumespool-main.meta";

  88.         private final File spoolDirectory;
  89.         private final String completedSuffix;
  90.         private final String deserializerType;
  91.         private final Context deserializerContext;
  92.         private final Pattern ignorePattern;
  93.         private final File metaFile;
  94.         private final boolean annotateFileName;
  95.         private final boolean annotateBaseName;
  96.         private final String fileNameHeader;
  97.         private final String baseNameHeader;

  98.         // 添加參數開始
  99.         private final boolean annotateFileNameExtractor;
  100.         private final String fileNameExtractorHeader;
  101.         private final Pattern fileNameExtractorPattern;
  102.         private final boolean convertToTimestamp;
  103.         private final String dateTimeFormat;

  104.         private final boolean splitFileName;
  105.         private final String splitBy;
  106.         private final String splitBaseNameHeader;
  107.         // 添加參數結束

  108.         private final String deletePolicy;
  109.         private final Charset inputCharset;
  110.         private final DecodeErrorPolicy decodeErrorPolicy;

  111.         private Optional<FileInfo> currentFile = Optional.absent();
  112.         /** Always contains the last file from which lines have been read. **/
  113.         private Optional<FileInfo> lastFileRead = Optional.absent();
  114.         private boolean committed = true;

  115.         /**
  116.          * Create a ReliableSpoolingFileEventReader to watch the given directory.
  117.          */
  118.         private ReliableSpoolingFileEventExtReader(File spoolDirectory,
  119.                         String completedSuffix, String ignorePattern,
  120.                         String trackerDirPath, boolean annotateFileName,
  121.                         String fileNameHeader, boolean annotateBaseName,
  122.                         String baseNameHeader, String deserializerType,
  123.                         Context deserializerContext, String deletePolicy,
  124.                         String inputCharset, DecodeErrorPolicy decodeErrorPolicy,
  125.                         boolean annotateFileNameExtractor, String fileNameExtractorHeader,
  126.                         String fileNameExtractorPattern, boolean convertToTimestamp,
  127.                         String dateTimeFormat, boolean splitFileName, String splitBy,
  128.                         String splitBaseNameHeader) throws IOException {

  129.                 // Sanity checks
  130.                 Preconditions.checkNotNull(spoolDirectory);
  131.                 Preconditions.checkNotNull(completedSuffix);
  132.                 Preconditions.checkNotNull(ignorePattern);
  133.                 Preconditions.checkNotNull(trackerDirPath);
  134.                 Preconditions.checkNotNull(deserializerType);
  135.                 Preconditions.checkNotNull(deserializerContext);
  136.                 Preconditions.checkNotNull(deletePolicy);
  137.                 Preconditions.checkNotNull(inputCharset);

  138.                 // validate delete policy
  139.                 if (!deletePolicy.equalsIgnoreCase(DeletePolicy.NEVER.name())
  140.                                 && !deletePolicy
  141.                                                 .equalsIgnoreCase(DeletePolicy.IMMEDIATE.name())) {
  142.                         throw new IllegalArgumentException("Delete policies other than "
  143.                                         + "NEVER and IMMEDIATE are not yet supported");
  144.                 }

  145.                 if (logger.isDebugEnabled()) {
  146.                         logger.debug("Initializing {} with directory={}, metaDir={}, "
  147.                                         + "deserializer={}", new Object[] {
  148.                                         ReliableSpoolingFileEventExtReader.class.getSimpleName(),
  149.                                         spoolDirectory, trackerDirPath, deserializerType });
  150.                 }

  151.                 // Verify directory exists and is readable/writable
  152.                 Preconditions
  153.                                 .checkState(
  154.                                                 spoolDirectory.exists(),
  155.                                                 "Directory does not exist: "
  156.                                                                 + spoolDirectory.getAbsolutePath());
  157.                 Preconditions.checkState(spoolDirectory.isDirectory(),
  158.                                 "Path is not a directory: " + spoolDirectory.getAbsolutePath());

  159.                 // Do a canary test to make sure we have access to spooling directory
  160.                 try {
  161.                         File canary = File.createTempFile("flume-spooldir-perm-check-",
  162.                                         ".canary", spoolDirectory);
  163.                         Files.write("testing flume file permissions\n", canary,
  164.                                         Charsets.UTF_8);
  165.                         List<String> lines = Files.readLines(canary, Charsets.UTF_8);
  166.                         Preconditions.checkState(!lines.isEmpty(), "Empty canary file %s",
  167.                                         canary);
  168.                         if (!canary.delete()) {
  169.                                 throw new IOException("Unable to delete canary file " + canary);
  170.                         }
  171.                         logger.debug("Successfully created and deleted canary file: {}",
  172.                                         canary);
  173.                 } catch (IOException e) {
  174.                         throw new FlumeException("Unable to read and modify files"
  175.                                         + " in the spooling directory: " + spoolDirectory, e);
  176.                 }

  177.                 this.spoolDirectory = spoolDirectory;
  178.                 this.completedSuffix = completedSuffix;
  179.                 this.deserializerType = deserializerType;
  180.                 this.deserializerContext = deserializerContext;
  181.                 this.annotateFileName = annotateFileName;
  182.                 this.fileNameHeader = fileNameHeader;
  183.                 this.annotateBaseName = annotateBaseName;
  184.                 this.baseNameHeader = baseNameHeader;
  185.                 this.ignorePattern = Pattern.compile(ignorePattern);
  186.                 this.deletePolicy = deletePolicy;
  187.                 this.inputCharset = Charset.forName(inputCharset);
  188.                 this.decodeErrorPolicy = Preconditions.checkNotNull(decodeErrorPolicy);

  189.                 // 增加代碼開始
  190.                 this.annotateFileNameExtractor = annotateFileNameExtractor;
  191.                 this.fileNameExtractorHeader = fileNameExtractorHeader;
  192.                 this.fileNameExtractorPattern = Pattern
  193.                                 .compile(fileNameExtractorPattern);
  194.                 this.convertToTimestamp = convertToTimestamp;
  195.                 this.dateTimeFormat = dateTimeFormat;

  196.                 this.splitFileName = splitFileName;
  197.                 this.splitBy = splitBy;
  198.                 this.splitBaseNameHeader = splitBaseNameHeader;
  199.                 // 增加代碼結束

  200.                 File trackerDirectory = new File(trackerDirPath);

  201.                 // if relative path, treat as relative to spool directory
  202.                 if (!trackerDirectory.isAbsolute()) {
  203.                         trackerDirectory = new File(spoolDirectory, trackerDirPath);
  204.                 }

  205.                 // ensure that meta directory exists
  206.                 if (!trackerDirectory.exists()) {
  207.                         if (!trackerDirectory.mkdir()) {
  208.                                 throw new IOException(
  209.                                                 "Unable to mkdir nonexistent meta directory "
  210.                                                                 + trackerDirectory);
  211.                         }
  212.                 }

  213.                 // ensure that the meta directory is a directory
  214.                 if (!trackerDirectory.isDirectory()) {
  215.                         throw new IOException("Specified meta directory is not a directory"
  216.                                         + trackerDirectory);
  217.                 }

  218.                 this.metaFile = new File(trackerDirectory, metaFileName);
  219.         }

  220.         /**
  221.          * Return the filename which generated the data from the last successful
  222.          * {@link #readEvents(int)} call. Returns null if called before any file
  223.          * contents are read.
  224.          */
  225.         public String getLastFileRead() {
  226.                 if (!lastFileRead.isPresent()) {
  227.                         return null;
  228.                 }
  229.                 return lastFileRead.get().getFile().getAbsolutePath();
  230.         }

  231.         // public interface
  232.         public Event readEvent() throws IOException {
  233.                 List<Event> events = readEvents(1);
  234.                 if (!events.isEmpty()) {
  235.                         return events.get(0);
  236.                 } else {
  237.                         return null;
  238.                 }
  239.         }

  240.         public List<Event> readEvents(int numEvents) throws IOException {
  241.                 if (!committed) {
  242.                         if (!currentFile.isPresent()) {
  243.                                 throw new IllegalStateException("File should not roll when "
  244.                                                 + "commit is outstanding.");
  245.                         }
  246.                         logger.info("Last read was never committed - resetting mark position.");
  247.                         currentFile.get().getDeserializer().reset();
  248.                 } else {
  249.                         // Check if new files have arrived since last call
  250.                         if (!currentFile.isPresent()) {
  251.                                 currentFile = getNextFile();
  252.                         }
  253.                         // Return empty list if no new files
  254.                         if (!currentFile.isPresent()) {
  255.                                 return Collections.emptyList();
  256.                         }
  257.                 }

  258.                 EventDeserializer des = currentFile.get().getDeserializer();
  259.                 List<Event> events = des.readEvents(numEvents);

  260.                 /*
  261.                  * It's possible that the last read took us just up to a file boundary.
  262.                  * If so, try to roll to the next file, if there is one.
  263.                  */
  264.                 if (events.isEmpty()) {
  265.                         retireCurrentFile();
  266.                         currentFile = getNextFile();
  267.                         if (!currentFile.isPresent()) {
  268.                                 return Collections.emptyList();
  269.                         }
  270.                         events = currentFile.get().getDeserializer().readEvents(numEvents);
  271.                 }

  272.                 if (annotateFileName) {
  273.                         String filename = currentFile.get().getFile().getAbsolutePath();
  274.                         for (Event event : events) {
  275.                                 event.getHeaders().put(fileNameHeader, filename);
  276.                         }
  277.                 }

  278.                 if (annotateBaseName) {
  279.                         String basename = currentFile.get().getFile().getName();
  280.                         for (Event event : events) {
  281.                                 event.getHeaders().put(baseNameHeader, basename);
  282.                         }
  283.                 }

  284.                 // 增加代碼開始

  285.                 // 按正則抽取文件名的內容
  286.                 if (annotateFileNameExtractor) {

  287.                         Matcher matcher = fileNameExtractorPattern.matcher(currentFile
  288.                                         .get().getFile().getName());

  289.                         if (matcher.find()) {
  290.                                 String value = matcher.group();
  291.                                 if (convertToTimestamp) {
  292.                                         DateTimeFormatter formatter = DateTimeFormat
  293.                                                         .forPattern(dateTimeFormat);
  294.                                         DateTime dateTime = formatter.parseDateTime(value);

  295.                                         value = Long.toString(dateTime.getMillis());
  296.                                 }

  297.                                 for (Event event : events) {
  298.                                         event.getHeaders().put(fileNameExtractorHeader, value);
  299.                                 }
  300.                         }

  301.                 }

  302.                 // 按分隔符拆分文件名
  303.                 if (splitFileName) {
  304.                         String[] splits = currentFile.get().getFile().getName()
  305.                                         .split(splitBy);

  306.                         for (Event event : events) {
  307.                                 for (int i = 0; i < splits.length; i++) {
  308.                                         event.getHeaders().put(splitBaseNameHeader + i, splits[i]);
  309.                                 }

  310.                         }

  311.                 }

  312.                 // 增加代碼結束

  313.                 committed = false;
  314.                 lastFileRead = currentFile;
  315.                 return events;
  316.         }

  317.         @Override
  318.         public void close() throws IOException {
  319.                 if (currentFile.isPresent()) {
  320.                         currentFile.get().getDeserializer().close();
  321.                         currentFile = Optional.absent();
  322.                 }
  323.         }

  324.         /** Commit the last lines which were read. */
  325.         @Override
  326.         public void commit() throws IOException {
  327.                 if (!committed && currentFile.isPresent()) {
  328.                         currentFile.get().getDeserializer().mark();
  329.                         committed = true;
  330.                 }
  331.         }

  332.         /**
  333.          * Closes currentFile and attempt to rename it.
  334.          * 
  335.          * If these operations fail in a way that may cause duplicate log entries,
  336.          * an error is logged but no exceptions are thrown. If these operations fail
  337.          * in a way that indicates potential misuse of the spooling directory, a
  338.          * FlumeException will be thrown.
  339.          * 
  340.          * @throws FlumeException
  341.          *             if files do not conform to spooling assumptions
  342.          */
  343.         private void retireCurrentFile() throws IOException {
  344.                 Preconditions.checkState(currentFile.isPresent());

  345.                 File fileToRoll = new File(currentFile.get().getFile()
  346.                                 .getAbsolutePath());

  347.                 currentFile.get().getDeserializer().close();

  348.                 // Verify that spooling assumptions hold
  349.                 if (fileToRoll.lastModified() != currentFile.get().getLastModified()) {
  350.                         String message = "File has been modified since being read: "
  351.                                         + fileToRoll;
  352.                         throw new IllegalStateException(message);
  353.                 }
  354.                 if (fileToRoll.length() != currentFile.get().getLength()) {
  355.                         String message = "File has changed size since being read: "
  356.                                         + fileToRoll;
  357.                         throw new IllegalStateException(message);
  358.                 }

  359.                 if (deletePolicy.equalsIgnoreCase(DeletePolicy.NEVER.name())) {
  360.                         rollCurrentFile(fileToRoll);
  361.                 } else if (deletePolicy.equalsIgnoreCase(DeletePolicy.IMMEDIATE.name())) {
  362.                         deleteCurrentFile(fileToRoll);
  363.                 } else {
  364.                         // TODO: implement delay in the future
  365.                         throw new IllegalArgumentException("Unsupported delete policy: "
  366.                                         + deletePolicy);
  367.                 }
  368.         }

  369.         /**
  370.          * Rename the given spooled file
  371.          * 
  372.          * @param fileToRoll
  373.          * @throws IOException
  374.          */
  375.         private void rollCurrentFile(File fileToRoll) throws IOException {

  376.                 File dest = new File(fileToRoll.getPath() + completedSuffix);
  377.                 logger.info("Preparing to move file {} to {}", fileToRoll, dest);

  378.                 // Before renaming, check whether destination file name exists
  379.                 if (dest.exists() && PlatformDetect.isWindows()) {
  380.                         /*
  381.                          * If we are here, it means the completed file already exists. In
  382.                          * almost every case this means the user is violating an assumption
  383.                          * of Flume (that log files are placed in the spooling directory
  384.                          * with unique names). However, there is a corner case on Windows
  385.                          * systems where the file was already rolled but the rename was not
  386.                          * atomic. If that seems likely, we let it pass with only a warning.
  387.                          */
  388.                         if (Files.equal(currentFile.get().getFile(), dest)) {
  389.                                 logger.warn("Completed file " + dest
  390.                                                 + " already exists, but files match, so continuing.");
  391.                                 boolean deleted = fileToRoll.delete();
  392.                                 if (!deleted) {
  393.                                         logger.error("Unable to delete file "
  394.                                                         + fileToRoll.getAbsolutePath()
  395.                                                         + ". It will likely be ingested another time.");
  396.                                 }
  397.                         } else {
  398.                                 String message = "File name has been re-used with different"
  399.                                                 + " files. Spooling assumptions violated for " + dest;
  400.                                 throw new IllegalStateException(message);
  401.                         }

  402.                         // Dest file exists and not on windows
  403.                 } else if (dest.exists()) {
  404.                         String message = "File name has been re-used with different"
  405.                                         + " files. Spooling assumptions violated for " + dest;
  406.                         throw new IllegalStateException(message);

  407.                         // Destination file does not already exist. We are good to go!
  408.                 } else {
  409.                         boolean renamed = fileToRoll.renameTo(dest);
  410.                         if (renamed) {
  411.                                 logger.debug("Successfully rolled file {} to {}", fileToRoll,
  412.                                                 dest);

  413.                                 // now we no longer need the meta file
  414.                                 deleteMetaFile();
  415.                         } else {
  416.                                 /*
  417.                                  * If we are here then the file cannot be renamed for a reason
  418.                                  * other than that the destination file exists (actually, that
  419.                                  * remains possible w/ small probability due to TOC-TOU
  420.                                  * conditions).
  421.                                  */
  422.                                 String message = "Unable to move "
  423.                                                 + fileToRoll
  424.                                                 + " to "
  425.                                                 + dest
  426.                                                 + ". This will likely cause duplicate events. Please verify that "
  427.                                                 + "flume has sufficient permissions to perform these operations.";
  428.                                 throw new FlumeException(message);
  429.                         }
  430.                 }
  431.         }

  432.         /**
  433.          * Delete the given spooled file
  434.          * 
  435.          * @param fileToDelete
  436.          * @throws IOException
  437.          */
  438.         private void deleteCurrentFile(File fileToDelete) throws IOException {
  439.                 logger.info("Preparing to delete file {}", fileToDelete);
  440.                 if (!fileToDelete.exists()) {
  441.                         logger.warn("Unable to delete nonexistent file: {}", fileToDelete);
  442.                         return;
  443.                 }
  444.                 if (!fileToDelete.delete()) {
  445.                         throw new IOException("Unable to delete spool file: "
  446.                                         + fileToDelete);
  447.                 }
  448.                 // now we no longer need the meta file
  449.                 deleteMetaFile();
  450.         }

  451.         /**
  452.          * Find and open the oldest file in the chosen directory. If two or more
  453.          * files are equally old, the file name with lower lexicographical value is
  454.          * returned. If the directory is empty, this will return an absent option.
  455.          */
  456.         private Optional<FileInfo> getNextFile() {
  457.                 /* Filter to exclude finished or hidden files */
  458.                 FileFilter filter = new FileFilter() {
  459.                         public boolean accept(File candidate) {
  460.                                 String fileName = candidate.getName();
  461.                                 if ((candidate.isDirectory())
  462.                                                 || (fileName.endsWith(completedSuffix))
  463.                                                 || (fileName.startsWith("."))
  464.                                                 || ignorePattern.matcher(fileName).matches()) {
  465.                                         return false;
  466.                                 }
  467.                                 return true;
  468.                         }
  469.                 };
  470.                 List<File> candidateFiles = Arrays.asList(spoolDirectory
  471.                                 .listFiles(filter));
  472.                 if (candidateFiles.isEmpty()) {
  473.                         return Optional.absent();
  474.                 } else {
  475.                         Collections.sort(candidateFiles, new Comparator<File>() {
  476.                                 public int compare(File a, File b) {
  477.                                         int timeComparison = new Long(a.lastModified())
  478.                                                         .compareTo(new Long(b.lastModified()));
  479.                                         if (timeComparison != 0) {
  480.                                                 return timeComparison;
  481.                                         } else {
  482.                                                 return a.getName().compareTo(b.getName());
  483.                                         }
  484.                                 }
  485.                         });
  486.                         File nextFile = candidateFiles.get(0);
  487.                         try {
  488.                                 // roll the meta file, if needed
  489.                                 String nextPath = nextFile.getPath();
  490.                                 PositionTracker tracker = DurablePositionTracker.getInstance(
  491.                                                 metaFile, nextPath);
  492.                                 if (!tracker.getTarget().equals(nextPath)) {
  493.                                         tracker.close();
  494.                                         deleteMetaFile();
  495.                                         tracker = DurablePositionTracker.getInstance(metaFile,
  496.                                                         nextPath);
  497.                                 }

  498.                                 // sanity check
  499.                                 Preconditions
  500.                                                 .checkState(
  501.                                                                 tracker.getTarget().equals(nextPath),
  502.                                                                 "Tracker target %s does not equal expected filename %s",
  503.                                                                 tracker.getTarget(), nextPath);

  504.                                 ResettableInputStream in = new ResettableFileInputStream(
  505.                                                 nextFile, tracker,
  506.                                                 ResettableFileInputStream.DEFAULT_BUF_SIZE,
  507.                                                 inputCharset, decodeErrorPolicy);
  508.                                 EventDeserializer deserializer = EventDeserializerFactory
  509.                                                 .getInstance(deserializerType, deserializerContext, in);

  510.                                 return Optional.of(new FileInfo(nextFile, deserializer));
  511.                         } catch (FileNotFoundException e) {
  512.                                 // File could have been deleted in the interim
  513.                                 logger.warn("Could not find file: " + nextFile, e);
  514.                                 return Optional.absent();
  515.                         } catch (IOException e) {
  516.                                 logger.error("Exception opening file: " + nextFile, e);
  517.                                 return Optional.absent();
  518.                         }
  519.                 }
  520.         }

  521.         private void deleteMetaFile() throws IOException {
  522.                 if (metaFile.exists() && !metaFile.delete()) {
  523.                         throw new IOException("Unable to delete old meta file " + metaFile);
  524.                 }
  525.         }

  526.         /** An immutable class with information about a file being processed. */
  527.         private static class FileInfo {
  528.                 private final File file;
  529.                 private final long length;
  530.                 private final long lastModified;
  531.                 private final EventDeserializer deserializer;

  532.                 public FileInfo(File file, EventDeserializer deserializer) {
  533.                         this.file = file;
  534.                         this.length = file.length();
  535.                         this.lastModified = file.lastModified();
  536.                         this.deserializer = deserializer;
  537.                 }

  538.                 public long getLength() {
  539.                         return length;
  540.                 }

  541.                 public long getLastModified() {
  542.                         return lastModified;
  543.                 }

  544.                 public EventDeserializer getDeserializer() {
  545.                         return deserializer;
  546.                 }

  547.                 public File getFile() {
  548.                         return file;
  549.                 }
  550.         }

  551.         @InterfaceAudience.Private
  552.         @InterfaceStability.Unstable
  553.         static enum DeletePolicy {
  554.                 NEVER, IMMEDIATE, DELAY
  555.         }

  556.         /**
  557.          * Special builder class for ReliableSpoolingFileEventReader
  558.          */
  559.         public static class Builder {
  560.                 private File spoolDirectory;
  561.                 private String completedSuffix = SpoolDirectorySourceConfigurationExtConstants.SPOOLED_FILE_SUFFIX;
  562.                 private String ignorePattern = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_IGNORE_PAT;
  563.                 private String trackerDirPath = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_TRACKER_DIR;
  564.                 private Boolean annotateFileName = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILE_HEADER;
  565.                 private String fileNameHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_HEADER_KEY;
  566.                 private Boolean annotateBaseName = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_BASENAME_HEADER;
  567.                 private String baseNameHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_BASENAME_HEADER_KEY;
  568.                 private String deserializerType = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_DESERIALIZER;
  569.                 private Context deserializerContext = new Context();
  570.                 private String deletePolicy = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_DELETE_POLICY;
  571.                 private String inputCharset = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_INPUT_CHARSET;
  572.                 private DecodeErrorPolicy decodeErrorPolicy = DecodeErrorPolicy
  573.                                 .valueOf(SpoolDirectorySourceConfigurationExtConstants.DEFAULT_DECODE_ERROR_POLICY
  574.                                                 .toUpperCase());

  575.                 // 增加代碼開始

  576.                 private Boolean annotateFileNameExtractor = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR;
  577.                 private String fileNameExtractorHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_HEADER_KEY;
  578.                 private String fileNameExtractorPattern = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_PATTERN;
  579.                 private Boolean convertToTimestamp = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP;

  580.                 private String dateTimeFormat = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_FILENAME_EXTRACTOR_DATETIME_FORMAT;

  581.                 private Boolean splitFileName = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_SPLIT_FILENAME;
  582.                 private String splitBy = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_SPLITY_BY;
  583.                 private String splitBaseNameHeader = SpoolDirectorySourceConfigurationExtConstants.DEFAULT_SPLIT_BASENAME_HEADER;

  584.                 public Builder annotateFileNameExtractor(
  585.                                 Boolean annotateFileNameExtractor) {
  586.                         this.annotateFileNameExtractor = annotateFileNameExtractor;
  587.                         return this;
  588.                 }

  589.                 public Builder fileNameExtractorHeader(String fileNameExtractorHeader) {
  590.                         this.fileNameExtractorHeader = fileNameExtractorHeader;
  591.                         return this;
  592.                 }

  593.                 public Builder fileNameExtractorPattern(String fileNameExtractorPattern) {
  594.                         this.fileNameExtractorPattern = fileNameExtractorPattern;
  595.                         return this;
  596.                 }

  597.                 public Builder convertToTimestamp(Boolean convertToTimestamp) {
  598.                         this.convertToTimestamp = convertToTimestamp;
  599.                         return this;
  600.                 }

  601.                 public Builder dateTimeFormat(String dateTimeFormat) {
  602.                         this.dateTimeFormat = dateTimeFormat;
  603.                         return this;
  604.                 }

  605.                 public Builder splitFileName(Boolean splitFileName) {
  606.                         this.splitFileName = splitFileName;
  607.                         return this;
  608.                 }

  609.                 public Builder splitBy(String splitBy) {
  610.                         this.splitBy = splitBy;
  611.                         return this;
  612.                 }

  613.                 public Builder splitBaseNameHeader(String splitBaseNameHeader) {
  614.                         this.splitBaseNameHeader = splitBaseNameHeader;
  615.                         return this;
  616.                 }

  617.                 // 增加代碼結束

  618.                 public Builder spoolDirectory(File directory) {
  619.                         this.spoolDirectory = directory;
  620.                         return this;
  621.                 }

  622.                 public Builder completedSuffix(String completedSuffix) {
  623.                         this.completedSuffix = completedSuffix;
  624.                         return this;
  625.                 }

  626.                 public Builder ignorePattern(String ignorePattern) {
  627.                         this.ignorePattern = ignorePattern;
  628.                         return this;
  629.                 }

  630.                 public Builder trackerDirPath(String trackerDirPath) {
  631.                         this.trackerDirPath = trackerDirPath;
  632.                         return this;
  633.                 }

  634.                 public Builder annotateFileName(Boolean annotateFileName) {
  635.                         this.annotateFileName = annotateFileName;
  636.                         return this;
  637.                 }

  638.                 public Builder fileNameHeader(String fileNameHeader) {
  639.                         this.fileNameHeader = fileNameHeader;
  640.                         return this;
  641.                 }

  642.                 public Builder annotateBaseName(Boolean annotateBaseName) {
  643.                         this.annotateBaseName = annotateBaseName;
  644.                         return this;
  645.                 }

  646.                 public Builder baseNameHeader(String baseNameHeader) {
  647.                         this.baseNameHeader = baseNameHeader;
  648.                         return this;
  649.                 }

  650.                 public Builder deserializerType(String deserializerType) {
  651.                         this.deserializerType = deserializerType;
  652.                         return this;
  653.                 }

  654.                 public Builder deserializerContext(Context deserializerContext) {
  655.                         this.deserializerContext = deserializerContext;
  656.                         return this;
  657.                 }

  658.                 public Builder deletePolicy(String deletePolicy) {
  659.                         this.deletePolicy = deletePolicy;
  660.                         return this;
  661.                 }

  662.                 public Builder inputCharset(String inputCharset) {
  663.                         this.inputCharset = inputCharset;
  664.                         return this;
  665.                 }

  666.                 public Builder decodeErrorPolicy(DecodeErrorPolicy decodeErrorPolicy) {
  667.                         this.decodeErrorPolicy = decodeErrorPolicy;
  668.                         return this;
  669.                 }

  670.                 public ReliableSpoolingFileEventExtReader build() throws IOException {
  671.                         return new ReliableSpoolingFileEventExtReader(spoolDirectory,
  672.                                         completedSuffix, ignorePattern, trackerDirPath,
  673.                                         annotateFileName, fileNameHeader, annotateBaseName,
  674.                                         baseNameHeader, deserializerType, deserializerContext,
  675.                                         deletePolicy, inputCharset, decodeErrorPolicy,
  676.                                         annotateFileNameExtractor, fileNameExtractorHeader,
  677.                                         fileNameExtractorPattern, convertToTimestamp,
  678.                                         dateTimeFormat, splitFileName, splitBy, splitBaseNameHeader);
  679.                 }
  680.         }

  681. }

複製代碼
  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. package com.besttone.flume;

  18. import org.apache.flume.serialization.DecodeErrorPolicy;

  19. public class SpoolDirectorySourceConfigurationExtConstants {
  20.         /** Directory where files are deposited. */
  21.         public static final String SPOOL_DIRECTORY = "spoolDir";

  22.         /** Suffix appended to files when they are finished being sent. */
  23.         public static final String SPOOLED_FILE_SUFFIX = "fileSuffix";
  24.         public static final String DEFAULT_SPOOLED_FILE_SUFFIX = ".COMPLETED";

  25.         /** Header in which to put absolute path filename. */
  26.         public static final String FILENAME_HEADER_KEY = "fileHeaderKey";
  27.         public static final String DEFAULT_FILENAME_HEADER_KEY = "file";

  28.         /** Whether to include absolute path filename in a header. */
  29.         public static final String FILENAME_HEADER = "fileHeader";
  30.         public static final boolean DEFAULT_FILE_HEADER = false;

  31.         /** Header in which to put the basename of file. */
  32.         public static final String BASENAME_HEADER_KEY = "basenameHeaderKey";
  33.         public static final String DEFAULT_BASENAME_HEADER_KEY = "basename";

  34.         /** Whether to include the basename of a file in a header. */
  35.         public static final String BASENAME_HEADER = "basenameHeader";
  36.         public static final boolean DEFAULT_BASENAME_HEADER = false;

  37.         /** What size to batch with before sending to ChannelProcessor. */
  38.         public static final String BATCH_SIZE = "batchSize";
  39.         public static final int DEFAULT_BATCH_SIZE = 100;

  40.         /** Maximum number of lines to buffer between commits. */
  41.         @Deprecated
  42.         public static final String BUFFER_MAX_LINES = "bufferMaxLines";
  43.         @Deprecated
  44.         public static final int DEFAULT_BUFFER_MAX_LINES = 100;

  45.         /** Maximum length of line (in characters) in buffer between commits. */
  46.         @Deprecated
  47.         public static final String BUFFER_MAX_LINE_LENGTH = "bufferMaxLineLength";
  48.         @Deprecated
  49.         public static final int DEFAULT_BUFFER_MAX_LINE_LENGTH = 5000;

  50.         /** Pattern of files to ignore */
  51.         public static final String IGNORE_PAT = "ignorePattern";
  52.         public static final String DEFAULT_IGNORE_PAT = "^[        DISCUZ_CODE_1        ]quot;; // no effect

  53.         /** Directory to store metadata about files being processed */
  54.         public static final String TRACKER_DIR = "trackerDir";
  55.         public static final String DEFAULT_TRACKER_DIR = ".flumespool";

  56.         /** Deserializer to use to parse the file data into Flume Events */
  57.         public static final String DESERIALIZER = "deserializer";
  58.         public static final String DEFAULT_DESERIALIZER = "LINE";

  59.         public static final String DELETE_POLICY = "deletePolicy";
  60.         public static final String DEFAULT_DELETE_POLICY = "never";

  61.         /** Character set used when reading the input. */
  62.         public static final String INPUT_CHARSET = "inputCharset";
  63.         public static final String DEFAULT_INPUT_CHARSET = "UTF-8";

  64.         /** What to do when there is a character set decoding error. */
  65.         public static final String DECODE_ERROR_POLICY = "decodeErrorPolicy";
  66.         public static final String DEFAULT_DECODE_ERROR_POLICY = DecodeErrorPolicy.FAIL
  67.                         .name();

  68.         public static final String MAX_BACKOFF = "maxBackoff";

  69.         public static final Integer DEFAULT_MAX_BACKOFF = 4000;

  70.         // 增加代碼開始
  71.         public static final String FILENAME_EXTRACTOR = "fileExtractor";
  72.         public static final boolean DEFAULT_FILENAME_EXTRACTOR = false;

  73.         public static final String FILENAME_EXTRACTOR_HEADER_KEY = "fileExtractorHeaderKey";
  74.         public static final String DEFAULT_FILENAME_EXTRACTOR_HEADER_KEY = "fileExtractorHeader";

  75.         public static final String FILENAME_EXTRACTOR_PATTERN = "fileExtractorPattern";
  76.         public static final String DEFAULT_FILENAME_EXTRACTOR_PATTERN = "(.)*";

  77.         public static final String FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP = "convertToTimestamp";
  78.         public static final boolean DEFAULT_FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP = false;

  79.         public static final String FILENAME_EXTRACTOR_DATETIME_FORMAT = "dateTimeFormat";
  80.         public static final String DEFAULT_FILENAME_EXTRACTOR_DATETIME_FORMAT = "yyyy-MM-dd";


  81.         public static final String SPLIT_FILENAME = "splitFileName";
  82.         public static final boolean DEFAULT_SPLIT_FILENAME = false;

  83.         public static final String SPLITY_BY = "splitBy";
  84.         public static final String DEFAULT_SPLITY_BY = "\\.";

  85.         public static final String SPLIT_BASENAME_HEADER = "splitBaseNameHeader";
  86.         public static final String DEFAULT_SPLIT_BASENAME_HEADER = "fileNameSplit";
  87.         // 增加代碼結束

  88. }

複製代碼
  1. package com.besttone.flume;

  2. import static com.besttone.flume.SpoolDirectorySourceConfigurationExtConstants.*;


  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.util.List;
  6. import java.util.concurrent.Executors;
  7. import java.util.concurrent.ScheduledExecutorService;
  8. import java.util.concurrent.TimeUnit;

  9. import org.apache.flume.ChannelException;
  10. import org.apache.flume.Context;
  11. import org.apache.flume.Event;
  12. import org.apache.flume.EventDrivenSource;
  13. import org.apache.flume.FlumeException;
  14. import org.apache.flume.conf.Configurable;
  15. import org.apache.flume.instrumentation.SourceCounter;
  16. import org.apache.flume.serialization.DecodeErrorPolicy;
  17. import org.apache.flume.serialization.LineDeserializer;
  18. import org.apache.flume.source.AbstractSource;
  19. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;

  21. import com.google.common.annotations.VisibleForTesting;
  22. import com.google.common.base.Preconditions;
  23. import com.google.common.base.Throwables;

  24. public class SpoolDirectoryExtSource extends AbstractSource implements
  25.                 Configurable, EventDrivenSource {

  26.         private static final Logger logger = LoggerFactory
  27.                         .getLogger(SpoolDirectoryExtSource.class);

  28.         // Delay used when polling for new files
  29.         private static final int POLL_DELAY_MS = 500;

  30.         /* Config options */
  31.         private String completedSuffix;
  32.         private String spoolDirectory;
  33.         private boolean fileHeader;
  34.         private String fileHeaderKey;
  35.         private boolean basenameHeader;
  36.         private String basenameHeaderKey;
  37.         private int batchSize;
  38.         private String ignorePattern;
  39.         private String trackerDirPath;
  40.         private String deserializerType;
  41.         private Context deserializerContext;
  42.         private String deletePolicy;
  43.         private String inputCharset;
  44.         private DecodeErrorPolicy decodeErrorPolicy;
  45.         private volatile boolean hasFatalError = false;

  46.         private SourceCounter sourceCounter;
  47.         ReliableSpoolingFileEventExtReader reader;
  48.         private ScheduledExecutorService executor;
  49.         private boolean backoff = true;
  50.         private boolean hitChannelException = false;
  51.         private int maxBackoff;

  52.         // 增加代碼開始

  53.         private Boolean annotateFileNameExtractor;
  54.         private String fileNameExtractorHeader;
  55.         private String fileNameExtractorPattern;
  56.         private Boolean convertToTimestamp;

  57.         private String dateTimeFormat;
  58.         
  59.         private boolean splitFileName;
  60.         private  String splitBy;
  61.         private  String splitBaseNameHeader;

  62.         // 增加代碼結束

  63.         @Override
  64.         public synchronized void start() {
  65.                 logger.info("SpoolDirectorySource source starting with directory: {}",
  66.                                 spoolDirectory);

  67.                 executor = Executors.newSingleThreadScheduledExecutor();

  68.                 File directory = new File(spoolDirectory);
  69.                 try {
  70.                         reader = new ReliableSpoolingFileEventExtReader.Builder()
  71.                                         .spoolDirectory(directory).completedSuffix(completedSuffix)
  72.                                         .ignorePattern(ignorePattern)
  73.                                         .trackerDirPath(trackerDirPath)
  74.                                         .annotateFileName(fileHeader).fileNameHeader(fileHeaderKey)
  75.                                         .annotateBaseName(basenameHeader)
  76.                                         .baseNameHeader(basenameHeaderKey)
  77.                                         .deserializerType(deserializerType)
  78.                                         .deserializerContext(deserializerContext)
  79.                                         .deletePolicy(deletePolicy).inputCharset(inputCharset)
  80.                                         .decodeErrorPolicy(decodeErrorPolicy)
  81.                                         .annotateFileNameExtractor(annotateFileNameExtractor)
  82.                                         .fileNameExtractorHeader(fileNameExtractorHeader)
  83.                                         .fileNameExtractorPattern(fileNameExtractorPattern)
  84.                                         .convertToTimestamp(convertToTimestamp)
  85.                                         .dateTimeFormat(dateTimeFormat)
  86.                                         .splitFileName(splitFileName).splitBy(splitBy)
  87.                                         .splitBaseNameHeader(splitBaseNameHeader).build();
  88.                 } catch (IOException ioe) {
  89.                         throw new FlumeException(
  90.                                         "Error instantiating spooling event parser", ioe);
  91.                 }

  92.                 Runnable runner = new SpoolDirectoryRunnable(reader, sourceCounter);
  93.                 executor.scheduleWithFixedDelay(runner, 0, POLL_DELAY_MS,
  94.                                 TimeUnit.MILLISECONDS);

  95.                 super.start();
  96.                 logger.debug("SpoolDirectorySource source started");
  97.                 sourceCounter.start();
  98.         }

  99.         @Override
  100.         public synchronized void stop() {
  101.                 executor.shutdown();
  102.                 try {
  103.                         executor.awaitTermination(10L, TimeUnit.SECONDS);
  104.                 } catch (InterruptedException ex) {
  105.                         logger.info("Interrupted while awaiting termination", ex);
  106.                 }
  107.                 executor.shutdownNow();

  108.                 super.stop();
  109.                 sourceCounter.stop();
  110.                 logger.info("SpoolDir source {} stopped. Metrics: {}", getName(),
  111.                                 sourceCounter);
  112.         }

  113.         @Override
  114.         public String toString() {
  115.                 return "Spool Directory source " + getName() + ": { spoolDir: "
  116.                                 + spoolDirectory + " }";
  117.         }

  118.         @Override
  119.         public synchronized void configure(Context context) {
  120.                 spoolDirectory = context.getString(SPOOL_DIRECTORY);
  121.                 Preconditions.checkState(spoolDirectory != null,
  122.                                 "Configuration must specify a spooling directory");

  123.                 completedSuffix = context.getString(SPOOLED_FILE_SUFFIX,
  124.                                 DEFAULT_SPOOLED_FILE_SUFFIX);
  125.                 deletePolicy = context.getString(DELETE_POLICY, DEFAULT_DELETE_POLICY);
  126.                 fileHeader = context.getBoolean(FILENAME_HEADER, DEFAULT_FILE_HEADER);
  127.                 fileHeaderKey = context.getString(FILENAME_HEADER_KEY,
  128.                                 DEFAULT_FILENAME_HEADER_KEY);
  129.                 basenameHeader = context.getBoolean(BASENAME_HEADER,
  130.                                 DEFAULT_BASENAME_HEADER);
  131.                 basenameHeaderKey = context.getString(BASENAME_HEADER_KEY,
  132.                                 DEFAULT_BASENAME_HEADER_KEY);
  133.                 batchSize = context.getInteger(BATCH_SIZE, DEFAULT_BATCH_SIZE);
  134.                 inputCharset = context.getString(INPUT_CHARSET, DEFAULT_INPUT_CHARSET);
  135.                 decodeErrorPolicy = DecodeErrorPolicy
  136.                                 .valueOf(context.getString(DECODE_ERROR_POLICY,
  137.                                                 DEFAULT_DECODE_ERROR_POLICY).toUpperCase());

  138.                 ignorePattern = context.getString(IGNORE_PAT, DEFAULT_IGNORE_PAT);
  139.                 trackerDirPath = context.getString(TRACKER_DIR, DEFAULT_TRACKER_DIR);

  140.                 deserializerType = context
  141.                                 .getString(DESERIALIZER, DEFAULT_DESERIALIZER);
  142.                 deserializerContext = new Context(context.getSubProperties(DESERIALIZER
  143.                                 + "."));

  144.                 // "Hack" to support backwards compatibility with previous generation of
  145.                 // spooling directory source, which did not support deserializers
  146.                 Integer bufferMaxLineLength = context
  147.                                 .getInteger(BUFFER_MAX_LINE_LENGTH);
  148.                 if (bufferMaxLineLength != null && deserializerType != null
  149.                                 && deserializerType.equalsIgnoreCase(DEFAULT_DESERIALIZER)) {
  150.                         deserializerContext.put(LineDeserializer.MAXLINE_KEY,
  151.                                         bufferMaxLineLength.toString());
  152.                 }

  153.                 maxBackoff = context.getInteger(MAX_BACKOFF, DEFAULT_MAX_BACKOFF);
  154.                 if (sourceCounter == null) {
  155.                         sourceCounter = new SourceCounter(getName());
  156.                 }
  157.                 
  158.                 //增加代碼開始

  159.                 annotateFileNameExtractor=context.getBoolean(FILENAME_EXTRACTOR, DEFAULT_FILENAME_EXTRACTOR);
  160.                 fileNameExtractorHeader=context.getString(FILENAME_EXTRACTOR_HEADER_KEY, DEFAULT_FILENAME_EXTRACTOR_HEADER_KEY);
  161.                 fileNameExtractorPattern=context.getString(FILENAME_EXTRACTOR_PATTERN,DEFAULT_FILENAME_EXTRACTOR_PATTERN);
  162.                 convertToTimestamp=context.getBoolean(FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP, DEFAULT_FILENAME_EXTRACTOR_CONVERT_TO_TIMESTAMP);
  163.                 dateTimeFormat=context.getString(FILENAME_EXTRACTOR_DATETIME_FORMAT, DEFAULT_FILENAME_EXTRACTOR_DATETIME_FORMAT);
  164.                 
  165.                 splitFileName=context.getBoolean(SPLIT_FILENAME, DEFAULT_SPLIT_FILENAME);
  166.                 splitBy=context.getString(SPLITY_BY, DEFAULT_SPLITY_BY);
  167.                 splitBaseNameHeader=context.getString(SPLIT_BASENAME_HEADER, DEFAULT_SPLIT_BASENAME_HEADER);
  168.                 
  169.                 
  170.                 
  171.                 //增加代碼結束 
  172.         }

  173.         @VisibleForTesting
  174.         protected boolean hasFatalError() {
  175.                 return hasFatalError;
  176.         }

  177.         /**
  178.          * The class always backs off, this exists only so that we can test without
  179.          * taking a really long time.
  180.          * 
  181.          * @param backoff
  182.          *            - whether the source should backoff if the channel is full
  183.          */
  184.         @VisibleForTesting
  185.         protected void setBackOff(boolean backoff) {
  186.                 this.backoff = backoff;
  187.         }

  188.         @VisibleForTesting
  189.         protected boolean hitChannelException() {
  190.                 return hitChannelException;
  191.         }

  192.         @VisibleForTesting
  193.         protected SourceCounter getSourceCounter() {
  194.                 return sourceCounter;
  195.         }

  196.         private class SpoolDirectoryRunnable implements Runnable {
  197.                 private ReliableSpoolingFileEventExtReader reader;
  198.                 private SourceCounter sourceCounter;

  199.                 public SpoolDirectoryRunnable(
  200.                                 ReliableSpoolingFileEventExtReader reader,
  201.                                 SourceCounter sourceCounter) {
  202.                         this.reader = reader;
  203.                         this.sourceCounter = sourceCounter;
  204.                 }

  205.                 @Override
  206.                 public void run() {
  207.                         int backoffInterval = 250;
  208.                         try {
  209.                                 while (!Thread.interrupted()) {
  210.                                         List<Event> events = reader.readEvents(batchSize);
  211.                                         if (events.isEmpty()) {
  212.                                                 break;
  213.                                         }
  214.                                         sourceCounter.addToEventReceivedCount(events.size());
  215.                                         sourceCounter.incrementAppendBatchReceivedCount();

  216.                                         try {
  217.                                                 getChannelProcessor().processEventBatch(events);
  218.                                                 reader.commit();
  219.                                         } catch (ChannelException ex) {
  220.                                                 logger.warn("The channel is full, and cannot write data now. The "
  221.                                                                 + "source will try again after "
  222.                                                                 + String.valueOf(backoffInterval)
  223.                                                                 + " milliseconds");
  224.                                                 hitChannelException = true;
  225.                                                 if (backoff) {
  226.                                                         TimeUnit.MILLISECONDS.sleep(backoffInterval);
  227.                                                         backoffInterval = backoffInterval << 1;
  228.                                                         backoffInterval = backoffInterval >= maxBackoff ? maxBackoff
  229.                                                                         : backoffInterval;
  230.                                                 }
  231.                                                 continue;
  232.                                         }
  233.                                         backoffInterval = 250;
  234.                                         sourceCounter.addToEventAcceptedCount(events.size());
  235.                                         sourceCounter.incrementAppendBatchAcceptedCount();
  236.                                 }
  237.                                 logger.info("Spooling Directory Source runner has shutdown.");
  238.                         } catch (Throwable t) {
  239.                                 logger.error(
  240.                                                 "FATAL: "
  241.                                                                 + SpoolDirectoryExtSource.this.toString()
  242.                                                                 + ": "
  243.                                                                 + "Uncaught exception in SpoolDirectorySource thread. "
  244.                                                                 + "Restart or reconfigure Flume to continue processing.",
  245.                                                 t);
  246.                                 hasFatalError = true;
  247.                                 Throwables.propagate(t);
  248.                         }
  249.                 }
  250.         }
  251. }

複製代碼
主要提供瞭如下幾個設置參數:

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目錄下就好了,最終的目錄結構:
  1. plugins.d/
  2. plugins.d/SpoolDirectoryExtSource/
  3. plugins.d/SpoolDirectoryExtSource/lib/SpoolDirectoryExtSource.jar
  4. plugins.d/SpoolDirectoryExtSource/libext/
  5. plugins.d/SpoolDirectoryExtSource/native/
複製代碼
重新啓動flume agent,flume就會自動裝載我們的插件,這樣在flume.conf中就可以使用全路徑類名配置type屬性了。

最終flume.conf配置如下:
  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的查看日誌文件的截圖:
 

20140731141724477.jpg (123.1 KB, 下載次數: 0)

下載附件  保存到相冊

2015-3-18 18:42 上傳


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章