View | Details | Raw Unified | Return to bug 49165
Collapse All | Expand All

(-)test/org/apache/catalina/valves/Benchmarks.java (-1 / +54 lines)
Lines 174-180 Link Here
174
                new TimeDateElementBenchmarkTest_Sync(),
174
                new TimeDateElementBenchmarkTest_Sync(),
175
                new TimeDateElementBenchmarkTest_Local(),
175
                new TimeDateElementBenchmarkTest_Local(),
176
                new TimeDateElementBenchmarkTest_LocalStruct(),
176
                new TimeDateElementBenchmarkTest_LocalStruct(),
177
                new TimeDateElementBenchmarkTest_LocalStruct_SBuilder() };
177
                new TimeDateElementBenchmarkTest_LocalStruct_SBuilder(),
178
                new TimeDateElementBenchmarkTest_LocalStruct_SimpleDateFormat()};
178
        benchmark.doTest(5, tests);
179
        benchmark.doTest(5, tests);
179
    }
180
    }
180
181
Lines 192-198 Link Here
192
            return (months[index]);
193
            return (months[index]);
193
        }
194
        }
194
    }
195
    }
196
    
197
    private static class TimeDateElementBenchmarkTest_LocalStruct_SimpleDateFormat extends
198
	    TimeDateElementBenchmarkTestBase implements Runnable {
195
199
200
201
202
        @Override
203
        public String toString() {
204
            return "single ThreadLocal SimpleDateFormat";
205
        }
206
207
        private static class Struct {
208
            public String currentDateString;
209
            public Date currentDate = new Date();
210
            private SimpleDateFormat sdf = new SimpleDateFormat("[dd/MMM/yyyy:hh:mm:ss");
211
        }
212
213
        private ThreadLocal<Struct> structLocal = new ThreadLocal<Struct>() {
214
            @Override
215
            protected Struct initialValue() {
216
                return new Struct();
217
            }
218
        };
219
220
        public void run() {
221
            printDate();
222
        }
223
224
        public String printDate() {
225
            getDateLocal();
226
            Struct struct = structLocal.get();
227
            if (struct.currentDateString == null) {
228
                struct.currentDateString = getOutput(struct.sdf, struct.currentDate);
229
            }
230
            return struct.currentDateString;
231
        }
232
233
        private Date getDateLocal() {
234
            Struct struct = structLocal.get();
235
            long systime = System.currentTimeMillis();
236
            if ((systime - struct.currentDate.getTime()) > 1000) {
237
                struct.currentDate.setTime(systime);
238
                struct.currentDateString = null;
239
            }
240
            return struct.currentDate;
241
        }
242
        
243
        private String getOutput(SimpleDateFormat sdf, Date date) {
244
             return sdf.format(date);
245
	}
246
247
    }
248
196
    private static class TimeDateElementBenchmarkTest_Sync extends
249
    private static class TimeDateElementBenchmarkTest_Sync extends
197
            TimeDateElementBenchmarkTestBase implements Runnable {
250
            TimeDateElementBenchmarkTestBase implements Runnable {
198
251
(-)java/org/apache/catalina/valves/AccessLogValve.java (-112 / +76 lines)
Lines 42-51 Link Here
42
import org.apache.catalina.connector.Request;
42
import org.apache.catalina.connector.Request;
43
import org.apache.catalina.connector.Response;
43
import org.apache.catalina.connector.Response;
44
import org.apache.catalina.util.LifecycleBase;
44
import org.apache.catalina.util.LifecycleBase;
45
import org.apache.tomcat.util.res.StringManager;
46
import org.apache.coyote.RequestInfo;
45
import org.apache.coyote.RequestInfo;
47
import org.apache.juli.logging.Log;
46
import org.apache.juli.logging.Log;
48
import org.apache.juli.logging.LogFactory;
47
import org.apache.juli.logging.LogFactory;
48
import org.apache.tomcat.util.res.StringManager;
49
49
50
50
51
/**
51
/**
Lines 75-80 Link Here
75
 * <li><b>%s</b> - HTTP status code of the response
75
 * <li><b>%s</b> - HTTP status code of the response
76
 * <li><b>%S</b> - User session ID
76
 * <li><b>%S</b> - User session ID
77
 * <li><b>%t</b> - Date and time, in Common Log Format format
77
 * <li><b>%t</b> - Date and time, in Common Log Format format
78
 * <li><b>%{xxx}t</b> - Date and time, in user format (see {@link SimpleDateFormat}).
79
 *     Warning: performance may be impacted if milliseconds are used in pattern.
78
 * <li><b>%u</b> - Remote user that was authenticated
80
 * <li><b>%u</b> - Remote user that was authenticated
79
 * <li><b>%U</b> - Requested URL path
81
 * <li><b>%U</b> - Requested URL path
80
 * <li><b>%v</b> - Local server name
82
 * <li><b>%v</b> - Local server name
Lines 152-165 Link Here
152
154
153
155
154
    /**
156
    /**
155
     * The set of month abbreviations for log messages.
156
     */
157
    protected static final String months[] =
158
    { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
159
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
160
161
162
    /**
163
     * enabled this component
157
     * enabled this component
164
     */
158
     */
165
    protected boolean enabled = true;
159
    protected boolean enabled = true;
Lines 215-269 Link Here
215
209
216
210
217
    /**
211
    /**
218
     * The system timezone.
212
     * The current log file we are writing to. Helpful when checkExists
213
     * is true.
219
     */
214
     */
220
    private TimeZone timezone = null;
215
    protected File currentLogFile = null;
221
222
    
216
    
217
    
223
    /**
218
    /**
224
     * The time zone offset relative to GMT in text form when daylight saving
219
     * Default Date and time format pattern (Common Log Format format).
225
     * is not in operation.
226
     */
220
     */
227
    private String timeZoneNoDST = null;
221
    private static final String defaultDateTimeFormat = "[dd/MMM/yyyy:HH:mm:ss Z]";
228
222
229
230
    /**
223
    /**
231
     * The time zone offset relative to GMT in text form when daylight saving
224
     * Current Date and time Format format for %t. Default value: Common Log 
232
     * is in operation.
225
     * Format format.
233
     */
226
     */
234
    private String timeZoneDST = null;
227
    private String dateTimeFormat = defaultDateTimeFormat;
235
    
228
236
    
237
    /**
238
     * The current log file we are writing to. Helpful when checkExists
239
     * is true.
240
     */
241
    protected File currentLogFile = null;
242
    private static class AccessDateStruct {
229
    private static class AccessDateStruct {
243
        private Date currentDate = new Date();
230
        private Date currentDate = new Date();
244
        private String currentDateString = null;
231
        private String currentDateString;
245
        private SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
232
        private SimpleDateFormat dateTimeFormatter;
246
        private SimpleDateFormat monthFormatter = new SimpleDateFormat("MM");
233
        
247
        private SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");
234
248
        private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss");
235
        /**
249
        public AccessDateStruct() {
236
         * Should we chunk %t output to thousand millis? Default is true (like 
250
            TimeZone tz = TimeZone.getDefault();
237
         * old behavior). Switching to false could impact performance.
251
            dayFormatter.setTimeZone(tz);
238
         */
252
            monthFormatter.setTimeZone(tz);
239
        private boolean thousandMillisDateTimeChunk = true;
253
            yearFormatter.setTimeZone(tz);
240
254
            timeFormatter.setTimeZone(tz);
241
242
        public AccessDateStruct(String dateFormat) {
243
            try {
244
	        dateTimeFormatter = new SimpleDateFormat(dateFormat);
245
	        dateTimeFormatter.setTimeZone(TimeZone.getDefault());
246
		thousandMillisDateTimeChunk = isPatternContainsMillis(dateFormat);
247
		currentDate = new Date();
248
		currentDateString = null;
249
	    } catch (IllegalArgumentException ex) {
250
		log.warn("invalid Date and time format for %t [" + dateFormat
251
		        + "]. Using Common Log Format format.");
252
	    }
255
        }
253
        }
254
255
        private boolean isPatternContainsMillis(String format) {
256
            return format.matches(".*S+.*");
257
        }
258
256
    }
259
    }
257
    
260
    
258
    /**
261
    /**
259
     * The system time when we last updated the Date that this valve
262
     * The system time when we last updated the Date that this valve
260
     * uses for log lines.
263
     * uses for log lines.
261
     */
264
     */
262
    private static final ThreadLocal<AccessDateStruct> currentDateStruct =
265
    private final ThreadLocal<AccessDateStruct> currentDateStruct =
263
            new ThreadLocal<AccessDateStruct>() {
266
            new ThreadLocal<AccessDateStruct>() {
264
        @Override
267
        @Override
265
        protected AccessDateStruct initialValue() {
268
        protected AccessDateStruct initialValue() {
266
            return new AccessDateStruct();
269
            return new AccessDateStruct(dateTimeFormat);
267
        }
270
        }
268
    };
271
    };
269
    /**
272
    /**
Lines 698-723 Link Here
698
        }
701
        }
699
702
700
    }
703
    }
704
    
701
705
702
703
    /**
706
    /**
704
     * Return the month abbreviation for the specified month, which must
705
     * be a two-digit String.
706
     *
707
     * @param month Month number ("01" .. "12").
708
     */
709
    private String lookup(String month) {
710
        int index;
711
        try {
712
            index = Integer.parseInt(month) - 1;
713
        } catch (Throwable t) {
714
            index = 0;  // Can not happen, in theory
715
        }
716
        return (months[index]);
717
    }
718
719
720
    /**
721
     * Open the new log file for the date specified by <code>dateStamp</code>.
707
     * Open the new log file for the date specified by <code>dateStamp</code>.
722
     */
708
     */
723
    protected synchronized void open() {
709
    protected synchronized void open() {
Lines 759-807 Link Here
759
     */
745
     */
760
    private Date getDate() {
746
    private Date getDate() {
761
        // Only create a new Date once per second, max.
747
        // Only create a new Date once per second, max.
762
        long systime = System.currentTimeMillis();
748
        long systime = getSysTime();
763
        AccessDateStruct struct = currentDateStruct.get(); 
749
        AccessDateStruct struct = currentDateStruct.get(); 
764
        if ((systime - struct.currentDate.getTime()) > 1000) {
750
	if (struct.thousandMillisDateTimeChunk
751
	        || (systime - struct.currentDate.getTime()) > 1000) {
765
            struct.currentDate.setTime(systime);
752
            struct.currentDate.setTime(systime);
766
            struct.currentDateString = null;
753
            struct.currentDateString = null;
767
        }
754
        }
768
        return struct.currentDate;
755
        return struct.currentDate;
769
    }
756
    }
770
771
772
    private String getTimeZone(Date date) {
773
        if (timezone.inDaylightTime(date)) {
774
            return timeZoneDST;
775
        } else {
776
            return timeZoneNoDST;
777
        }
778
    }
779
    
757
    
780
    
758
    /**
781
    private String calculateTimeZoneOffset(long offset) {
759
     * Current system time. Created as the hook for date and time-sensitive tests.
782
        StringBuilder tz = new StringBuilder();
760
     * @return current time in millis.
783
        if ((offset < 0)) {
761
     */
784
            tz.append("-");
762
    protected long getSysTime() {
785
            offset = -offset;
763
	return System.currentTimeMillis();
786
        } else {
787
            tz.append("+");
788
        }
789
790
        long hourOffset = offset / (1000 * 60 * 60);
791
        long minuteOffset = (offset / (1000 * 60)) % 60;
792
793
        if (hourOffset < 10)
794
            tz.append("0");
795
        tz.append(hourOffset);
796
797
        if (minuteOffset < 10)
798
            tz.append("0");
799
        tz.append(minuteOffset);
800
801
        return tz.toString();
802
    }
764
    }
803
765
804
805
    /**
766
    /**
806
     * Start this component and implement the requirements
767
     * Start this component and implement the requirements
807
     * of {@link LifecycleBase#startInternal()}.
768
     * of {@link LifecycleBase#startInternal()}.
Lines 813-827 Link Here
813
    protected synchronized void startInternal() throws LifecycleException {
774
    protected synchronized void startInternal() throws LifecycleException {
814
775
815
        // Initialize the timeZone, Date formatters, and currentDate
776
        // Initialize the timeZone, Date formatters, and currentDate
816
        timezone = TimeZone.getDefault();
817
        timeZoneNoDST = calculateTimeZoneOffset(timezone.getRawOffset());
818
        int offset = timezone.getDSTSavings();
819
        timeZoneDST = calculateTimeZoneOffset(timezone.getRawOffset() + offset);
820
821
        if (fileDateFormat == null || fileDateFormat.length() == 0)
777
        if (fileDateFormat == null || fileDateFormat.length() == 0)
822
            fileDateFormat = "yyyy-MM-dd";
778
            fileDateFormat = "yyyy-MM-dd";
779
823
        fileDateFormatter = new SimpleDateFormat(fileDateFormat);
780
        fileDateFormatter = new SimpleDateFormat(fileDateFormat);
824
        fileDateFormatter.setTimeZone(timezone);
781
        fileDateFormatter.setTimeZone(TimeZone.getDefault());
825
        dateStamp = fileDateFormatter.format(currentDateStruct.get().currentDate);
782
        dateStamp = fileDateFormatter.format(currentDateStruct.get().currentDate);
826
        open();
783
        open();
827
        
784
        
Lines 951-982 Link Here
951
908
952
    /**
909
    /**
953
     * write date and time, in Common Log Format - %t
910
     * write date and time, in Common Log Format - %t
911
     * write date and time, in specified log pattern - %{xxx}t (see {@link SimpleDateFormat})
954
     */
912
     */
955
    protected class DateAndTimeElement implements AccessLogElement {
913
    protected class DateAndTimeElement implements AccessLogElement {
956
        
914
        
915
        /**
916
         * Constructs old-style %t date and time format in Common Log Format
917
         */
918
        public DateAndTimeElement() {
919
            // Old-style %t
920
        }
957
        
921
        
958
922
        /**
959
923
         * Constructs custom %{TIME_FORMAT}t (see {@link SimpleDateFormat} for
924
         * format information). <br/>
925
         * Warning: performance may be impacted if milliseconds are used in 
926
         * pattern.
927
         * @param format user format which should be used to display date and time.
928
         */
929
        public DateAndTimeElement(String format) {
930
            dateTimeFormat = format;
931
        }
932
        
960
        public void addElement(StringBuilder buf, Date date, Request request,
933
        public void addElement(StringBuilder buf, Date date, Request request,
961
                Response response, long time) {
934
                Response response, long time) {
962
            AccessDateStruct struct = currentDateStruct.get();
935
            AccessDateStruct struct = currentDateStruct.get();
963
            if (struct.currentDateString == null) {
936
            if (struct.currentDateString == null) {
964
                StringBuilder current = new StringBuilder(32);
937
        	struct.currentDateString = struct.dateTimeFormatter.format(date);
965
                current.append('[');
966
                current.append(struct.dayFormatter.format(date));
967
                current.append('/');
968
                current.append(lookup(struct.monthFormatter.format(date)));
969
                current.append('/');
970
                current.append(struct.yearFormatter.format(date));
971
                current.append(':');
972
                current.append(struct.timeFormatter.format(date));
973
                current.append(' ');
974
                current.append(getTimeZone(date));
975
                current.append(']');
976
                struct.currentDateString = current.toString();
977
            }
938
            }
978
            buf.append(struct.currentDateString);
939
            buf.append(struct.currentDateString);
979
        }
940
        }
941
        
980
    }
942
    }
981
943
982
    /**
944
    /**
Lines 1370-1375 Link Here
1370
            return new RequestAttributeElement(header);
1332
            return new RequestAttributeElement(header);
1371
        case 's':
1333
        case 's':
1372
            return new SessionAttributeElement(header);            
1334
            return new SessionAttributeElement(header);            
1335
        case 't':
1336
        	return new DateAndTimeElement(header);            
1373
        default:
1337
        default:
1374
            return new StringElement("???");
1338
            return new StringElement("???");
1375
        }
1339
        }

Return to bug 49165