diff options
Diffstat (limited to 'vespalog/src/main/java/com/yahoo/log/VespaFormatter.java')
-rw-r--r-- | vespalog/src/main/java/com/yahoo/log/VespaFormatter.java | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/vespalog/src/main/java/com/yahoo/log/VespaFormatter.java b/vespalog/src/main/java/com/yahoo/log/VespaFormatter.java new file mode 100644 index 00000000000..ee0f6e90b7c --- /dev/null +++ b/vespalog/src/main/java/com/yahoo/log/VespaFormatter.java @@ -0,0 +1,172 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// $Id$ +package com.yahoo.log; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.LogRecord; +import java.util.logging.SimpleFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.yahoo.log.event.Event; + +/** + * This class implements a log formatter which takes care of + * formatting messages according to the VESPA common log format. + * + * @author Bjorn Borud + * @author arnej27959 + * + */ +public class VespaFormatter extends SimpleFormatter { + + private static final Pattern backSlash = Pattern.compile("\\\\"); + + // other way around + private static final Pattern backSlashN = Pattern.compile("\\\\n"); + private static final Pattern backSlashT = Pattern.compile("\\\\t"); + private static final Pattern backSlash2 = Pattern.compile("\\\\\\\\"); + + private static final String hostname; + private static final String processID; + + public static final String serviceNameUnsetValue = "-"; + + static { + hostname = Util.getHostName(); + processID = Util.getPID(); + } + + private String serviceName; + private String componentPrefix; + + /** + * Default constructor + */ + public VespaFormatter() { + this.serviceName = serviceNameUnsetValue; + } + + /** + * @param serviceName The VESPA service name. + * @param componentPrefix The application name. + */ + public VespaFormatter(String serviceName, String componentPrefix) { + if (serviceName == null) { + this.serviceName = serviceNameUnsetValue; + } else { + this.serviceName = serviceName; + } + this.componentPrefix = componentPrefix; + } + + /** + * Un-escapes previously escaped string. + * note: look at com.yahoo.config.StringNode.unescapeQuotedString() + * + * @param s String that might need un-escaping + * @return Returns un-escaped string + */ + public static String unEscape(String s) { + Matcher m = backSlash.matcher(s); + if (! m.find()) { + return s; + } + m = backSlashN.matcher(s); + if (m.find()) { + s = m.replaceAll("\n"); + } + m = backSlashT.matcher(s); + if (m.find()) { + s = m.replaceAll("\t"); + } + m = backSlash2.matcher(s); + if (m.find()) { + s = m.replaceAll("\\\\"); + } + return s; + } + + public String format(LogRecord r) { + StringBuilder sbuf = new StringBuilder(300); // initial guess + + String levelName = LogLevel.getVespaLogLevel(r.getLevel()) + .toString() + .toLowerCase(); + + String component = r.getLoggerName(); + + // format the time + VespaFormat.formatTime(r.getMillis(), sbuf); + sbuf.append("\t"); + + sbuf.append(hostname).append("\t") + .append(processID).append("/") + .append(r.getThreadID()).append("\t") + .append(serviceName).append("\t"); + + if (component == null && componentPrefix == null) { + sbuf.append("-"); + } else if (component == null) { + sbuf.append(componentPrefix); + } else if (componentPrefix == null) { + sbuf.append(".").append(component); + } else { + sbuf.append(componentPrefix).append(".").append(component); + } + + sbuf.append("\t").append(levelName).append("\t"); + + // for events, there is no ordinary message string; + // instead we render a string represantion of the event object: + if (r.getLevel() == LogLevel.EVENT) { + Event event = (Event) r.getParameters()[0]; + sbuf.append(VespaFormat.escape(event.toString())); + } else { + // otherwise, run standard java text formatting on the message + sbuf.append(VespaFormat.escape(formatMessage(r))); + } + appendException(r.getThrown(), sbuf); + + + sbuf.append("\n"); + return sbuf.toString(); + } + + private void appendException(Throwable throwable, StringBuilder builder) { + if (throwable == null) + return; + + String escapedStackTrace = VespaFormat.escape(stackTrace(throwable)); + builder.append("\\n").append("exception=").append("\\n").append(escapedStackTrace); + } + + private String stackTrace(Throwable throwable) { + StringWriter writer = new StringWriter(); + PrintWriter wrappedWriter = new PrintWriter(writer); + throwable.printStackTrace(wrappedWriter); + wrappedWriter.close(); + return writer.toString(); + } + + + /** + * Set the service name (usually the VESPA config-id) of this + * formatter. + * + * @param serviceName The service name + */ + public void setServiceName (String serviceName) { + this.serviceName = serviceName; + } + + /** + * Get the service name for this formatter. + * + * @return Returns the service name. + */ + public String getServiceName () { + return serviceName; + } +} |