(该文由本人翻译,最先发表于importnew,原文译文链接请参见文章末尾)
在上一篇博文中,我们讨论了需要确定应用是否能在生产环境中正常运行,一种方法是通过检查日志文件中的异常并分析它们。不过,日志文件会占用大量的硬盘空间,而且人工检查它们是非常枯燥和不切实际的。我也指出了有几种自动化监控日志文件的方法,譬如,一个基于spring的工具能每天梳理日志,并在发现异常时发出邮件通知。
上次只讲到FileLocator类,它通过遍历一个目录及其下的子目录来找到日志文件,并将它们交给类FileValidator处理。类FileValidator会对文件进行一些检查。首先,检查文件是否足够新。因为有些应用是定期运行的,没有必要对找到的所有文件都进行检查,我们只需要检查当前这次运行过程中新建或修改的日志文件即可。
基于委托模式的异常跟踪,设计思想是通过整合同一个接口的不同实现,构建一个新的聚合对象来负责文件检查。
在上面的类图中,将类RegexValidator和FileAgeValidator的实例注入FileValidator,FileValidator就会代理这些类的验证工作。
下面先来看看Validator接口
1
2
3
4
5
6
|
public
interface
Validator {
/** The validation method */
public
<T>
boolean
validate(T obj);
}
|
上面的代码展示了Validator接口的简单,它只有一个泛型方法validate(T obj),这增加了接口的灵活性和可重用性。当类实现了这个接口时,它们可以根据自己的需要改变方法的输入参数类型,就像下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
@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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
@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 翻译: ImportNew.com - 秋双译文链接: http://www.importnew.com/11659.html