blob: 1d7c3a75ba8e26e547828c2aef8b40113f4a0f09 [file] [log] [blame]
/*
* Copyright 2014-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.log;
import com.google.common.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javax.annotation.Nullable;
public class LogFormatter extends java.util.logging.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 final ConcurrentMap<Long, String> threadIdToCommandId;
private final ThreadLocal<SimpleDateFormat> simpleDateFormat;
public LogFormatter() {
this(
GlobalState.THREAD_ID_TO_COMMAND_ID,
Locale.US,
TimeZone.getDefault());
}
@VisibleForTesting
LogFormatter(
ConcurrentMap<Long, String> threadIdToCommandId,
final Locale locale,
final TimeZone timeZone) {
this.threadIdToCommandId = threadIdToCommandId;
simpleDateFormat = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat format = new SimpleDateFormat(
"[yyyy-MM-dd HH:mm:ss.SSS]",
locale);
format.setTimeZone(timeZone);
return format;
}
};
}
@Override
public String format(LogRecord record) {
String timestamp = simpleDateFormat.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();
@Nullable String command = threadIdToCommandId.get(tid);
StringBuilder sb = new StringBuilder(timestamp)
.append(formatRecordLevel(record.getLevel()))
.append("[command:")
.append(command)
.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("] ")
.append(formatMessage(record))
.append("\n");
Throwable t = record.getThrown();
if (t != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
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);
}
}
}