zhuguifei
2026-03-10 58402bd5e762361363a0f7d7907153c77dbb819f
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package com.shlanbao.tzsc.utils.extents;
 
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
 
import org.apache.log4j.FileAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
 
/**
 * 扩展的一个按天滚动的appender类 暂时不支持datePattern设置,但是可以配置maxBackupIndex
 * <li>@author Leejean
 * <li>@create 2014-6-24 下午04:14:38
 */
public class DayRollingFileAppender extends FileAppender {
 
    /** 不允许改写的datepattern */
    private final String datePattern = "'.'yyyy-MM-dd";
 
    /** 最多文件增长个数 */
    private int maxBackupIndex = 2;
 
    /** "文件名+上次最后更新时间" */
    private String scheduledFilename;
 
    /**
     * The next time we estimate a rollover should occur.
     */
    private long nextCheck = System.currentTimeMillis() - 1;
 
    Date now = new Date();
 
    SimpleDateFormat sdf;
 
    /**
     * The default constructor does nothing.
     */
    public DayRollingFileAppender() {
    }
 
    /**
     * 改造过的构造器
     */
    public DayRollingFileAppender(Layout layout, String filename,
            int maxBackupIndex) throws IOException {
        super(layout, filename, true);
        this.maxBackupIndex = maxBackupIndex;
        activateOptions();
    }
 
    /**
     * 初始化本Appender对象的时候调用一次
     */
    public void activateOptions() {
        super.activateOptions();
        if (fileName != null) { // perf.log
            now.setTime(System.currentTimeMillis());
            sdf = new SimpleDateFormat(datePattern);
            File file = new File(fileName);
            // 获取最后更新时间拼成的文件名
            scheduledFilename = fileName
                    + sdf.format(new Date(file.lastModified()));
        } else {
            LogLog.error("File is not set for appender [" + name + "].");
        }
        if (maxBackupIndex <= 0) {
            LogLog.error("maxBackupIndex reset to default value[2],orignal value is:"
                    + maxBackupIndex);
            maxBackupIndex = 2;
        }
    }
 
    /**
     * 滚动文件的函数: 1.对文件名带的时间戳进行比较,确定是否更新 2.if需要更新,当前文件rename到文件名+日期, 重新开始写文件 3.
     * 针对配置的maxBackupIndex,删除过期的文件
     */
    void rollOver() throws IOException {
 
        String datedFilename = fileName + sdf.format(now);
        // 如果上次写的日期跟当前日期相同,不需要换文件
        if (scheduledFilename.equals(datedFilename)) {
            return;
        }
 
        // close current file, and rename it to datedFilename
        this.closeFile();
 
        File target = new File(scheduledFilename);
        if (target.exists()) {
            target.delete();
        }
 
        File file = new File(fileName);
        boolean result = file.renameTo(target);
        if (result) {
            LogLog.debug(fileName + " -> " + scheduledFilename);
        } else {
            LogLog.error("Failed to rename [" + fileName + "] to ["
                    + scheduledFilename + "].");
        }
 
        // 删除过期文件
        if (maxBackupIndex > 0) {
            File folder = new File(file.getParent());
            List<String> maxBackupIndexDates = getMaxBackupIndexDates();
            for (File ff : folder.listFiles()) { // 遍历目录,将日期不在备份范围内的日志删掉
                if (ff.getName().startsWith(file.getName())
                        && !ff.getName().equals(file.getName())) {
                    // 获取文件名带的日期时间戳
                    String markedDate = ff.getName().substring(
                            file.getName().length());
                    if (!maxBackupIndexDates.contains(markedDate)) {
                        result = ff.delete();
                    }
                    if (result) {
                        LogLog.debug(ff.getName() + " ->deleted ");
                    } else {
                        LogLog.error("Failed to deleted old DayRollingFileAppender file :"
                                + ff.getName());
                    }
                }
            }
        }
 
        try {
            // This will also close the file. This is OK since multiple
            // close operations are safe.
            this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
        } catch (IOException e) {
            errorHandler.error("setFile(" + fileName + ", false) call failed.");
        }
        scheduledFilename = datedFilename; // 更新最后更新日期戳
    }
 
    /**
     * Actual writing occurs here. 这个方法是写操作真正的执行过程!
     * */
    protected void subAppend(LoggingEvent event) {
        long n = System.currentTimeMillis();
        if (n >= nextCheck) { // 在每次写操作前判断一下是否需要滚动文件
            now.setTime(n);
            nextCheck = getNextDayCheckPoint(now);
            try {
                rollOver();
            } catch (IOException ioe) {
                LogLog.error("rollOver() failed.", ioe);
            }
        }
        super.subAppend(event);
    }
 
    /**
     * 获取下一天的时间变更点
     * 
     * @param now
     * @return
     */
    long getNextDayCheckPoint(Date now) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);// 注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的
        calendar.add(Calendar.DATE, 1);
        return calendar.getTimeInMillis();
    }
 
/**
   * 根据maxBackupIndex配置的备份文件个数,获取要保留log文件的日期范围集合
   * @return list<'fileName+yyyy-MM-dd'>
   */
    List<String> getMaxBackupIndexDates() {
        List<String> result = new ArrayList<String>();
        if (maxBackupIndex > 0) {
            for (int i = 1; i <= maxBackupIndex; i++) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(now);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);// 注意MILLISECOND,毫秒也要置0.。。否则错了也找不出来的
                calendar.add(Calendar.DATE, -i);
                result.add(sdf.format(calendar.getTime()));
            }
        }
        return result;
    }
 
    public int getMaxBackupIndex() {
        return maxBackupIndex;
    }
 
    public void setMaxBackupIndex(int maxBackupIndex) {
        this.maxBackupIndex = maxBackupIndex;
    }
 
    public String getDatePattern() {
        return datePattern;
    }
 
    // public static void main(String[] args) {
    // DayRollingFileAppender da = new DayRollingFileAppender();
    // da.setMaxBackupIndex(2);
    // da.sdf = new SimpleDateFormat(da.getDatePattern());
    // System.out.println(da.getMaxBackupIndexDates());
    //
    // File f = new File("e:/log/b2c/perf.log");
    // System.out.println("f.name=" + f.getName());
    // File p = new File(f.getParent());
    // for(File ff : p.listFiles()) {
    // System.out.println(ff);
    // }
    // }
}