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 package org.apache.logging.log4j.core.pattern;
018
019 import java.text.SimpleDateFormat;
020 import java.util.Date;
021 import java.util.TimeZone;
022
023 import org.apache.logging.log4j.core.LogEvent;
024 import org.apache.logging.log4j.core.config.plugins.Plugin;
025
026 /**
027 * Convert and format the event's date in a StringBuilder.
028 */
029 @Plugin(name = "DatePatternConverter", category = PatternConverter.CATEGORY)
030 @ConverterKeys({ "d", "date" })
031 public final class DatePatternConverter extends LogEventPatternConverter implements ArrayPatternConverter {
032
033 private abstract static class Formatter {
034 abstract String format(long time);
035
036 public String toPattern() {
037 return null;
038 }
039 }
040
041 private static class PatternFormatter extends Formatter {
042 private final SimpleDateFormat simpleDateFormat;
043
044 PatternFormatter(final SimpleDateFormat simpleDateFormat) {
045 this.simpleDateFormat = simpleDateFormat;
046 }
047
048 @Override
049 String format(final long time) {
050 return simpleDateFormat.format(Long.valueOf(time));
051 }
052
053 @Override
054 public String toPattern() {
055 return simpleDateFormat.toPattern();
056 }
057 }
058
059 private static class UnixFormatter extends Formatter {
060
061 @Override
062 String format(final long time) {
063 return Long.toString(time / 1000);
064 }
065
066 }
067
068 private static class UnixMillisFormatter extends Formatter {
069
070 @Override
071 String format(final long time) {
072 return Long.toString(time);
073 }
074
075 }
076
077 /**
078 * ABSOLUTE string literal.
079 */
080 private static final String ABSOLUTE_FORMAT = "ABSOLUTE";
081
082 /**
083 * SimpleTimePattern for ABSOLUTE.
084 */
085 private static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
086
087 /**
088 * COMPACT string literal.
089 */
090 private static final String COMPACT_FORMAT = "COMPACT";
091
092 /**
093 * SimpleTimePattern for COMPACT.
094 */
095 private static final String COMPACT_PATTERN = "yyyyMMddHHmmssSSS";
096
097 /**
098 * DATE string literal.
099 */
100 private static final String DATE_AND_TIME_FORMAT = "DATE";
101
102 /**
103 * SimpleTimePattern for DATE.
104 */
105 private static final String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS";
106
107 /**
108 * DEFAULT string literal.
109 */
110 private static final String DEFAULT_FORMAT = "DEFAULT";
111
112 /**
113 * SimpleTimePattern for DEFAULT.
114 */
115 // package private for unit tests
116 static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
117
118 /**
119 * ISO8601_BASIC string literal.
120 */
121 private static final String ISO8601_BASIC_FORMAT = "ISO8601_BASIC";
122
123 /**
124 * SimpleTimePattern for ISO8601_BASIC.
125 */
126 private static final String ISO8601_BASIC_PATTERN = "yyyyMMdd'T'HHmmss,SSS";
127
128 /**
129 * ISO8601 string literal.
130 */
131 // package private for unit tests
132 static final String ISO8601_FORMAT = "ISO8601";
133
134 /**
135 * SimpleTimePattern for ISO8601.
136 */
137 // package private for unit tests
138 static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss,SSS";
139
140 /**
141 * UNIX formatter in seconds (standard).
142 */
143 private static final String UNIX_FORMAT = "UNIX";
144
145 /**
146 * UNIX formatter in milliseconds
147 */
148 private static final String UNIX_MILLIS_FORMAT = "UNIX_MILLIS";
149
150 /**
151 * Obtains an instance of pattern converter.
152 *
153 * @param options
154 * options, may be null.
155 * @return instance of pattern converter.
156 */
157 public static DatePatternConverter newInstance(final String[] options) {
158 return new DatePatternConverter(options);
159 }
160
161 /**
162 * Date format.
163 */
164 private String cachedDateString;
165
166 private final Formatter formatter;
167
168 private long lastTimestamp;
169
170 /**
171 * Private constructor.
172 *
173 * @param options
174 * options, may be null.
175 */
176 private DatePatternConverter(final String[] options) {
177 super("Date", "date");
178
179 // null patternOption is OK.
180 final String patternOption = options != null && options.length > 0 ? options[0] : null;
181
182 String pattern = null;
183 Formatter tempFormatter = null;
184
185 if (patternOption == null || patternOption.equalsIgnoreCase(DEFAULT_FORMAT)) {
186 pattern = DEFAULT_PATTERN;
187 } else if (patternOption.equalsIgnoreCase(ISO8601_FORMAT)) {
188 pattern = ISO8601_PATTERN;
189 } else if (patternOption.equalsIgnoreCase(ISO8601_BASIC_FORMAT)) {
190 pattern = ISO8601_BASIC_PATTERN;
191 } else if (patternOption.equalsIgnoreCase(ABSOLUTE_FORMAT)) {
192 pattern = ABSOLUTE_TIME_PATTERN;
193 } else if (patternOption.equalsIgnoreCase(DATE_AND_TIME_FORMAT)) {
194 pattern = DATE_AND_TIME_PATTERN;
195 } else if (patternOption.equalsIgnoreCase(COMPACT_FORMAT)) {
196 pattern = COMPACT_PATTERN;
197 } else if (patternOption.equalsIgnoreCase(UNIX_FORMAT)) {
198 tempFormatter = new UnixFormatter();
199 } else if (patternOption.equalsIgnoreCase(UNIX_MILLIS_FORMAT)) {
200 tempFormatter = new UnixMillisFormatter();
201 } else {
202 pattern = patternOption;
203 }
204
205 if (pattern != null) {
206 SimpleDateFormat tempFormat;
207
208 try {
209 tempFormat = new SimpleDateFormat(pattern);
210 } catch (final IllegalArgumentException e) {
211 LOGGER.warn("Could not instantiate SimpleDateFormat with pattern " + patternOption, e);
212
213 // default to the DEFAULT format
214 tempFormat = new SimpleDateFormat(DEFAULT_PATTERN);
215 }
216
217 // if the option list contains a TZ option, then set it.
218 if (options != null && options.length > 1) {
219 final TimeZone tz = TimeZone.getTimeZone(options[1]);
220 tempFormat.setTimeZone(tz);
221 }
222 tempFormatter = new PatternFormatter(tempFormat);
223 }
224 formatter = tempFormatter;
225 }
226
227 /**
228 * Append formatted date to string buffer.
229 *
230 * @param date
231 * date
232 * @param toAppendTo
233 * buffer to which formatted date is appended.
234 */
235 public void format(final Date date, final StringBuilder toAppendTo) {
236 synchronized (this) {
237 toAppendTo.append(formatter.format(date.getTime()));
238 }
239 }
240
241 /**
242 * {@inheritDoc}
243 */
244 @Override
245 public void format(final LogEvent event, final StringBuilder output) {
246 final long timestamp = event.getTimeMillis();
247
248 synchronized (this) {
249 if (timestamp != lastTimestamp) {
250 lastTimestamp = timestamp;
251 cachedDateString = formatter.format(timestamp);
252 }
253 }
254 output.append(cachedDateString);
255 }
256
257 /**
258 * {@inheritDoc}
259 */
260 @Override
261 public void format(final Object obj, final StringBuilder output) {
262 if (obj instanceof Date) {
263 format((Date) obj, output);
264 }
265 super.format(obj, output);
266 }
267
268 @Override
269 public void format(final StringBuilder toAppendTo, final Object... objects) {
270 for (final Object obj : objects) {
271 if (obj instanceof Date) {
272 format(obj, toAppendTo);
273 break;
274 }
275 }
276 }
277
278 /**
279 * Gets the pattern string describing this date format.
280 *
281 * @return the pattern string describing this date format.
282 */
283 public String getPattern() {
284 return formatter.toPattern();
285 }
286
287 }