summaryrefslogtreecommitdiffstats
path: root/vespalog/src/main/java/com/yahoo/log/VespaFormatter.java
diff options
context:
space:
mode:
Diffstat (limited to 'vespalog/src/main/java/com/yahoo/log/VespaFormatter.java')
-rw-r--r--vespalog/src/main/java/com/yahoo/log/VespaFormatter.java172
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;
+ }
+}