/*
* Copyright 2015-present Facebook, Inc.
*
* 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.facebook.buck.testrunner;
import com.facebook.buck.log.AppendableLogRecord;
import java.io.PrintWriter; // NOPMD can't depend on Guava
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
public class JulLogFormatter extends Formatter {
private static final int ERROR_LEVEL = Level.SEVERE.intValue();
private static final int WARN_LEVEL = Level.WARNING.intValue();
private static final int INFO_LEVEL = Level.INFO.intValue();
private static final int DEBUG_LEVEL = Level.FINE.intValue();
private static final int VERBOSE_LEVEL = Level.FINER.intValue();
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT =
new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat format = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss.SSS]", Locale.US);
format.setTimeZone(TimeZone.getDefault());
return format;
}
};
@Override
public String format(LogRecord record) {
String timestamp = DATE_FORMAT.get().format(new Date(record.getMillis()));
// We explicitly don't use String.format here because this code is very
// performance-critical: http://stackoverflow.com/a/1281651
long tid = record.getThreadID();
StringBuilder sb =
new StringBuilder(timestamp).append(formatRecordLevel(record.getLevel())).append("[tid:");
// Zero-pad on the left. We're currently assuming we have less than 100 threads.
if (tid < 10) {
sb.append("0").append(tid);
} else {
sb.append(tid);
}
sb.append("][").append(record.getLoggerName()).append("] ");
if (record instanceof AppendableLogRecord) {
((AppendableLogRecord) record).appendFormattedMessage(sb);
} else {
sb.append(formatMessage(record));
}
sb.append("\n");
Throwable t = record.getThrown();
if (t != null) {
// Closing a StringWriter has no effect, so we don't need to do it in a
// try-with-resources (but oddly, it throws IOException, so we couldn't
// use it in a try-with-resources if we wanted to).
StringWriter sw = new StringWriter();
try (PrintWriter pw = new PrintWriter(sw)) { // NOPMD can't depend on Guava
t.printStackTrace(pw);
sb.append(sw).append("\n");
}
}
return sb.toString();
}
private static String formatRecordLevel(Level level) {
int l = level.intValue();
if (l == ERROR_LEVEL) {
return "[error]";
} else if (l == WARN_LEVEL) {
return "[warn ]";
} else if (l == INFO_LEVEL) {
return "[info ]";
} else if (l == DEBUG_LEVEL) {
return "[debug]";
} else if (l == VERBOSE_LEVEL) {
return "[vrbos]";
} else {
// We don't expect this to happen, so meh, let's use String.format for simplicity.
return String.format("[%-5d]", l);
}
}
}