使用Spring跟踪应用异常(2)—委托模式

(该文由本人翻译,最先发表于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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值