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 */
017package org.apache.logging.log4j.core.pattern;
018
019import java.text.SimpleDateFormat;
020import java.util.Date;
021import java.util.TimeZone;
022
023import org.apache.logging.log4j.core.LogEvent;
024import 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 = "Converter")
030@ConverterKeys({ "d", "date" })
031public 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(SimpleDateFormat simpleDateFormat) {
045            this.simpleDateFormat = simpleDateFormat;
046        }
047
048        @Override
049        String format(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(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(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     * ISO8601_BASIC string literal.
109     */
110    private static final String ISO8601_BASIC_FORMAT = "ISO8601_BASIC";
111
112    /**
113     * SimpleTimePattern for ISO8601_BASIC.
114     */
115    private static final String ISO8601_BASIC_PATTERN = "yyyyMMdd HHmmss,SSS";
116
117    /**
118     * ISO8601 string literal.
119     */
120    private static final String ISO8601_FORMAT = "ISO8601";
121
122    /**
123     * SimpleTimePattern for ISO8601.
124     */
125    private static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
126
127    /**
128     * UNIX formatter in seconds (standard).
129     */
130    private static final String UNIX_FORMAT = "UNIX";
131
132    /**
133     * UNIX formatter in milliseconds
134     */
135    private static final String UNIX_MILLIS_FORMAT = "UNIX_MILLIS";
136
137    /**
138     * Obtains an instance of pattern converter.
139     * 
140     * @param options
141     *            options, may be null.
142     * @return instance of pattern converter.
143     */
144    public static DatePatternConverter newInstance(final String[] options) {
145        return new DatePatternConverter(options);
146    }
147
148    /**
149     * Date format.
150     */
151    private String cachedDateString;
152
153    private final Formatter formatter;
154
155    private long lastTimestamp;
156
157    /**
158     * Private constructor.
159     * 
160     * @param options
161     *            options, may be null.
162     */
163    private DatePatternConverter(final String[] options) {
164        super("Date", "date");
165
166        // null patternOption is OK.
167        final String patternOption = options != null && options.length > 0 ? options[0] : null;
168
169        String pattern = null;
170        Formatter tempFormatter = null;
171
172        if (patternOption == null || patternOption.equalsIgnoreCase(ISO8601_FORMAT)) {
173            pattern = ISO8601_PATTERN;
174        } else if (patternOption.equalsIgnoreCase(ISO8601_BASIC_FORMAT)) {
175            pattern = ISO8601_BASIC_PATTERN;
176        } else if (patternOption.equalsIgnoreCase(ABSOLUTE_FORMAT)) {
177            pattern = ABSOLUTE_TIME_PATTERN;
178        } else if (patternOption.equalsIgnoreCase(DATE_AND_TIME_FORMAT)) {
179            pattern = DATE_AND_TIME_PATTERN;
180        } else if (patternOption.equalsIgnoreCase(COMPACT_FORMAT)) {
181            pattern = COMPACT_PATTERN;
182        } else if (patternOption.equalsIgnoreCase(UNIX_FORMAT)) {
183            tempFormatter = new UnixFormatter();
184        } else if (patternOption.equalsIgnoreCase(UNIX_MILLIS_FORMAT)) {
185            tempFormatter = new UnixMillisFormatter();
186        } else {
187            pattern = patternOption;
188        }
189
190        if (pattern != null) {
191            SimpleDateFormat tempFormat;
192
193            try {
194                tempFormat = new SimpleDateFormat(pattern);
195            } catch (final IllegalArgumentException e) {
196                LOGGER.warn("Could not instantiate SimpleDateFormat with pattern " + patternOption, e);
197
198                // default to the ISO8601 format
199                tempFormat = new SimpleDateFormat(ISO8601_PATTERN);
200            }
201
202            // if the option list contains a TZ option, then set it.
203            if (options != null && options.length > 1) {
204                final TimeZone tz = TimeZone.getTimeZone(options[1]);
205                tempFormat.setTimeZone(tz);
206            }
207            tempFormatter = new PatternFormatter(tempFormat);
208        }
209        formatter = tempFormatter;
210    }
211
212    /**
213     * Append formatted date to string buffer.
214     * 
215     * @param date
216     *            date
217     * @param toAppendTo
218     *            buffer to which formatted date is appended.
219     */
220    public void format(final Date date, final StringBuilder toAppendTo) {
221        synchronized (this) {
222            toAppendTo.append(formatter.format(date.getTime()));
223        }
224    }
225
226    /**
227     * {@inheritDoc}
228     */
229    @Override
230    public void format(final LogEvent event, final StringBuilder output) {
231        final long timestamp = event.getMillis();
232
233        synchronized (this) {
234            if (timestamp != lastTimestamp) {
235                lastTimestamp = timestamp;
236                cachedDateString = formatter.format(timestamp);
237            }
238        }
239        output.append(cachedDateString);
240    }
241
242    /**
243     * {@inheritDoc}
244     */
245    @Override
246    public void format(final Object obj, final StringBuilder output) {
247        if (obj instanceof Date) {
248            format((Date) obj, output);
249        }
250        super.format(obj, output);
251    }
252
253    @Override
254    public void format(final StringBuilder toAppendTo, final Object... objects) {
255        for (final Object obj : objects) {
256            if (obj instanceof Date) {
257                format(obj, toAppendTo);
258                break;
259            }
260        }
261    }
262
263    /**
264     * Gets the pattern string describing this date format.
265     * 
266     * @return the pattern string describing this date format.
267     */
268    public String getPattern() {
269        return formatter.toPattern();
270    }
271
272}