使用Spring跟踪应用异常(2)—委托模式,spring跟踪
使用Spring跟踪应用异常(2)—委托模式,spring跟踪
在上一篇博文中,我们讨论了需要确定应用是否能在生产环境中正常运行,一种方法是通过检查日志文件中的异常并分析它们。不过,日志文件会占用大量的硬盘空间,而且人工检查它们是非常枯燥和不切实际的。我也指出了有几种自动化监控日志文件的方法,譬如,一个基于spring的工具能每天梳理日志,并在发现异常时发出邮件通知。
上次只讲到FileLocator类,它通过遍历一个目录及其下的子目录来找到日志文件,并将它们交给类FileValidator处理。类FileValidator会对文件进行一些检查。首先,检查文件是否足够新。因为有些应用是定期运行的,没有必要对找到的所有文件都进行检查,我们只需要检查当前这次运行过程中新建或修改的日志文件即可。
基于委托模式的异常跟踪,设计思想是通过整合同一个接口的不同实现,构建一个新的聚合对象来负责文件检查。
在上面的类图中,将类RegexValidator和FileAgeValidator的实例注入FileValidator,FileValidator就会代理这些类的验证工作。
下面先来看看Validator接口
public interface Validator { /** The validation method */ public <T> boolean validate(T obj); }
上面的代码展示了Validator接口的简单,它只有一个泛型方法validate(T obj),这增加了接口的灵活性和可重用性。当类实现了这个接口时,它们可以根据自己的需要改变方法的输入参数类型,就像下面这样:
public class RegexValidator implements Validator { private static final Logger logger = LoggerFactory.getLogger(RegexValidator.class); private final Pattern pattern; public RegexValidator(String regex) { pattern = Pattern.compile(regex); logger.info("loaded regex: {}", regex); } @Override public <T> boolean validate(T string) { boolean retVal = false; Matcher matcher = pattern.matcher((String) string); retVal = matcher.matches(); if (retVal && logger.isDebugEnabled()) { logger.debug("Found error line: {}", string); } return retVal; } }
类RegexValidator有一个单参数构造函数,参数为一个正则表达式字符串,构造函数会将该字符串转换为一个模式实例。在方法validate(T String)中,模式实例用于创建Matcher对象,以验证输入参数是否与该对象匹配。如果匹配,validate(T string)方法返回true。
@Service public class FileAgeValidator implements Validator { @Value("${max.days}") private int maxDays; /** * Validate the age of the file. * * @see com.captaindebug.errortrack.Validator#validate(java.lang.Object) */ @Override public <T> boolean validate(T obj) { File file = (File) obj; Calendar fileDate = getFileDate(file); Calendar ageLimit = getFileAgeLimit(); boolean retVal = false; if (fileDate.after(ageLimit)) { retVal = true; } return retVal; } private Calendar getFileAgeLimit() { Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, -1 * maxDays); return cal; } private Calendar getFileDate(File file) { long fileDate = file.lastModified(); Calendar when = Calendar.getInstance(); when.setTimeInMillis(fileDate); return when; } }
上面的类FileAgeValidator是Validator接口的第二个实现,在该类中需要注意的是max.days属性,它被注入到FileAgeValidator类的用@Value标记的maxDays中。这个变量决定了文件距今的最大未修改天数,如果文件上次修改时间早于它,validate(T obj)将返回false。
在FileAgeValidator类中,validate(T obj)的参数obj会被转换为一个File对象,然后将该File对象的上次修改时间转换为一个Calendar实例fileDate。再将maxDasy变量转换为第二个Calendar实例ageLimit,如果fileDate表示的时间晚于ageLimit表示的时间,validate(T obj)方法就返回true。
validator包中的最后一个类是FileValidator,我们会将其他所有类的验证工作委托给该类。这些类包括一个FileAgeValidator和两个RegexValidator。
@Service public class FileValidator implements Validator { private static final Logger logger = LoggerFactory.getLogger(FileValidator.class); @Value("${following.lines}") private Integer extraLineCount; @Autowired @Qualifier("scan-for") private RegexValidator scanForValidator; @Autowired(required = false) @Qualifier("exclude") private RegexValidator excludeValidator; @Autowired private FileAgeValidator fileAgeValidator; @Autowired private Results results; @Override public <T> boolean validate(T obj) { boolean retVal = false; File file = (File) obj; if (fileAgeValidator.validate(file)) { results.addFile(file.getPath()); checkFile(file); retVal = true; } return retVal; } private void checkFile(File file) { try { BufferedReader in = createBufferedReader(file); readLines(in, file); in.close(); } catch (Exception e) { logger.error("Error whilst processing file: " + file.getPath() + " Message: " + e.getMessage(), e); } } @VisibleForTesting BufferedReader createBufferedReader(File file) throws FileNotFoundException { BufferedReader in = new BufferedReader(new FileReader(file)); return in; } private void readLines(BufferedReader in, File file) throws IOException { int lineNumber = 0; String line; do { line = in.readLine(); if (isNotNull(line)) { processLine(line, file.getPath(), ++lineNumber, in); } } while (isNotNull(line)); } private boolean isNotNull(Object obj) { return obj != null; } private int processLine(String line, String filePath, int lineNumber, BufferedReader in) throws IOException { if (canValidateLine(line) && scanForValidator.validate(line)) { List<String> lines = new ArrayList<String>(); lines.add(line); addExtraDetailLines(in, lines); results.addResult(filePath, lineNumber, lines); lineNumber += extraLineCount; } return lineNumber; } private boolean canValidateLine(String line) { boolean retVal = true; if (isNotNull(excludeValidator)) { retVal = !excludeValidator.validate(line); } return retVal; } private void addExtraDetailLines(BufferedReader in, List<String> lines) throws IOException { for (int i = 0; i < extraLineCount; i++) { String line = in.readLine(); if (isNotNull(line)) { lines.add(line); } else { break; } } } }
FileValidator类的validate(T obj)以一个File作为输入参数,它首先验证文件在指定时间内是否修改过。如果是,就通知Report类发现一个新的需要检查的文件。然后通过一个BufferedReader来顺序检查文件中的每一行,检查过程中需要忽略一些不关心的异常。如果某一行没通过excludeValidator验证(即不是需要忽略的行),就用scanForValidator来检查该行。当这行包含一个错误时,将其加入一个List实例中,该List使得存放检查结果的report可读性更高。这样一个个文件处理下来,就能够生成一个可供后续分析的报告。
这里讲解了如果利用委托模式来检查日志文件并生成一个报告,但是如何利用这个报告,输出如果生成,则是后面要讲的内容。
以上代码请见Github:https://github.com/roghughe/captaindebug/tree/master/error-track
原文链接: captaindebug 翻译: Wld5.com - 秋双译文链接: http://www.wld5.com/11659.html
[ 转载请保留原文出处、译者和译文链接。]
用户点评