// Copyright 2017 The Bazel Authors. All rights reserved. // // Licensed 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 com.google.devtools.build.lib.util; import com.google.common.collect.ImmutableRangeMap; import com.google.common.collect.Range; import java.io.PrintWriter; import java.io.StringWriter; import java.util.logging.Formatter; import java.util.logging.Level; import java.util.logging.LogRecord; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; /** * Formatter to write java.util.logging messages out in single-line format. * * <p>Log entries contain the date and time (in UTC), log level (as letter and numerical value), * source location, thread ID, message and, if applicable, a stack trace. */ public class SingleLineFormatter extends Formatter { /** Single-character codes based on {@link Level}s. */ private static final ImmutableRangeMap<Integer, Character> CODES_BY_LEVEL = ImmutableRangeMap.<Integer, Character>builder() .put(Range.atMost(Level.FINE.intValue()), 'D') .put(Range.open(Level.FINE.intValue(), Level.WARNING.intValue()), 'I') .put(Range.closedOpen(Level.WARNING.intValue(), Level.SEVERE.intValue()), 'W') .put(Range.atLeast(Level.SEVERE.intValue()), 'X') .build(); /** A thread safe, immutable formatter that can be used by all without contention. */ private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormat.forPattern("yyMMdd HH:mm:ss.SSS").withZoneUTC(); @Override public String format(LogRecord rec) { StringBuilder buf = new StringBuilder(); // Timestamp buf.append(DATE_TIME_FORMAT.print(rec.getMillis())) .append(':'); // One character code for level buf.append(CODES_BY_LEVEL.get(rec.getLevel().intValue())); // The stack trace, if any Throwable thrown = rec.getThrown(); if (thrown != null) { buf.append('T'); } buf.append(' '); // Information about the source of the exception buf.append(rec.getThreadID()) .append(" [") .append(rec.getSourceClassName()) .append('.') .append(rec.getSourceMethodName()) .append("] "); // The actual message buf.append(formatMessage(rec)).append('\n'); if (thrown != null) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); thrown.printStackTrace(pw); pw.flush(); buf.append(sw.toString()); } return buf.toString(); } }