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 = "Converter")
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(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.getTimeMillis();
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    }