001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j.pattern; 019 020import org.apache.log4j.helpers.LogLog; 021import org.apache.log4j.spi.LoggingEvent; 022 023import java.text.SimpleDateFormat; 024import java.text.DateFormat; 025import java.text.FieldPosition; 026import java.text.ParsePosition; 027import java.util.Date; 028import java.util.TimeZone; 029 030 031/** 032 * Convert and format the event's date in a StringBuffer. 033 * 034 * @author Ceki Gülcü 035 */ 036public final class DatePatternConverter extends LoggingEventPatternConverter { 037 /** 038 * ABSOLUTE string literal. 039 */ 040 private static final String ABSOLUTE_FORMAT = "ABSOLUTE"; 041 /** 042 * SimpleTimePattern for ABSOLUTE. 043 */ 044 private static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS"; 045 046 047 /** 048 * DATE string literal. 049 */ 050 private static final String DATE_AND_TIME_FORMAT = "DATE"; 051 /** 052 * SimpleTimePattern for DATE. 053 */ 054 private static final String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS"; 055 056 /** 057 * ISO8601 string literal. 058 */ 059 private static final String ISO8601_FORMAT = "ISO8601"; 060 /** 061 * SimpleTimePattern for ISO8601. 062 */ 063 private static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS"; 064 /** 065 * Date format. 066 */ 067 private final CachedDateFormat df; 068 069 /** 070 * This class wraps a DateFormat and forces the time zone to the 071 * default time zone before each format and parse request. 072 */ 073 private static class DefaultZoneDateFormat extends DateFormat { 074 /** 075 * Serialization version ID. 076 */ 077 private static final long serialVersionUID = 1; 078 /** 079 * Wrapped instance of DateFormat. 080 */ 081 private final DateFormat dateFormat; 082 083 /** 084 * Construct new instance. 085 * @param format format, may not be null. 086 */ 087 public DefaultZoneDateFormat(final DateFormat format) { 088 dateFormat = format; 089 } 090 091 /** 092 * @{inheritDoc} 093 */ 094 public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { 095 dateFormat.setTimeZone(TimeZone.getDefault()); 096 return dateFormat.format(date, toAppendTo, fieldPosition); 097 } 098 099 /** 100 * @{inheritDoc} 101 */ 102 public Date parse(String source, ParsePosition pos) { 103 dateFormat.setTimeZone(TimeZone.getDefault()); 104 return dateFormat.parse(source, pos); 105 } 106 } 107 108 /** 109 * Private constructor. 110 * @param options options, may be null. 111 */ 112 private DatePatternConverter(final String[] options) { 113 super("Date", "date"); 114 115 String patternOption; 116 117 if ((options == null) || (options.length == 0)) { 118 // the branch could be optimized, but here we are making explicit 119 // that null values for patternOption are allowed. 120 patternOption = null; 121 } else { 122 patternOption = options[0]; 123 } 124 125 String pattern; 126 127 if ( 128 (patternOption == null) 129 || patternOption.equalsIgnoreCase(ISO8601_FORMAT)) { 130 pattern = ISO8601_PATTERN; 131 } else if (patternOption.equalsIgnoreCase(ABSOLUTE_FORMAT)) { 132 pattern = ABSOLUTE_TIME_PATTERN; 133 } else if (patternOption.equalsIgnoreCase(DATE_AND_TIME_FORMAT)) { 134 pattern = DATE_AND_TIME_PATTERN; 135 } else { 136 pattern = patternOption; 137 } 138 139 int maximumCacheValidity = 1000; 140 DateFormat simpleFormat = null; 141 142 try { 143 simpleFormat = new SimpleDateFormat(pattern); 144 maximumCacheValidity = CachedDateFormat.getMaximumCacheValidity(pattern); 145 } catch (IllegalArgumentException e) { 146 LogLog.warn( 147 "Could not instantiate SimpleDateFormat with pattern " 148 + patternOption, e); 149 150 // default to the ISO8601 format 151 simpleFormat = new SimpleDateFormat(ISO8601_PATTERN); 152 } 153 154 // if the option list contains a TZ option, then set it. 155 if ((options != null) && (options.length > 1)) { 156 TimeZone tz = TimeZone.getTimeZone((String) options[1]); 157 simpleFormat.setTimeZone(tz); 158 } else { 159 simpleFormat = new DefaultZoneDateFormat(simpleFormat); 160 } 161 162 df = new CachedDateFormat(simpleFormat, maximumCacheValidity); 163 } 164 165 /** 166 * Obtains an instance of pattern converter. 167 * @param options options, may be null. 168 * @return instance of pattern converter. 169 */ 170 public static DatePatternConverter newInstance( 171 final String[] options) { 172 return new DatePatternConverter(options); 173 } 174 175 /** 176 * {@inheritDoc} 177 */ 178 public void format(final LoggingEvent event, final StringBuffer output) { 179 synchronized(this) { 180 df.format(event.timeStamp, output); 181 } 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 public void format(final Object obj, final StringBuffer output) { 188 if (obj instanceof Date) { 189 format((Date) obj, output); 190 } 191 192 super.format(obj, output); 193 } 194 195 /** 196 * Append formatted date to string buffer. 197 * @param date date 198 * @param toAppendTo buffer to which formatted date is appended. 199 */ 200 public void format(final Date date, final StringBuffer toAppendTo) { 201 synchronized(this) { 202 df.format(date.getTime(), toAppendTo); 203 } 204 } 205}