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    }