spring 异常捕获异常
在上一个博客中 ,我开始谈论需要弄清您的应用程序在生产环境中是否行为异常。 我说过,监视应用程序的一种方法是检查其日志文件是否存在异常,如果发现异常,则采取适当的措施。 显然,日志文件可能会占用数百兆的磁盘空间,用手监视它们是不切实际的,而且很无聊。
我还说过,有几种方法可以自动监视日志文件,并提出了一个基于Spring的实用程序,该实用程序每天都会梳理日志文件,并在发现任何异常时向您发送电子邮件。
我只介绍了第一类: FileLocator
,它将在目录及其子目录中搜索日志文件。 找到一个后,将其传递给FileValidator
。
FileValidator
必须对文件执行多项检查。 首先,它必须确定文件是否足够年轻以检查异常。 想法是,由于应用程序定期运行,因此没有必要检查目录中找到的所有文件是否存在错误,我们只希望自应用程序上次运行以来已创建或更新的文件。
这种设计的思想是将同一个接口的多个实现组合在一起,创建一个负责验证文件的聚合对象。 鹰眼的读者会注意到,这是Delegate Pattern的实现。
在上面的类图中,将RegexValidator
和FileAgeValidator
实例注入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
类具有采用正则表达式字符串的单个参数构造函数。 然后将其转换为Pattern
实例变量,并由validate(T string)
方法使用它来测试String
输入参数是否与原始构造函数arg正则表达式匹配。 如果是这样,那么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;
}
}
第二个Validator(T obj)
实现是FileAgeValidator
显示的FileAgeValidator
,要注意的第一件事是整个过程由max.days
属性驱动。 这被注入到FileAgeValidator
的@Value
注释的maxDays
实例变量中。 此变量确定文件的最长期限(天)。 该文件早于该值,然后validate(T obj)
将返回false。
在此实现中, validate(T obj)
'obj'参数被强制转换为File
对象,然后将其用于将文件的日期转换为Calendar
对象。 下一行代码将maxDays
变量转换为第二个Calendar
对象: ageLimit
。 然后将ageLimit
与fileDate
对象进行比较。 如果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
作为参数。 它的首要职责是验证文件的年龄。 如果该验证器返回true,则它通知Report
类它已找到一个新的有效文件。 然后,它检查文件中是否有错误,并将找到的任何内容添加到Report
实例。 它通过使用BufferedReader
依次检查文件的每一行来做到这一点。 在检查某行是否包含错误之前,它会检查该行是否未从检查中排除-即,它与排除的异常或我们不感兴趣的异常不匹配。如果该行与排除的异常不匹配异常,然后使用RegexValidator
的第二个实例检查是否需要查找异常。 如果该行确实包含错误,则会将其添加到List<String>
对象。 然后从添加到列表的文件中读取以下几行,以使报告更具可读性。
因此,文件解析将继续进行,一次检查每一行以查找错误并建立报告,以便稍后进行处理。
该封面的验证文件使用委托模式添加了发现到Report
任何异常,但是此Report
对象如何工作? 我没有提到它,输出是如何产生的? 下次再说。
- 该博客的代码可在Github上找到: https : //github.com/roghughe/captaindebug/tree/master/error-track 。
翻译自: https://www.javacodegeeks.com/2014/03/tracking-exceptions-with-spring-part-2-delegate-pattern.html
spring 异常捕获异常