package hudson.plugins.performance.reports; import hudson.model.ParameterValue; import hudson.model.ParametersAction; import hudson.model.Result; import hudson.model.Run; import hudson.model.StringParameterValue; import hudson.plugins.performance.constraints.AbsoluteConstraint; import hudson.plugins.performance.constraints.ConstraintEvaluation; import hudson.plugins.performance.constraints.RelativeConstraint; import jenkins.model.Jenkins; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.List; /** * Creates a report of the constraint evaluation and stores it into a consecutive log file, a build * environment variable and prints it to the Jenkins console output. * * @author Rene Kugel */ public class ConstraintReport { /** * Log file that is created and the log is printed to */ private File performanceLog; /** * Newly created build object. Used to determine build number and build date */ private Run<?, ?> newBuild; /** * Number of the build */ private int buildNumber; /** * Date when the build was started */ private Calendar buildDate; /** * Result of the build */ private Result buildResult; /** * Link to the build with the build number */ private String linkToBuild; /** * Number of all constraints in this build */ private short allConstraints = 0; /** * Number of all relative constraints in this build */ private short relativeConstraints = 0; /** * Number of all absolute constraints in this build */ private short absoluteConstraints = 0; /** * Number of all successful constraints in this build */ private short successfulConstraints = 0; /** * Number of all violated constraints in this build */ private short violatedConstraints = 0; /** * Number of all violated constraints with the escalation level INFORMATION in this build */ private short violatedInformation = 0; /** * Number of all violated constraints with the escalation level WARNING in this build */ private short violatedUnstable = 0; /** * Number of all violated constraints with the escalation level ERROR in this build */ private short violatedError = 0; /** * Logger message for console output */ private String loggerMsg; /** * Logger message for log file and environment variable */ private String loggerMsgAdv; public ConstraintReport(ArrayList<ConstraintEvaluation> ceList, Run<?, ?> globBuild, boolean persistConstraintLog) throws IOException, InterruptedException { this.newBuild = globBuild; this.createMetaData(ceList, newBuild); this.createLoggerMsg(ceList); this.createLoggerMsgAdv(); this.writeResultsToEnvVar(); if (persistConstraintLog) { this.writeResultsToFile(); } } /** * Creates the head data for the report * * @param ceList ArrayList of evaluated constraints * @param newBuild Newly created build */ private void createMetaData(ArrayList<ConstraintEvaluation> ceList, Run<?, ?> newBuild) { this.buildNumber = newBuild.getNumber(); this.buildDate = newBuild.getTimestamp(); this.buildResult = determineBuildResult(ceList); /* * Jenkins cannot reliable resolve it's own root URL unless it is set in the Jenkins System * Configuration. This will cause that getRootUrl() returns null */ if (Jenkins.getInstance().getRootUrl() == null) { this.linkToBuild = "Could not resolve URL - Please set the root URL in the Jenkins System Configuration"; } else { this.linkToBuild = Jenkins.getInstance().getRootUrl() + newBuild.getUrl(); } for (ConstraintEvaluation ce : ceList) { if (ce.getAbstractConstraint() instanceof AbsoluteConstraint) { this.allConstraints++; this.absoluteConstraints++; if (ce.getAbstractConstraint().getSuccess() == true) { this.successfulConstraints++; } else { this.violatedConstraints++; switch (ce.getAbstractConstraint().getEscalationLevel().ordinal()) { case 0: this.violatedInformation++; break; case 1: this.violatedUnstable++; break; case 2: this.violatedError++; break; } } } else if (ce.getAbstractConstraint() instanceof RelativeConstraint) { this.allConstraints++; this.relativeConstraints++; if (ce.getAbstractConstraint().getSuccess() == true) { this.successfulConstraints++; } else { this.violatedConstraints++; switch (ce.getAbstractConstraint().getEscalationLevel().ordinal()) { case 0: this.violatedInformation++; break; case 1: this.violatedUnstable++; break; case 2: this.violatedError++; break; } } } } } /** * Determines the build result based on the violated constraint with the highest escalation * level * * @param ceList ArrayList of evaluated constraints * @return The determined build result. The build will be marked based on this result: SUCCESS, * UNSTABLE, FAILURE */ private Result determineBuildResult(ArrayList<ConstraintEvaluation> ceList) { int highestViolatedEscalation = 0; for (ConstraintEvaluation ce : ceList) { if (ce.getAbstractConstraint().getEscalationLevel().ordinal() > highestViolatedEscalation && !ce.getAbstractConstraint().getSuccess()) { highestViolatedEscalation = ce.getAbstractConstraint().getEscalationLevel().ordinal(); } } switch (highestViolatedEscalation) { case 0: return Result.SUCCESS; case 1: return Result.UNSTABLE; case 2: return Result.FAILURE; default: return Result.FAILURE; } } /** * Creates the body data for the report. * * @param ceList ArrayList of evaluated constraints */ private void createLoggerMsg(ArrayList<ConstraintEvaluation> ceList) { loggerMsg = "----------------------------------------------------------- \n"; if (relativeConstraints == 0) { loggerMsg += "There are no relative constraints to evaluate! \n-------------- \n"; } else { loggerMsg += "Evaluating all relative constraints! \n-------------- \n"; for (ConstraintEvaluation ce : ceList) { if (ce.getAbstractConstraint() instanceof RelativeConstraint) { loggerMsg += ce.getAbstractConstraint().getResultMessage() + "\n-------------- \n"; } } } if (absoluteConstraints == 0) { loggerMsg += "There are no absolute constraints to evaluate! \n-------------- \n"; } else { loggerMsg += "Evaluating all absolute constraints! \n-------------- \n"; for (ConstraintEvaluation ce : ceList) { if (ce.getAbstractConstraint() instanceof AbsoluteConstraint) { loggerMsg += ce.getAbstractConstraint().getResultMessage() + "\n-------------- \n"; } } } if (violatedConstraints == 0) { loggerMsg += "There were no failing Constraints! The build will be marked as SUCCESS"; } else if (buildResult.equals(Result.SUCCESS)) { loggerMsg += "The highest escalation: Information! The build will be marked as SUCCESS"; } else if (buildResult.equals(Result.UNSTABLE)) { loggerMsg += "The highest escalation: Warning! The build will be marked as UNSTABLE"; } else if (buildResult.equals(Result.FAILURE)) { loggerMsg += "The highest escalation: Error! The build will be marked as FAILURE"; } loggerMsg += "\n"; } /** * Concatenates the body and the head of the message */ private void createLoggerMsgAdv() { loggerMsgAdv = "----------------------------------------------------------- \n" + "Build Number: #" + this.getBuildNumber() + "\n" + "Build Date: " + this.getBuildDate().getTime() + "\n" + "Build State: " + this.getBuildResult().toString() + "\n" + "Link to build: " + this.linkToBuild + "\n" + "-------------- \n" + "Number of all constraints: " + this.getAllConstraints() + "\n" + "Relative constraints: " + this.getRelativeConstraints() + "\n" + "Absolute constraints: " + this.getAbsoluteConstraints() + "\n" + "Successful constraints: " + this.getSuccessfulConstraints() + "\n" + "Violated constraints: " + this.getViolatedConstraints() + "\n" + "->INFORMATION: " + this.getViolatedInformation() + "\n" + "->UNSTABLE: " + this.getViolatedUnstable() + "\n" + "->ERROR: " + this.getViolatedError() + "\n" + "-------------- \n" + loggerMsg + "\n"; } /** * Creates the log file if not present and writes the report to the log file. Only executed if * persistConstraintLog == true * * @throws IOException * @throws InterruptedException */ public void writeResultsToFile() throws InterruptedException, IOException { performanceLog = new File(newBuild.getRootDir() + File.separator + "performance-results" + File.separator + "performance.log"); if (!performanceLog.exists()) { performanceLog.getParentFile().mkdirs(); performanceLog.createNewFile(); } FileOutputStream outWriter = new FileOutputStream(performanceLog, true); try { outWriter.write(getLoggerMsgAdv().getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { outWriter.close(); } } /** * Writes the complete report to the environment variable: BUILD_CONSTRAINT_LOG */ public void writeResultsToEnvVar() { List<ParameterValue> params = new ArrayList<ParameterValue>(); params.add(new StringParameterValue("BUILD_CONSTRAINT_LOG", getLoggerMsgAdv())); newBuild.addAction(new ParametersAction(params)); } public int getBuildNumber() { return buildNumber; } public Calendar getBuildDate() { return buildDate; } public Result getBuildResult() { return buildResult; } public String getLinkToBuild() { return linkToBuild; } public short getAllConstraints() { return allConstraints; } public short getRelativeConstraints() { return relativeConstraints; } public short getAbsoluteConstraints() { return absoluteConstraints; } public short getSuccessfulConstraints() { return successfulConstraints; } public short getViolatedConstraints() { return violatedConstraints; } public short getViolatedInformation() { return violatedInformation; } public short getViolatedUnstable() { return violatedUnstable; } public short getViolatedError() { return violatedError; } public String getLoggerMsg() { return loggerMsg; } public String getLoggerMsgAdv() { return loggerMsgAdv; } public File getPerformanceLog() { return performanceLog; } public void setPerformanceLog(File performanceLog) { this.performanceLog = performanceLog; } }