View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.pattern;
18  
19  import java.util.Date;
20  import java.util.Objects;
21  import java.util.TimeZone;
22  import java.util.concurrent.atomic.AtomicReference;
23  
24  import org.apache.logging.log4j.core.LogEvent;
25  import org.apache.logging.log4j.core.config.plugins.Plugin;
26  import org.apache.logging.log4j.core.util.datetime.FastDateFormat;
27  import org.apache.logging.log4j.core.util.datetime.FixedDateFormat;
28  import org.apache.logging.log4j.core.util.datetime.FixedDateFormat.FixedFormat;
29  
30  /**
31   * Converts and formats the event's date in a StringBuilder.
32   */
33  @Plugin(name = "DatePatternConverter", category = PatternConverter.CATEGORY)
34  @ConverterKeys({ "d", "date" })
35  public final class DatePatternConverter extends LogEventPatternConverter implements ArrayPatternConverter {
36  
37      private final class CachedTime {
38          public long timestampMillis;
39          public String formatted;
40  
41          public CachedTime(final long timestampMillis) {
42              this.timestampMillis = timestampMillis;
43              this.formatted = formatter.format(this.timestampMillis);
44          }
45      }
46  
47      private final AtomicReference<CachedTime> cachedTime;
48  
49      private abstract static class Formatter {
50          abstract String format(long timeMillis);
51  
52          public String toPattern() {
53              return null;
54          }
55      }
56  
57      private static final class PatternFormatter extends Formatter {
58          private final FastDateFormat fastDateFormat;
59  
60          PatternFormatter(final FastDateFormat fastDateFormat) {
61              this.fastDateFormat = fastDateFormat;
62          }
63  
64          @Override
65          String format(final long timeMillis) {
66              return fastDateFormat.format(timeMillis);
67          }
68  
69          @Override
70          public String toPattern() {
71              return fastDateFormat.toPattern();
72          }
73      }
74  
75      private static final class FixedFormatter extends Formatter {
76          private final FixedDateFormat fixedDateFormat;
77  
78          FixedFormatter(final FixedDateFormat fixedDateFormat) {
79              this.fixedDateFormat = fixedDateFormat;
80          }
81  
82          @Override
83          String format(final long timeMillis) {
84              return fixedDateFormat.format(timeMillis);
85          }
86  
87          @Override
88          public String toPattern() {
89              return fixedDateFormat.getFormat();
90          }
91      }
92  
93      private static final class UnixFormatter extends Formatter {
94  
95          @Override
96          String format(final long timeMillis) {
97              return Long.toString(timeMillis / 1000);
98          }
99      }
100 
101     private static final class UnixMillisFormatter extends Formatter {
102 
103         @Override
104         String format(final long timeMillis) {
105             return Long.toString(timeMillis);
106         }
107     }
108 
109     /**
110      * UNIX formatter in seconds (standard).
111      */
112     private static final String UNIX_FORMAT = "UNIX";
113 
114     /**
115      * UNIX formatter in milliseconds
116      */
117     private static final String UNIX_MILLIS_FORMAT = "UNIX_MILLIS";
118 
119     /**
120      * Obtains an instance of pattern converter.
121      *
122      * @param options
123      *            options, may be null.
124      * @return instance of pattern converter.
125      */
126     public static DatePatternConverter newInstance(final String[] options) {
127         return new DatePatternConverter(options);
128     }
129 
130     private final Formatter formatter;
131 
132     /**
133      * Private constructor.
134      *
135      * @param options
136      *            options, may be null.
137      */
138     private DatePatternConverter(final String[] options) {
139         super("Date", "date");
140 
141         final FixedDateFormat fixedDateFormat = FixedDateFormat.createIfSupported(options);
142         if (fixedDateFormat != null) {
143             formatter = createFormatter(fixedDateFormat);
144         } else {
145             formatter = createFormatter(options);
146         }
147         cachedTime = new AtomicReference<>(new CachedTime(System.currentTimeMillis()));
148     }
149 
150     private static Formatter createFormatter(final FixedDateFormat fixedDateFormat) {
151         return new FixedFormatter(fixedDateFormat);
152     }
153 
154     private static Formatter createFormatter(final String[] options) {
155         // if we get here, options is a non-null array with at least one element (first of which non-null)
156         Objects.requireNonNull(options);
157         if (options.length == 0) {
158             throw new IllegalArgumentException("options array must have at least one element");
159         }
160         Objects.requireNonNull(options[0]);
161         final String patternOption = options[0];
162         if (UNIX_FORMAT.equals(patternOption)) {
163             return new UnixFormatter();
164         }
165         if (UNIX_MILLIS_FORMAT.equals(patternOption)) {
166             return new UnixMillisFormatter();
167         }
168 
169         // if the option list contains a TZ option, then set it.
170         TimeZone tz = null;
171         if (options != null && options.length > 1) {
172             tz = TimeZone.getTimeZone(options[1]);
173         }
174 
175         try {
176             final FastDateFormat tempFormat = FastDateFormat.getInstance(patternOption, tz);
177             return new PatternFormatter(tempFormat);
178         } catch (final IllegalArgumentException e) {
179             LOGGER.warn("Could not instantiate FastDateFormat with pattern " + patternOption, e);
180 
181             // default to the DEFAULT format
182             return createFormatter(FixedDateFormat.create(FixedFormat.DEFAULT));
183         }
184     }
185 
186     /**
187      * Appends formatted date to string buffer.
188      *
189      * @param date
190      *            date
191      * @param toAppendTo
192      *            buffer to which formatted date is appended.
193      */
194     public void format(final Date date, final StringBuilder toAppendTo) {
195         format(date.getTime(), toAppendTo);
196     }
197 
198     /**
199      * {@inheritDoc}
200      */
201     @Override
202     public void format(final LogEvent event, final StringBuilder output) {
203         format(event.getTimeMillis(), output);
204     }
205     
206     public void format(final long timestampMillis, final StringBuilder output) {
207         CachedTime cached = cachedTime.get();
208         if (timestampMillis != cached.timestampMillis) {
209             final CachedTime newTime = new CachedTime(timestampMillis);
210             if (cachedTime.compareAndSet(cached, newTime)) {
211                 cached = newTime;
212             } else {
213                 cached = cachedTime.get();
214             }
215         }
216         output.append(cached.formatted);
217     }
218 
219     /**
220      * {@inheritDoc}
221      */
222     @Override
223     public void format(final Object obj, final StringBuilder output) {
224         if (obj instanceof Date) {
225             format((Date) obj, output);
226         }
227         super.format(obj, output);
228     }
229 
230     @Override
231     public void format(final StringBuilder toAppendTo, final Object... objects) {
232         for (final Object obj : objects) {
233             if (obj instanceof Date) {
234                 format(obj, toAppendTo);
235                 break;
236             }
237         }
238     }
239 
240     /**
241      * Gets the pattern string describing this date format.
242      *
243      * @return the pattern string describing this date format.
244      */
245     public String getPattern() {
246         return formatter.toPattern();
247     }
248 
249 }