flume-taildir/glob语法/路径迭代
最近在使用flume的taildir做多文件监控,发现配置路径的增则表达式不起作用,跟踪源码发现:
TaildirMatcher(String fileGroup, String filePattern, boolean cachePatternMatching) {
// store whatever came from configuration
this.fileGroup = fileGroup;
this.filePattern = filePattern;
this.cachePatternMatching = cachePatternMatching;
// calculate final members
File f = new File(filePattern);
this.parentDir = f.getParentFile();
String regex = f.getName();
final PathMatcher matcher = FS.getPathMatcher("regex:" + regex);
this.fileFilter = new DirectoryStream.Filter() {
@Override
public boolean accept(Path entry) throws IOException {
return matcher.matches(entry.getFileName()) && !Files.isDirectory(entry);
}
};
// sanity check
Preconditions.checkState(parentDir.exists(),
"Directory does not exist: " + parentDir.getAbsolutePath());
}
其中的
final PathMatcher matcher = FS.getPathMatcher("regex:" + regex);
这一行是用来做正则匹配的。
这里的参数有两种类型:一是glob;另一是regex的。flume默认使用的是regex类型,其是java.util.regex.Pattern的java类型的正则表达式。这两种类型的正则匹配不一样,所以导致配置路径不起作用。至于glob则是下面介绍的一种
Glob
Glob 是一种模式匹配,类似于正则表达式但是语法相对简单。Glob 语句是一个含有 *,?{}[] 这些特殊字符的字符串,并与目标字符串匹配。可以用来做文件路径匹配或文件查找。例如 Linux 下的命令 ls *.txt ,其中的 *.txt 就是一个 glob 语句。
语法规则
Java 语言中的 Glob 语法遵循几个简单的规则:
*
匹配任意个数的字符,包括空。不包括路径边界 / 或 \。例如 /path/*/abc 可以匹配 /path/a/abc 和 /path/b/abc等。
**
和一个星号类似,区别是可以跨路径边界,一般用来匹配多级目录。例如 /path/**/abc 可以用来匹配 /path/a/abc、/path/b/abc 、/path/a/b/abc、/path/a/b/c/abc 等。
一个问号 ?
匹配任意一个字符
{}
大括号用来指定一个子模式匹配集合,例如:{sun,moon,stars} 可以匹配 sun moon starts , {temp*, tmp*} 可以匹配任何以 temp tmp 开头的字符串等。
[]
方括号表示匹配括号内的任意一个单个字符,当有 - 时表示匹配任意一个连续范围内的单个字符。例如:[aeiou] 匹配任意一个小写元音字符,[0-9] 匹配任意一个数字,[A-Z] 匹配任意一个大写字母,[a-z,A-Z] 匹配任意一个大写或小写字母。另外,在方括号内, * ? \ 字符仅匹配它们自身。
任意其它字符
任意的其他字符表示匹配它们自身。
反斜杠\转义
匹配 * ? 或其他特殊字符需要使用反斜杠\转义。例如: \\ 匹配一个反斜杠,\? 匹配一个问号。
举例说明
Glob
说明
*.log
匹配所有.log结尾的字符串
??
匹配所有有两个数字或字母构成的字符串,aa,a1
*[0-9]*
匹配所有含有一个数字的字符串
*.{htm,html,pdf}
匹配所有以 .htm .html 或 .pdf 结尾的字符串
a?*.java
匹配所有以 a 开头,且a 之后至少由一个字母或数字,且以 .java 结尾的字符传
{foo*,[0-9]}
匹配所有以 foo 开头,或含有数字的字符串,如 foobar x1y foo1xyz
代码
JDK 中 glob 相关的内容主要在 java.nio.file 包内,其中 glob 语法转正则表达式的实现在 sun.nio.fs.Globs.java 中。下面代码是一个寻找/lihao/test/ 目录下所有 jpg 文件的例子:
String match = "glob:/lihao/test/**/*.jpg";
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(match);
SimpleFileVisitor fileVisitor = new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if (pathMatcher.matches(file)) {
System.out.println("find: " + file.toFile().toString())
}
return super.visitFile(file, attrs);
}
};
try {
Files.walkFileTree(Paths.get(dir.getPath()), fileVisitor);
} catch (IOException e) {
e.printStackTrace();
}
参考
taildir监控文件迭代问题源码修改
在TaildirMatcher类中使用如下代码覆盖相应的代码即可将完成路径的迭代问题
private List getMatchingFilesNoCache() {
List result = Lists.newArrayList();
List paths = recurseFolder(parentDir);
for(Path path:paths){
try (DirectoryStream stream = Files.newDirectoryStream(path, fileFilter)) {
for (Path entry : stream) {
result.add(entry.toFile());
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public List recurseFolder(File root) {
List allParentFolders = new ArrayList<>();
if (root.exists()) {
allParentFolders.add(root.toPath());
File[] files = root.listFiles();
if (null == files || files.length == 0) {
return allParentFolders;
} else {
for (File subFile : files) {
if (subFile.isDirectory()) {
allParentFolders.addAll(recurseFolder(subFile));
}
}
}
}
return allParentFolders;
}