Author: rjung Date: Mon Jun 20 19:47:57 2011 New Revision: 1137758 URL: http://svn.apache.org/viewvc?rev=1137758&view=rev Log: Let OneLineFormatter use DateFormatCache.
Added: tomcat/trunk/java/org/apache/juli/DateFormatCache.java (with props) Modified: tomcat/trunk/java/org/apache/juli/OneLineFormatter.java tomcat/trunk/webapps/docs/changelog.xml Added: tomcat/trunk/java/org/apache/juli/DateFormatCache.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/juli/DateFormatCache.java?rev=1137758&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/juli/DateFormatCache.java (added) +++ tomcat/trunk/java/org/apache/juli/DateFormatCache.java Mon Jun 20 19:47:57 2011 @@ -0,0 +1,166 @@ +/* + * 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 formatted timestamps based on seconds.</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> + * + * @version $Id$ + */ + +public class DateFormatCache { + + /* Timestamp format */ + private final String format; + + /* Number of cached entries */ + private int cacheSize = 0; + + private DateFormatCache parent; + private Cache cache; + + /* Cache type, "parent" or "child" */ + private String type; + + public DateFormatCache(int size, String format, DateFormatCache parent) { + if (parent == null) { + type = "main"; + } else { + type = "child"; + } + cacheSize = size; + this.format = format; + this.parent = parent; + 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/trunk/java/org/apache/juli/DateFormatCache.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: tomcat/trunk/java/org/apache/juli/DateFormatCache.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Modified: tomcat/trunk/java/org/apache/juli/OneLineFormatter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/juli/OneLineFormatter.java?rev=1137758&r1=1137757&r2=1137758&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/juli/OneLineFormatter.java (original) +++ tomcat/trunk/java/org/apache/juli/OneLineFormatter.java Mon Jun 20 19:47:57 2011 @@ -19,8 +19,6 @@ package org.apache.juli; import java.io.PrintWriter; import java.io.StringWriter; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.logging.Formatter; import java.util.logging.LogRecord; @@ -34,45 +32,59 @@ import java.util.logging.LogRecord; */ 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 set of month abbreviations for log messages. + * The size of our global date format cache */ - private static final String months[] = {"Jan", "Feb", "Mar", "Apr", "May", - "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + private static final int globalCacheSize = 30; - private static final String LINE_SEP = System.getProperty("line.separator"); - private static final String ST_SEP = LINE_SEP + " "; + /** + * 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); - private final SimpleDateFormat dayFormatter = new SimpleDateFormat("dd"); - private final SimpleDateFormat monthFormatter = new SimpleDateFormat("MM"); - private final SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy"); - private final SimpleDateFormat timeFormatter = - new SimpleDateFormat("HH:mm:ss.SSS"); - - private volatile Date currentDate; - private volatile String currentDateString; + /** + * Thread local date format cache. + */ + private static final ThreadLocal<DateFormatCache> localDateCache = + new ThreadLocal<DateFormatCache>() { + protected DateFormatCache initialValue() { + return new DateFormatCache(localCacheSize, timeFormat, globalDateCache); + } + }; @Override public String format(LogRecord record) { StringBuilder sb = new StringBuilder(); - + // Timestamp - addTimestamp(sb, new Date(record.getMillis())); - + addTimestamp(sb, record.getMillis()); + // Severity sb.append(' '); sb.append(record.getLevel()); - + // 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); @@ -89,39 +101,17 @@ public class OneLineFormatter extends Fo return sb.toString(); } - protected void addTimestamp(StringBuilder buf, Date date) { - if (currentDate.getTime() != date.getTime()) { - synchronized (this) { - if (currentDate.getTime() != date.getTime()) { - StringBuilder current = new StringBuilder(32); - current.append(dayFormatter.format(date)); // Day - current.append('-'); - current.append(lookup(monthFormatter.format(date))); // Month - current.append('-'); - current.append(yearFormatter.format(date)); // Year - current.append(' '); - current.append(timeFormatter.format(date)); // Time - currentDateString = current.toString(); - currentDate = date; - } + 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("00"); + } else { + buf.append("0"); } } - buf.append(currentDateString); - } - - /** - * Return the month abbreviation for the specified month, which must - * be a two-digit String. - * - * @param month Month number ("01" .. "12"). - */ - private String lookup(String month) { - int index; - try { - index = Integer.parseInt(month) - 1; - } catch (Exception e) { - index = 0; // Can not happen, in theory - } - return (months[index]); + buf.append(frac); } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1137758&r1=1137757&r2=1137758&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Mon Jun 20 19:47:57 2011 @@ -81,9 +81,9 @@ <fix> Correct issues with JULI's OneLineFormatter including: correctly re-using formatted timestamps when possible; thread-safety issues in - tiomstamp formatting; correcting the output of any milliseconds to + timestamp formatting; correcting the output of any milliseconds to include leading zeros and formatting any parameters present. - (kolinko/markt) + (kolinko/markt/rjung) </fix> <fix> <bug>51395</bug>: Fix memory leak triggered when an application that --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org