/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The SF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package org.apache.sling.hc.core.impl.servlet; import java.io.PrintWriter; import java.io.StringWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Dictionary; import java.util.List; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Service; import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.hc.api.Result; import org.apache.sling.hc.api.ResultLog.Entry; import org.apache.sling.hc.api.execution.HealthCheckExecutionResult; import org.apache.sling.hc.util.FormattingResultLog; import org.osgi.service.component.ComponentContext; /** Serializes health check results into html format. */ @Service(ResultHtmlSerializer.class) @Component(metatype = true, name = "Apache Sling Health Check Result HTML Serializer", description = "Serializer for health check results in HTML format") public class ResultHtmlSerializer { private static final String CSS_STYLE_DEFAULT = "body { font-size:12px; font-family:arial,verdana,sans-serif;background-color:#FFFDF1; }\n" + "h1 { font-size:20px;}\n" + "table { font-size:12px; border:#ccc 1px solid; border-radius:3px; }\n" + "table th { padding:5px; text-align: left; background: #ededed; }\n" + "table td { padding:5px; border-top: 1px solid #ffffff; border-bottom:1px solid #e0e0e0; border-left: 1px solid #e0e0e0; }\n" + ".statusOK { background-color:#CCFFCC;}\n" + ".statusWARN { background-color:#FFE569;}\n" + ".statusCRITICAL { background-color:#F0975A;}\n" + ".statusHEALTH_CHECK_ERROR { background-color:#F16D4E;}\n" + ".helpText { color:grey; font-size:80%; }\n"; public static final String PROPERTY_CSS_STYLE = "styleString"; @Property(name = PROPERTY_CSS_STYLE, label = "CSS Style", description = "CSS Style - can be configured to change the look and feel of the html result page.", value = CSS_STYLE_DEFAULT) private String styleString; @Activate protected final void activate(final ComponentContext context) { final Dictionary<?, ?> properties = context.getProperties(); this.styleString = PropertiesUtil.toString(properties.get(PROPERTY_CSS_STYLE), CSS_STYLE_DEFAULT); } public String serialize(final Result overallResult, final List<HealthCheckExecutionResult> executionResults, String escapedHelpText, boolean includeDebug) { StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); writer.println("<html><head><title>System Health</title>" + "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /><style>" + styleString + "</style></head><body><h1>System Health</h1>"); writer.println("<p><span class=\"" + getClassForStatus(overallResult.getStatus()) + "\"><strong>Overall Result: " + overallResult.getStatus() + "</strong></span></p>"); final DateFormat dfShort = new SimpleDateFormat("HH:mm:ss.SSS"); final DateFormat dfLong = new SimpleDateFormat("yyyy-MM-dd HH:mm"); writer.println("<table id=\"healthCheckResults\" cellspacing=\"0\">"); writer.println( "<thead><tr><th>Health Check <span style='color:gray'>(tags)</span></th><th>Status</th><th>Log</th><th>Finished At</th><th>Time</th></tr></thead>"); for (HealthCheckExecutionResult executionResult : executionResults) { Result result = executionResult.getHealthCheckResult(); List<String> tags = executionResult.getHealthCheckMetadata().getTags(); boolean hasTags = tags != null && tags.size() > 0 && StringUtils.isNotBlank(tags.get(0)); writer.print("<tr class=\"" + getClassForStatus(result.getStatus()) + "\">"); writer.print("<td><p title=\"" + StringEscapeUtils.escapeHtml(executionResult.getHealthCheckMetadata().getName()) + "\">" + StringEscapeUtils.escapeHtml(executionResult.getHealthCheckMetadata().getTitle()) + ""); if (hasTags) { writer.println("<br/><span style='color:gray'>" + StringEscapeUtils.escapeHtml(StringUtils.join(tags, ", ")) + "</span>"); } writer.println("</p></td>"); writer.println("<td style='font-weight:bold;'>" + StringEscapeUtils.escapeHtml(result.getStatus().toString()) + "</td>"); writer.println("<td>"); boolean isFirst = true; boolean isSingleResult = isSingleResult(result); for (Entry entry : result) { if(!includeDebug && entry.getStatus()==Result.Status.DEBUG) { continue; } if (isFirst) { isFirst = false; } else { writer.println("<br/>\n"); } boolean showStatus = !isSingleResult && entry.getStatus()!=Result.Status.DEBUG && entry.getStatus() !=Result.Status.INFO; String message = StringEscapeUtils.escapeHtml(entry.getMessage()); if(entry.getStatus()==Result.Status.DEBUG) { message = "<span style='color:gray'/>"+message + "</span>"; } writer.println((showStatus ? StringEscapeUtils.escapeHtml(entry.getStatus().toString()) + " " : "") + message); Exception exception = entry.getException(); if (exception != null) { writer.println("<span style='width:20px'/>" + StringEscapeUtils.escapeHtml(exception.toString())); writer.println("<!--"); exception.printStackTrace(writer); writer.println("-->"); } } writer.println("</td>"); Date finishedAt = executionResult.getFinishedAt(); writer.println("<td>" + (isToday(finishedAt) ? dfShort.format(finishedAt) : dfLong.format(finishedAt)) + "</td>"); writer.println("<td>" + FormattingResultLog.msHumanReadable(executionResult.getElapsedTimeInMs()) + "</td>"); writer.println("</tr>"); } writer.println("</table>"); writer.println("<div class='helpText'>"); writer.println(escapedHelpText); writer.println("</div>"); writer.println("</body></html>"); return stringWriter.toString(); } private String getClassForStatus(final Result.Status status) { return "status" + status.name(); } private boolean isSingleResult(final Result result) { int count = 0; for (Entry entry : result) { count++; if (count > 1) { return false; } } return true; } private boolean isToday(Date date) { Calendar cal1 = Calendar.getInstance(); Calendar cal2 = Calendar.getInstance(); cal2.setTime(date); boolean isToday = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); return isToday; } }