/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.juli;
/** * Provides same information as default log format but on a single line to make it easier to grep the logs. The only * exception is stacktraces which are always preceded by whitespace to make it simple to skip them.
*/ /* * Date processing based on AccessLogValve.
*/ publicclass OneLineFormatter extends Formatter {
privatestaticfinal String UNKNOWN_THREAD_NAME = "Unknown thread with ID "; privatestaticfinal Object threadMxBeanLock = new Object(); privatestaticvolatile ThreadMXBean threadMxBean = null; privatestaticfinalint THREAD_NAME_CACHE_SIZE = 10000; privatestaticfinal ThreadLocal<ThreadNameCache> threadNameCache = ThreadLocal
.withInitial(() -> new ThreadNameCache(THREAD_NAME_CACHE_SIZE));
/* Timestamp format */ privatestaticfinal String DEFAULT_TIME_FORMAT = "dd-MMM-yyyy HH:mm:ss.SSS";
/** * The size of our global date format cache
*/ privatestaticfinalint globalCacheSize = 30;
/** * The size of our thread local date format cache
*/ privatestaticfinalint localCacheSize = 5;
/** * Thread local date format cache.
*/ private ThreadLocal<DateFormatCache> localDateCache;
public OneLineFormatter() {
String timeFormat = LogManager.getLogManager().getProperty(OneLineFormatter.class.getName() + ".timeFormat"); if (timeFormat == null) {
timeFormat = DEFAULT_TIME_FORMAT;
}
setTimeFormat(timeFormat);
}
/** * Specify the time format to use for time stamps in log messages. * * @param timeFormat The format to use using the {@link java.text.SimpleDateFormat} syntax
*/ publicvoid setTimeFormat(final String timeFormat) { final String cachedTimeFormat;
final DateFormatCache globalDateCache = new DateFormatCache(globalCacheSize, cachedTimeFormat, null);
localDateCache = ThreadLocal
.withInitial(() -> new DateFormatCache(localCacheSize, cachedTimeFormat, globalDateCache));
}
/** * Obtain the format currently being used for time stamps in log messages. * * @return The current format in {@link java.text.SimpleDateFormat} syntax
*/ public String getTimeFormat() { return localDateCache.get().getTimeFormat();
}
@Override public String format(LogRecord record) {
StringBuilder sb = new StringBuilder();
// Thread
sb.append(' ');
sb.append('['); final String threadName = Thread.currentThread().getName(); if (threadName != null && threadName.startsWith(AsyncFileHandler.THREAD_PREFIX)) { // If using the async handler can't get the thread name from the // current thread.
sb.append(getThreadName(record.getThreadID()));
} else {
sb.append(threadName);
}
sb.append(']');
// New line for next record
sb.append(System.lineSeparator());
// Stack trace if (record.getThrown() != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new IndentingPrintWriter(sw);
record.getThrown().printStackTrace(pw);
pw.close();
sb.append(sw.getBuffer());
}
return sb.toString();
}
protectedvoid addTimestamp(StringBuilder buf, long timestamp) {
String cachedTimeStamp = localDateCache.get().getFormat(timestamp); if (millisHandling == MillisHandling.NONE) {
buf.append(cachedTimeStamp);
} elseif (millisHandling == MillisHandling.APPEND) {
buf.append(cachedTimeStamp); long frac = timestamp % 1000;
buf.append('.'); if (frac < 100) { if (frac < 10) {
buf.append('0');
buf.append('0');
} else {
buf.append('0');
}
}
buf.append(frac);
} else { // Some version of replace long frac = timestamp % 1000; // Formatted string may vary in length so the insert point may vary int insertStart = cachedTimeStamp.indexOf(DateFormatCache.MSEC_PATTERN);
buf.append(cachedTimeStamp.subSequence(0, insertStart)); if (frac < 100 && millisHandling == MillisHandling.REPLACE_SSS) {
buf.append('0'); if (frac < 10) {
buf.append('0');
}
} elseif (frac < 10 && millisHandling == MillisHandling.REPLACE_SS) {
buf.append('0');
}
buf.append(frac); if (millisHandling == MillisHandling.REPLACE_SSS) {
buf.append(cachedTimeStamp.substring(insertStart + 3));
} elseif (millisHandling == MillisHandling.REPLACE_SS) {
buf.append(cachedTimeStamp.substring(insertStart + 2));
} else {
buf.append(cachedTimeStamp.substring(insertStart + 1));
}
}
}
/** * LogRecord has threadID but no thread name. LogRecord uses an int for thread ID but thread IDs are longs. If the * real thread ID > (Integer.MAXVALUE / 2) LogRecord uses it's own ID in an effort to avoid clashes due to overflow. * <p> * Words fail me to describe what I think of the design decision to use an int in LogRecord for a long value and the * resulting mess that follows.
*/ privatestatic String getThreadName(int logRecordThreadId) {
Map<Integer, String> cache = threadNameCache.get();
String result = cache.get(Integer.valueOf(logRecordThreadId));
if (result != null) { return result;
}
if (logRecordThreadId > Integer.MAX_VALUE / 2) {
result = UNKNOWN_THREAD_NAME + logRecordThreadId;
} else { // Double checked locking OK as threadMxBean is volatile if (threadMxBean == null) { synchronized (threadMxBeanLock) { if (threadMxBean == null) {
threadMxBean = ManagementFactory.getThreadMXBean();
}
}
}
ThreadInfo threadInfo = threadMxBean.getThreadInfo(logRecordThreadId); if (threadInfo == null) { returnLong.toString(logRecordThreadId);
}
result = threadInfo.getThreadName();
}
/* * Minimal implementation to indent the printing of stack traces. This implementation depends on Throwable using * WrappedPrintWriter.
*/ privatestaticclass IndentingPrintWriter extends PrintWriter {
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.