Author: rjung Date: Mon Sep 26 08:55:14 2011 New Revision: 1175721 URL: http://svn.apache.org/viewvc?rev=1175721&view=rev Log: Backport (copy in) JULI OneLineFormatter plus DateFormatCache helper class from TC 7 JULI.
Added: tomcat/tc6.0.x/trunk/java/org/apache/juli/DateFormatCache.java (with props) tomcat/tc6.0.x/trunk/java/org/apache/juli/OneLineFormatter.java (with props) Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Added: tomcat/tc6.0.x/trunk/java/org/apache/juli/DateFormatCache.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/juli/DateFormatCache.java?rev=1175721&view=auto ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/juli/DateFormatCache.java (added) +++ tomcat/tc6.0.x/trunk/java/org/apache/juli/DateFormatCache.java Mon Sep 26 08:55:14 2011 @@ -0,0 +1,191 @@ +/* + * 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; + + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + * <p>Cache structure for SimpleDateFormat formatted timestamps based on + * seconds.</p> + * + * <p>Millisecond formatting using S is not supported. You should add the + * millisecond information after getting back the second formatting.</p> + * + * <p>The cache consists of entries for a consecutive range of + * seconds. The length of the range is configurable. It is + * implemented based on a cyclic buffer. New entries shift the range.</p> + * + * <p>The cache is not threadsafe. It can be used without synchronization + * via thread local instances, or with synchronization as a global cache.</p> + * + * <p>The cache can be created with a parent cache to build a cache hierarchy. + * Access to the parent cache is threadsafe.</p> + * + * @version $Id$ + */ + +public class DateFormatCache { + + private static final String msecPattern = "#"; + + /* Timestamp format */ + private final String format; + + /* Number of cached entries */ + private int cacheSize = 0; + + private Cache cache; + + /** + * Replace the millisecond formatting character 'S' by + * some dummy characters in order to make the resulting + * formatted time stamps cacheable. Our consumer might + * choose to replace the dummies with the actual milliseconds + * because that's relatively cheap. + */ + private String tidyFormat(String format) { + boolean escape = false; + StringBuilder result = new StringBuilder(); + int len = format.length(); + char x; + for (int i = 0; i < len; i++) { + x = format.charAt(i); + if (escape || x != 'S') { + result.append(x); + } else { + result.append(msecPattern); + } + if (x == '\'') { + escape = !escape; + } + } + return result.toString(); + } + + public DateFormatCache(int size, String format, DateFormatCache parent) { + cacheSize = size; + this.format = tidyFormat(format); + Cache parentCache = null; + if (parent != null) { + synchronized(parent) { + parentCache = parent.cache; + } + } + cache = new Cache(parentCache); + } + + public String getFormat(long time) { + return cache.getFormat(time); + } + + private class Cache { + + /* Second formatted in most recent invocation */ + private long previousSeconds = 0L; + /* Formatted timestamp generated in most recent invocation */ + private String previousFormat = ""; + + /* First second contained in cache */ + private long first = 0L; + /* Last second contained in cache */ + private long last = 0L; + /* Index of "first" in the cyclic cache */ + private int offset = 0; + /* Helper object to be able to call SimpleDateFormat.format(). */ + private final Date currentDate = new Date(); + + private String cache[]; + private SimpleDateFormat formatter; + + private Cache parent = null; + + private Cache(Cache parent) { + cache = new String[cacheSize]; + for (int i = 0; i < cacheSize; i++) { + cache[i] = null; + } + formatter = new SimpleDateFormat(format); + formatter.setTimeZone(TimeZone.getDefault()); + this.parent = parent; + } + + private String getFormat(long time) { + + long seconds = time / 1000; + + /* First step: if we have seen this timestamp + during the previous call, return the previous value. */ + if (seconds == previousSeconds) { + return previousFormat; + } + + /* Second step: Try to locate in cache */ + previousSeconds = seconds; + int index = (offset + (int)(seconds - first)) % cacheSize; + if (index < 0) { + index += cacheSize; + } + if (seconds >= first && seconds <= last) { + if (cache[index] != null) { + /* Found, so remember for next call and return.*/ + previousFormat = cache[index]; + return previousFormat; + } + + /* Third step: not found in cache, adjust cache and add item */ + } else if (seconds >= last + cacheSize || seconds <= first - cacheSize) { + first = seconds; + last = first + cacheSize - 1; + index = 0; + offset = 0; + for (int i = 1; i < cacheSize; i++) { + cache[i] = null; + } + } else if (seconds > last) { + for (int i = 1; i < seconds - last; i++) { + cache[(index + cacheSize - i) % cacheSize] = null; + } + first = seconds - cacheSize; + last = seconds; + } else if (seconds < first) { + for (int i = 1; i < first - seconds; i++) { + cache[(index + i) % cacheSize] = null; + } + first = seconds; + last = seconds + cacheSize; + } + + /* Last step: format new timestamp either using + * parent cache or locally. */ + if (parent != null) { + synchronized(parent) { + previousFormat = parent.getFormat(time); + } + } else { + currentDate.setTime(time); + previousFormat = formatter.format(currentDate); + } + cache[index] = previousFormat; + return previousFormat; + } + } +} Propchange: tomcat/tc6.0.x/trunk/java/org/apache/juli/DateFormatCache.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/tc6.0.x/trunk/java/org/apache/juli/DateFormatCache.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Added: tomcat/tc6.0.x/trunk/java/org/apache/juli/OneLineFormatter.java URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/juli/OneLineFormatter.java?rev=1175721&view=auto ============================================================================== --- tomcat/tc6.0.x/trunk/java/org/apache/juli/OneLineFormatter.java (added) +++ tomcat/tc6.0.x/trunk/java/org/apache/juli/OneLineFormatter.java Mon Sep 26 08:55:14 2011 @@ -0,0 +1,125 @@ +/* + * 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; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.logging.Formatter; +import java.util.logging.LogRecord; + +/** + * 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. + */ +public class OneLineFormatter extends Formatter { + + private static final String LINE_SEP = System.getProperty("line.separator"); + private static final String ST_SEP = LINE_SEP + " "; + + /* Timestamp format */ + private static final String timeFormat = "dd-MMM-yyyy HH:mm:ss"; + + /** + * The size of our global date format cache + */ + private static final int globalCacheSize = 30; + + /** + * The size of our thread local date format cache + */ + private static final int localCacheSize = 5; + + /** + * Global date format cache. + */ + private static final DateFormatCache globalDateCache = + new DateFormatCache(globalCacheSize, timeFormat, null); + + /** + * Thread local date format cache. + */ + private static final ThreadLocal<DateFormatCache> localDateCache = + new ThreadLocal<DateFormatCache>() { + @Override + protected DateFormatCache initialValue() { + return new DateFormatCache(localCacheSize, timeFormat, globalDateCache); + } + }; + + @Override + public String format(LogRecord record) { + StringBuilder sb = new StringBuilder(); + + // Timestamp + addTimestamp(sb, record.getMillis()); + + // Severity + sb.append(' '); + sb.append(record.getLevel()); + + // Thread + sb.append(' '); + sb.append('['); + sb.append(Thread.currentThread().getName()); + sb.append(']'); + + // Source + sb.append(' '); + sb.append(record.getSourceClassName()); + sb.append('.'); + sb.append(record.getSourceMethodName()); + + // Message + sb.append(' '); + sb.append(formatMessage(record)); + + // Stack trace + if (record.getThrown() != null) { + sb.append(ST_SEP); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + record.getThrown().printStackTrace(pw); + pw.close(); + sb.append(sw.getBuffer()); + } + + // New line for next record + sb.append(LINE_SEP); + + return sb.toString(); + } + + protected void addTimestamp(StringBuilder buf, long timestamp) { + buf.append(localDateCache.get().getFormat(timestamp)); + 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); + } +} Propchange: tomcat/tc6.0.x/trunk/java/org/apache/juli/OneLineFormatter.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/tc6.0.x/trunk/java/org/apache/juli/OneLineFormatter.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=1175721&r1=1175720&r2=1175721&view=diff ============================================================================== --- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Mon Sep 26 08:55:14 2011 @@ -65,6 +65,11 @@ Allow to overwrite the check for distributability of session attributes by session implementations. (rjung) </add> + <add> + Provide the log format "OneLineFormatter" for JULI that provides the same + information as the default plus thread name but on a single line. + (markt/rjung) + </add> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org