/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.systemservices.impl.logsvc.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FilenameFilter; import java.util.*; import java.util.regex.Pattern; /** * Given the log file path information, this class returns all matching files grouped * by base name. */ public class LogFileFinder { // Logger reference. private static final Logger logger = LoggerFactory.getLogger(LogFileFinder.class); private List<String> _logFilePaths; private List<String> _excludedFilePaths; public LogFileFinder(List<String> logFilePaths, List<String> excludedLogFilePaths) { _logFilePaths = logFilePaths; _excludedFilePaths = excludedLogFilePaths; } /** * Groups files by their base name as key and rolled files as values. * * @return Map of grouped files */ public Map<String, List<File>> findFilesGroupedByBaseName() { Map<String, List<File>> baseLogFiles = new HashMap<String, List<File>>(); String fileName; List<File> logFiles = getAllFilesMatchingGlobPattern(); List<File> sameBaseFiles; for (File file : logFiles) { String baseName; fileName = file.getName(); int dotIndex = fileName.indexOf("."); if (dotIndex > 0) { baseName = fileName.substring(0, dotIndex); } else { baseName = fileName; } logger.debug("fileName: {}. baseName: {}", fileName, baseName); if ((sameBaseFiles = baseLogFiles.get(baseName)) == null) { sameBaseFiles = new ArrayList<File>(); } sameBaseFiles.add(file); baseLogFiles.put(baseName, sameBaseFiles); } return baseLogFiles; } /** * Retrieves all files matching global pattern * * @return list of valid files */ private List<File> getAllFilesMatchingGlobPattern() { List<File> logFiles = new ArrayList<File>(); String logFileDirPath; String logFileNameGlob; File logFileDir; String fileRegEx; Map<String, List<String>> excludeRegexesInDir = new HashMap(); String excludeGlob = null; String excludeFileDirPath = null; for (String excludedFilePath : _excludedFilePaths) { logger.debug("Found excluded log file path {}", excludedFilePath); excludeFileDirPath = getLogFileDir(excludedFilePath); excludeGlob = getGlobExpr(excludedFilePath); if (excludeFileDirPath != null) { if (excludeRegexesInDir.containsKey(excludeFileDirPath)) { excludeRegexesInDir.get(excludeFileDirPath).add(convertGlobToRegEx( excludeGlob.trim())); } else { ArrayList<String> excludeRegexList = new ArrayList<String>(); excludeRegexList.add(convertGlobToRegEx(excludeGlob.trim())); excludeRegexesInDir.put(excludeFileDirPath, excludeRegexList); } } } for (String logFilePath : _logFilePaths) { logger.debug("Looking for log files {}", logFilePath); // Since there really is no standard easy way in java // to get the list of files specified by a wild-carded // path such as that above, we need to split the path // into its directory path and file name glob. logFileNameGlob = getGlobExpr(logFilePath); logger.debug("logFileNameGlob: {}", logFileNameGlob); // We turn the file name glob into regular // expressions we can use to match the file // names in the directory. fileRegEx = convertGlobToRegEx(logFileNameGlob); // Now, we list the files in the directory // and those whose name matches a regular // expression is a log file. logFileDirPath = getLogFileDir(logFilePath); if (logFileDirPath != null) { logFileDir = new File(logFileDirPath); logger.debug("Getting files from dir: {} matching ex: {}", logFileDirPath, fileRegEx); logFiles.addAll(getAllFilesInDirMatchingRegEx(logFileDir, fileRegEx, excludeRegexesInDir.get(logFileDirPath))); } } return logFiles; } /** * Retrieves directory path from the log file path. * EX: if logFilePath: /opt/storageos/logs/*.log Returns: /opt/storageos/logs * * @param logFilePath full log file path * @return directory path */ private String getLogFileDir(String logFilePath) { String directoryPathComp = null; int indexOfLastSlash = logFilePath.lastIndexOf(File.separator); if ((indexOfLastSlash != -1) && (logFilePath.length() > indexOfLastSlash + 1)) { directoryPathComp = logFilePath.substring(0, indexOfLastSlash); if (directoryPathComp.length() == 0) { // The file must be in the root directory. directoryPathComp = File.separator; } } return directoryPathComp; } /** * Retrieves the glob expression part from the log file path * EX: if logFilePath: /opt/storageos/logs/*.log Returns: *.log * * @param logFilePath log file path * @return glob expression */ private String getGlobExpr(String logFilePath) { String logFileNameGlob = null; int indexOfLastSlash = logFilePath.lastIndexOf(File.separator); logFileNameGlob = logFilePath.substring(indexOfLastSlash + 1); return logFileNameGlob; } /** * Retrieves all files in a directory that are matching the reg-ex. * * @param fileDir File directory from which log files are retrieved. * @param regEx Regular expression that file name should match * @return List of files. */ private List<File> getAllFilesInDirMatchingRegEx(File fileDir, final String regEx, final List<String> excludeRegexList) { List<File> logFiles = new ArrayList<File>(); if (fileDir.exists()) { logFiles.addAll(Arrays.asList(fileDir.listFiles( new FilenameFilter() { @Override public boolean accept(File logFileDir, String fileName) { logger.debug("Checking file {}", fileName); boolean match = false; if (isLogFileMatchesRegEx(fileName, regEx)) { match = true; if (excludeRegexList != null) { for (String eRegex : excludeRegexList) { if (isLogFileMatchesRegEx(fileName, eRegex)) { match = false; break; } } } if (match) { logger.debug("File {} is a log file", fileName); } } return match; } }))); } return logFiles; } /** * Checks if the file matches the regular expression * * @param fileName * @param regEx * @return true if matches, false otherwise. */ private boolean isLogFileMatchesRegEx(String fileName, String regEx) { boolean matches; Pattern fileNamePattern = Pattern.compile(regEx); matches = fileNamePattern.matcher(fileName).matches(); return matches; } /** * Converts global expression to regular expression * * @param name global file name * @return regular expression. */ private String convertGlobToRegEx(String name) { name = name.trim(); int strLen = name.length(); StringBuilder sb = new StringBuilder(strLen); boolean escaping = false; int inCurlies = 0; for (char currentChar : name.toCharArray()) { switch (currentChar) { case '*': if (escaping) { sb.append("\\*"); } else { sb.append(".*"); } escaping = false; break; case '?': if (escaping) { sb.append("\\?"); } else { sb.append('.'); } escaping = false; break; case '.': case '(': case ')': case '+': case '|': case '^': case '$': case '@': case '%': sb.append('\\'); sb.append(currentChar); escaping = false; break; case '\\': if (escaping) { sb.append("\\\\"); escaping = false; } else { escaping = true; } break; case '{': if (escaping) { sb.append("\\{"); } else { sb.append('('); inCurlies++; } escaping = false; break; case '}': if (inCurlies > 0 && !escaping) { sb.append(')'); inCurlies--; } else if (escaping) { sb.append("\\}"); } else { sb.append("}"); } escaping = false; break; case ',': if (inCurlies > 0 && !escaping) { sb.append('|'); } else if (escaping) { sb.append("\\,"); } else { sb.append(","); } break; default: escaping = false; sb.append(currentChar); } } return sb.toString(); } }