/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. */
  25. package hudson.tasks;
  26. import hudson.model.Describable;
  27. import hudson.model.Descriptor;
  28. import hudson.model.Job;
  29. import hudson.model.Run;
  30. import hudson.scm.SCM;
  31. import java.io.IOException;
  32. import java.util.Calendar;
  33. import java.util.GregorianCalendar;
  34. import java.util.List;
  35. import java.util.logging.Logger;
  36. import org.kohsuke.stapler.DataBoundConstructor;
  37. import static java.util.logging.Level.FINE;
  38. import static java.util.logging.Level.FINER;
  39. /**
  40. * Deletes old log files.
  41. *
  42. * TODO: is there any other task that follows the same pattern?
  43. * try to generalize this just like {@link SCM} or {@link BuildStep}.
  44. *
  45. * @author Kohsuke Kawaguchi
  46. */
  47. public class LogRotator implements Describable<LogRotator> {
  48. private static final Logger LOGGER = Logger.getLogger(LogRotator.class.getName());
  49. /**
  50. * If not -1, history is only kept up to this days.
  51. */
  52. private final int daysToKeep;
  53. /**
  54. * If not -1, only this number of build logs are kept.
  55. */
  56. private final int numToKeep;
  57. /**
  58. * If not -1 nor null, artifacts are only kept up to this days.
  59. * Null handling is necessary to remain data compatible with old versions.
  60. * @since 1.350
  61. */
  62. private final Integer artifactDaysToKeep;
  63. /**
  64. * If not -1 nor null, only this number of builds have their artifacts kept.
  65. * Null handling is necessary to remain data compatible with old versions.
  66. * @since 1.350
  67. */
  68. private final Integer artifactNumToKeep;
  69. @DataBoundConstructor
  70. public LogRotator (String logrotate_days, String logrotate_nums, String logrotate_artifact_days, String logrotate_artifact_nums) {
  71. this (parse(logrotate_days),parse(logrotate_nums),
  72. parse(logrotate_artifact_days),parse(logrotate_artifact_nums));
  73. }
  74. public static int parse(String p) {
  75. if(p==null) return -1;
  76. try {
  77. return Integer.parseInt(p);
  78. } catch (NumberFormatException e) {
  79. return -1;
  80. }
  81. }
  82. /**
  83. * @deprecated since 1.350.
  84. * Use {@link #LogRotator(int, int, int, int)}
  85. */
  86. public LogRotator(int daysToKeep, int numToKeep) {
  87. this(daysToKeep, numToKeep, -1, -1);
  88. }
  89. public LogRotator(int daysToKeep, int numToKeep, int artifactDaysToKeep, int artifactNumToKeep) {
  90. this.daysToKeep = daysToKeep;
  91. this.numToKeep = numToKeep;
  92. this.artifactDaysToKeep = artifactDaysToKeep;
  93. this.artifactNumToKeep = artifactNumToKeep;
  94. }
  95. public void perform(Job<?, ?> job) throws IOException, InterruptedException {
  96. LOGGER.log(FINE, "Running the log rotation for " + job.getFullDisplayName());
  97. // keep the last successful build regardless of the status
  98. Run lsb = job.getLastSuccessfulBuild();
  99. Run lstb = job.getLastStableBuild();
  100. List<? extends Run<?, ?>> builds = job.getBuilds();
  101. Calendar cal = null;
  102. //Delete builds
  103. if (-1 != numToKeep || -1 != daysToKeep) {
  104. if (-1 != daysToKeep) {
  105. cal = new GregorianCalendar();
  106. cal.add(Calendar.DAY_OF_YEAR, -daysToKeep);
  107. }
  108. if (-1 != numToKeep) {
  109. builds = builds.subList(Math.min(builds.size(), numToKeep), builds.size());
  110. }
  111. //Delete builds based on configured values. See http://issues.hudson-ci.org/browse/HUDSON-3650
  112. deleteBuilds(builds, lsb, lstb, cal);
  113. }
  114. cal = null;
  115. builds = job.getBuilds();
  116. //Delete build artifacts
  117. if (-1 != artifactNumToKeep || -1 != artifactDaysToKeep) {
  118. if (-1 != artifactDaysToKeep) {
  119. cal = new GregorianCalendar();
  120. cal.add(Calendar.DAY_OF_YEAR, -artifactDaysToKeep);
  121. }
  122. if (-1 != artifactNumToKeep) {
  123. builds = builds.subList(Math.min(builds.size(), artifactNumToKeep), builds.size());
  124. }
  125. //Delete build artifacts based on configured values. See http://issues.hudson-ci.org/browse/HUDSON-3650
  126. deleteBuildArtifacts(builds, lsb, lstb, cal);
  127. }
  128. }
  129. /**
  130. * Performs builds deletion
  131. *
  132. * @param builds list of builds
  133. * @param lastSuccessBuild last success build
  134. * @param lastStableBuild last stable build
  135. * @param cal calendar if configured
  136. * @throws IOException if configured
  137. */
  138. private void deleteBuilds(List<? extends Run<?, ?>> builds, Run lastSuccessBuild, Run lastStableBuild, Calendar cal)
  139. throws IOException {
  140. for (Run currentBuild : builds) {
  141. if (allowDeleteBuild(lastSuccessBuild, lastStableBuild, currentBuild, cal)) {
  142. LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is to be removed");
  143. currentBuild.delete();
  144. }
  145. }
  146. }
  147. /**
  148. * Checks whether current build could be deleted.
  149. * If current build equals to last Success Build or last Stable Build or currentBuild is configured to keep logs or
  150. * currentBuild timestamp is before configured calendar value - return false, otherwise return true.
  151. *
  152. * @param lastSuccessBuild {@link Run}
  153. * @param lastStableBuild {@link Run}
  154. * @param currentBuild {@link Run}
  155. * @param cal {@link Calendar}
  156. * @return true - if deletion is allowed, false - otherwise.
  157. */
  158. private boolean allowDeleteBuild(Run lastSuccessBuild, Run lastStableBuild, Run currentBuild, Calendar cal) {
  159. if (currentBuild.isKeepLog()) {
  160. LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not GC-ed because it's marked as a keeper");
  161. return false;
  162. }
  163. if (currentBuild == lastSuccessBuild) {
  164. LOGGER.log(FINER,
  165. currentBuild.getFullDisplayName() + " is not GC-ed because it's the last successful build");
  166. return false;
  167. }
  168. if (currentBuild == lastStableBuild) {
  169. LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not GC-ed because it's the last stable build");
  170. return false;
  171. }
  172. if (null != cal && !currentBuild.getTimestamp().before(cal)) {
  173. LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not GC-ed because it's still new");
  174. return false;
  175. }
  176. return true;
  177. }
  178. /**
  179. * Performs build artifacts deletion
  180. *
  181. * @param builds list of builds
  182. * @param lastSuccessBuild last success build
  183. * @param lastStableBuild last stable build
  184. * @param cal calendar if configured
  185. * @throws IOException if configured
  186. */
  187. private void deleteBuildArtifacts(List<? extends Run<?, ?>> builds, Run lastSuccessBuild, Run lastStableBuild,
  188. Calendar cal) throws IOException {
  189. for (Run currentBuild : builds) {
  190. if (allowDeleteArtifact(lastSuccessBuild, lastStableBuild, currentBuild, cal)) {
  191. currentBuild.deleteArtifacts();
  192. }
  193. }
  194. }
  195. /**
  196. * Checks whether artifacts from build could be deleted.
  197. * If current build equals to last Success Build or last Stable Build or currentBuild is configured to keep logs or
  198. * currentBuild timestamp is before configured calendar value - return false, otherwise return true.
  199. *
  200. * @param lastSuccessBuild {@link Run}
  201. * @param lastStableBuild {@link Run}
  202. * @param currentBuild {@link Run}
  203. * @param cal {@link Calendar}
  204. * @return true - if deletion is allowed, false - otherwise.
  205. */
  206. private boolean allowDeleteArtifact(Run lastSuccessBuild, Run lastStableBuild, Run currentBuild, Calendar cal) {
  207. if (currentBuild.isKeepLog()) {
  208. LOGGER.log(FINER,
  209. currentBuild.getFullDisplayName() + " is not purged of artifacts because it's marked as a keeper");
  210. return false;
  211. }
  212. if (currentBuild == lastSuccessBuild) {
  213. LOGGER.log(FINER, currentBuild.getFullDisplayName()
  214. + " is not purged of artifacts because it's the last successful build");
  215. return false;
  216. }
  217. if (currentBuild == lastStableBuild) {
  218. LOGGER.log(FINER,
  219. currentBuild.getFullDisplayName() + " is not purged of artifacts because it's the last stable build");
  220. return false;
  221. }
  222. if (null != cal && !currentBuild.getTimestamp().before(cal)) {
  223. LOGGER.log(FINER, currentBuild.getFullDisplayName() + " is not purged of artifacts because it's still new");
  224. return false;
  225. }
  226. return true;
  227. }
  228. public int getDaysToKeep() {
  229. return daysToKeep;
  230. }
  231. public int getNumToKeep() {
  232. return numToKeep;
  233. }
  234. public int getArtifactDaysToKeep() {
  235. return unbox(artifactDaysToKeep);
  236. }
  237. public int getArtifactNumToKeep() {
  238. return unbox(artifactNumToKeep);
  239. }
  240. public String getDaysToKeepStr() {
  241. return toString(daysToKeep);
  242. }
  243. public String getNumToKeepStr() {
  244. return toString(numToKeep);
  245. }
  246. public String getArtifactDaysToKeepStr() {
  247. return toString(artifactDaysToKeep);
  248. }
  249. public String getArtifactNumToKeepStr() {
  250. return toString(artifactNumToKeep);
  251. }
  252. private int unbox(Integer i) {
  253. return i==null ? -1: i;
  254. }
  255. private String toString(Integer i) {
  256. if (i==null || i==-1) return "";
  257. return String.valueOf(i);
  258. }
  259. public LRDescriptor getDescriptor() {
  260. return DESCRIPTOR;
  261. }
  262. public static final LRDescriptor DESCRIPTOR = new LRDescriptor();
  263. public static final class LRDescriptor extends Descriptor<LogRotator> {
  264. public String getDisplayName() {
  265. return "Log Rotation";
  266. }
  267. }
  268. @Override
  269. public boolean equals(Object o) {
  270. if (this == o) {
  271. return true;
  272. }
  273. if (o == null || getClass() != o.getClass()) {
  274. return false;
  275. }
  276. LogRotator that = (LogRotator) o;
  277. if (daysToKeep != that.daysToKeep) {
  278. return false;
  279. }
  280. if (numToKeep != that.numToKeep) {
  281. return false;
  282. }
  283. if (artifactDaysToKeep != null ? !artifactDaysToKeep.equals(that.artifactDaysToKeep)
  284. : that.artifactDaysToKeep != null) {
  285. return false;
  286. }
  287. if (artifactNumToKeep != null ? !artifactNumToKeep.equals(that.artifactNumToKeep)
  288. : that.artifactNumToKeep != null) {
  289. return false;
  290. }
  291. return true;
  292. }
  293. @Override
  294. public int hashCode() {
  295. int result = daysToKeep;
  296. result = 31 * result + numToKeep;
  297. result = 31 * result + (artifactDaysToKeep != null ? artifactDaysToKeep.hashCode() : 0);
  298. result = 31 * result + (artifactNumToKeep != null ? artifactNumToKeep.hashCode() : 0);
  299. return result;
  300. }
  301. }