PageRenderTime 71ms CodeModel.GetById 25ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 0ms

/hudson-core/src/main/java/hudson/tasks/LogRotator.java

http://github.com/hudson/hudson
Java | 341 lines | 204 code | 40 blank | 97 comment | 57 complexity | 775060514cf901dc8560b526223fad4a MD5 | raw file
  1/*
  2 * The MIT License
  3 * 
  4 * Copyright (c) 2004-2011, Oracle Corporation, Kohsuke Kawaguchi, Martin Eigenbrodt,
  5 * Anton Kozak, Nikita Levyankov
  6 * 
  7 * Permission is hereby granted, free of charge, to any person obtaining a copy
  8 * of this software and associated documentation files (the "Software"), to deal
  9 * in the Software without restriction, including without limitation the rights
 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11 * copies of the Software, and to permit persons to whom the Software is
 12 * furnished to do so, subject to the following conditions:
 13 * 
 14 * The above copyright notice and this permission notice shall be included in
 15 * all copies or substantial portions of the Software.
 16 * 
 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23 * THE SOFTWARE.
 24 */
 25package hudson.tasks;
 26
 27import hudson.model.Describable;
 28import hudson.model.Descriptor;
 29import hudson.model.Job;
 30import hudson.model.Run;
 31import hudson.scm.SCM;
 32import java.io.IOException;
 33import java.util.Calendar;
 34import java.util.GregorianCalendar;
 35import java.util.List;
 36import java.util.logging.Logger;
 37import org.kohsuke.stapler.DataBoundConstructor;
 38
 39import static java.util.logging.Level.FINE;
 40import static java.util.logging.Level.FINER;
 41
 42/**
 43 * Deletes old log files.
 44 *
 45 * TODO: is there any other task that follows the same pattern?
 46 * try to generalize this just like {@link SCM} or {@link BuildStep}.
 47 *
 48 * @author Kohsuke Kawaguchi
 49 */
 50public class LogRotator implements Describable<LogRotator> {
 51
 52    private static final Logger LOGGER = Logger.getLogger(LogRotator.class.getName());
 53
 54    /**
 55     * If not -1, history is only kept up to this days.
 56     */
 57    private final int daysToKeep;
 58
 59    /**
 60     * If not -1, only this number of build logs are kept.
 61     */
 62    private final int numToKeep;
 63
 64    /**
 65     * If not -1 nor null, artifacts are only kept up to this days.
 66     * Null handling is necessary to remain data compatible with old versions.
 67     * @since 1.350
 68     */
 69    private final Integer artifactDaysToKeep;
 70
 71    /**
 72     * If not -1 nor null, only this number of builds have their artifacts kept.
 73     * Null handling is necessary to remain data compatible with old versions.
 74     * @since 1.350
 75     */
 76    private final Integer artifactNumToKeep;
 77
 78    @DataBoundConstructor
 79    public LogRotator (String logrotate_days, String logrotate_nums, String logrotate_artifact_days, String logrotate_artifact_nums) {
 80        this (parse(logrotate_days),parse(logrotate_nums),
 81              parse(logrotate_artifact_days),parse(logrotate_artifact_nums));
 82    }
 83
 84    public static int parse(String p) {
 85        if(p==null)     return -1;
 86        try {
 87            return Integer.parseInt(p);
 88        } catch (NumberFormatException e) {
 89            return -1;
 90        }
 91    }
 92
 93    /**
 94     * @deprecated since 1.350.
 95     *      Use {@link #LogRotator(int, int, int, int)}
 96     */
 97    public LogRotator(int daysToKeep, int numToKeep) {
 98        this(daysToKeep, numToKeep, -1, -1);
 99    }
100
101    public LogRotator(int daysToKeep, int numToKeep, int artifactDaysToKeep, int artifactNumToKeep) {
102        this.daysToKeep = daysToKeep;
103        this.numToKeep = numToKeep;
104        this.artifactDaysToKeep = artifactDaysToKeep;
105        this.artifactNumToKeep = artifactNumToKeep;
106
107    }
108
109    public void perform(Job<?, ?> job) throws IOException, InterruptedException {
110        LOGGER.log(FINE, "Running the log rotation for " + job.getFullDisplayName());
111
112        // keep the last successful build regardless of the status
113        Run lsb = job.getLastSuccessfulBuild();
114        Run lstb = job.getLastStableBuild();
115
116        List<? extends Run<?, ?>> builds = job.getBuilds();
117        Calendar cal = null;
118        //Delete builds
119        if (-1 != numToKeep || -1 != daysToKeep) {
120            if (-1 != daysToKeep) {
121                cal = new GregorianCalendar();
122                cal.add(Calendar.DAY_OF_YEAR, -daysToKeep);
123            }
124            if (-1 != numToKeep) {
125                builds = builds.subList(Math.min(builds.size(), numToKeep), builds.size());
126            }
127            //Delete builds based on configured values. See http://issues.hudson-ci.org/browse/HUDSON-3650
128            deleteBuilds(builds, lsb, lstb, cal);
129        }
130
131        cal = null;
132        builds = job.getBuilds();
133        //Delete build artifacts
134        if (-1 != artifactNumToKeep || -1 != artifactDaysToKeep) {
135            if (-1 != artifactDaysToKeep) {
136                cal = new GregorianCalendar();
137                cal.add(Calendar.DAY_OF_YEAR, -artifactDaysToKeep);
138            }
139            if (-1 != artifactNumToKeep) {
140                builds = builds.subList(Math.min(builds.size(), artifactNumToKeep), builds.size());
141            }
142            //Delete build artifacts based on configured values. See http://issues.hudson-ci.org/browse/HUDSON-3650
143            deleteBuildArtifacts(builds, lsb, lstb, cal);
144        }
145    }
146
147    /**
148     * Performs builds deletion
149     *
150     * @param builds list of builds
151     * @param lastSuccessBuild last success build
152     * @param lastStableBuild last stable build
153     * @param cal calendar if configured
154     * @throws IOException if configured
155     */
156    private void deleteBuilds(List<? extends Run<?, ?>> builds, Run lastSuccessBuild, Run lastStableBuild, Calendar cal)
157        throws IOException {
158        for (Run currentBuild : builds) {
159            if (allowDeleteBuild(lastSuccessBuild, lastStableBuild, currentBuild, cal)) {
160                LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is to be removed");
161                currentBuild.delete();
162            }
163        }
164    }
165
166    /**
167     * Checks whether current build could be deleted.
168     * If current build equals to last Success Build or last Stable Build or currentBuild is configured to keep logs or
169     * currentBuild timestamp is before configured calendar value - return false, otherwise return true.
170     *
171     * @param lastSuccessBuild {@link Run}
172     * @param lastStableBuild {@link Run}
173     * @param currentBuild {@link Run}
174     * @param cal {@link Calendar}
175     * @return true - if deletion is allowed, false - otherwise.
176     */
177    private boolean allowDeleteBuild(Run lastSuccessBuild, Run lastStableBuild, Run currentBuild, Calendar cal) {
178        if (currentBuild.isKeepLog()) {
179            LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not GC-ed because it's marked as a keeper");
180            return false;
181        }
182        if (currentBuild == lastSuccessBuild) {
183            LOGGER.log(FINER,
184                currentBuild.getFullDisplayName() + " is not GC-ed because it's the last successful build");
185            return false;
186        }
187        if (currentBuild == lastStableBuild) {
188            LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not GC-ed because it's the last stable build");
189            return false;
190        }
191        if (null != cal && !currentBuild.getTimestamp().before(cal)) {
192            LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not GC-ed because it's still new");
193            return false;
194        }
195        return true;
196    }
197
198    /**
199     * Performs build artifacts deletion
200     *
201     * @param builds list of builds
202     * @param lastSuccessBuild last success build
203     * @param lastStableBuild last stable build
204     * @param cal calendar if configured
205     * @throws IOException if configured
206     */
207    private void deleteBuildArtifacts(List<? extends Run<?, ?>> builds, Run lastSuccessBuild, Run lastStableBuild,
208                                      Calendar cal) throws IOException {
209        for (Run currentBuild : builds) {
210            if (allowDeleteArtifact(lastSuccessBuild, lastStableBuild, currentBuild, cal)) {
211                currentBuild.deleteArtifacts();
212            }
213        }
214    }
215
216    /**
217     * Checks whether artifacts from build could be deleted.
218     * If current build equals to last Success Build or last Stable Build or currentBuild is configured to keep logs or
219     * currentBuild timestamp is before configured calendar value - return false, otherwise return true.
220     *
221     * @param lastSuccessBuild {@link Run}
222     * @param lastStableBuild {@link Run}
223     * @param currentBuild {@link Run}
224     * @param cal {@link Calendar}
225     * @return true - if deletion is allowed, false - otherwise.
226     */
227    private boolean allowDeleteArtifact(Run lastSuccessBuild, Run lastStableBuild, Run currentBuild, Calendar cal) {
228        if (currentBuild.isKeepLog()) {
229            LOGGER.log(FINER,
230                currentBuild.getFullDisplayName() + " is not purged of artifacts because it's marked as a keeper");
231            return false;
232        }
233        if (currentBuild == lastSuccessBuild) {
234            LOGGER.log(FINER, currentBuild.getFullDisplayName()
235                + " is not purged of artifacts because it's the last successful build");
236            return false;
237        }
238        if (currentBuild == lastStableBuild) {
239            LOGGER.log(FINER,
240                currentBuild.getFullDisplayName() + " is not purged of artifacts because it's the last stable build");
241            return false;
242        }
243        if (null != cal && !currentBuild.getTimestamp().before(cal)) {
244            LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not purged of artifacts because it's still new");
245            return false;
246        }
247        return true;
248    }
249
250    public int getDaysToKeep() {
251        return daysToKeep;
252    }
253
254    public int getNumToKeep() {
255        return numToKeep;
256    }
257
258    public int getArtifactDaysToKeep() {
259        return unbox(artifactDaysToKeep);
260    }
261
262    public int getArtifactNumToKeep() {
263        return unbox(artifactNumToKeep);
264    }
265
266    public String getDaysToKeepStr() {
267        return toString(daysToKeep);
268    }
269
270    public String getNumToKeepStr() {
271        return toString(numToKeep);
272    }
273
274    public String getArtifactDaysToKeepStr() {
275        return toString(artifactDaysToKeep);
276    }
277
278    public String getArtifactNumToKeepStr() {
279        return toString(artifactNumToKeep);
280    }
281
282    private int unbox(Integer i) {
283        return i==null ? -1: i;
284    }
285
286    private String toString(Integer i) {
287        if (i==null || i==-1)   return "";
288        return String.valueOf(i);
289    }
290
291
292    public LRDescriptor getDescriptor() {
293        return DESCRIPTOR;
294    }
295
296    public static final LRDescriptor DESCRIPTOR = new LRDescriptor();
297
298    public static final class LRDescriptor extends Descriptor<LogRotator> {
299        public String getDisplayName() {
300            return "Log Rotation";
301        }
302    }
303
304    @Override
305    public boolean equals(Object o) {
306        if (this == o) {
307            return true;
308        }
309        if (o == null || getClass() != o.getClass()) {
310            return false;
311        }
312
313        LogRotator that = (LogRotator) o;
314
315        if (daysToKeep != that.daysToKeep) {
316            return false;
317        }
318        if (numToKeep != that.numToKeep) {
319            return false;
320        }
321        if (artifactDaysToKeep != null ? !artifactDaysToKeep.equals(that.artifactDaysToKeep)
322            : that.artifactDaysToKeep != null) {
323            return false;
324        }
325        if (artifactNumToKeep != null ? !artifactNumToKeep.equals(that.artifactNumToKeep)
326            : that.artifactNumToKeep != null) {
327            return false;
328        }
329
330        return true;
331    }
332
333    @Override
334    public int hashCode() {
335        int result = daysToKeep;
336        result = 31 * result + numToKeep;
337        result = 31 * result + (artifactDaysToKeep != null ? artifactDaysToKeep.hashCode() : 0);
338        result = 31 * result + (artifactNumToKeep != null ? artifactNumToKeep.hashCode() : 0);
339        return result;
340    }
341}