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 abstract static class Formatter {
38          abstract String format(long timeMillis);
39  
40          public String toPattern() {
41              return null;
42          }
43      }
44  
45      private static final class PatternFormatter extends Formatter {
46          private final FastDateFormat fastDateFormat;
47  
48          PatternFormatter(final FastDateFormat fastDateFormat) {
49              this.fastDateFormat = fastDateFormat;
50          }
51  
52          @Override
53          String format(final long timeMillis) {
54              return fastDateFormat.format(timeMillis);
55          }
56  
57          @Override
58          public String toPattern() {
59              return fastDateFormat.toPattern();
60          }
61      }
62  
63      private static final class FixedFormatter extends Formatter {
64          private final FixedDateFormat fixedDateFormat;
65  
66          FixedFormatter(final FixedDateFormat fixedDateFormat) {
67              this.fixedDateFormat = fixedDateFormat;
68          }
69  
70          @Override
71          String format(final long timeMillis) {
72              return fixedDateFormat.format(timeMillis);
73          }
74  
75          @Override
76          public String toPattern() {
77              return fixedDateFormat.getFormat();
78          }
79      }
80  
81      private static final class UnixFormatter extends Formatter {
82  
83          @Override
84          String format(final long timeMillis) {
85              return Long.toString(timeMillis / 1000);
86          }
87      }
88  
89      private static final class UnixMillisFormatter extends Formatter {
90  
91          @Override
92          String format(final long timeMillis) {
93              return Long.toString(timeMillis);
94          }
95      }
96  
97      private final class CachedTime {
98          public long timestampMillis;
99          public String formatted;
100 
101         public CachedTime(final long timestampMillis) {
102             this.timestampMillis = timestampMillis;
103             this.formatted = formatter.format(this.timestampMillis);
104         }
105     }
106 
107     /**
108      * UNIX formatter in seconds (standard).
109      */
110     private static final String UNIX_FORMAT = "UNIX";
111 
112     /**
113      * UNIX formatter in milliseconds
114      */
115     private static final String UNIX_MILLIS_FORMAT = "UNIX_MILLIS";
116 
117     private final AtomicReference<CachedTime> cachedTime;
118     private final Formatter formatter;
119 
120     /**
121      * Private constructor.
122      *
123      * @param options options, may be null.
124      */
125     private DatePatternConverter(final String[] options) {
126         super("Date", "date");
127 
128         final FixedDateFormat fixedDateFormat = FixedDateFormat.createIfSupported(options);
129         if (fixedDateFormat != null) {
130             formatter = createFormatter(fixedDateFormat);
131         } else {
132             formatter = createFormatter(options);
133         }
134         cachedTime = new AtomicReference<>(new CachedTime(System.currentTimeMillis()));
135     }
136 
137     /**
138      * Obtains an instance of pattern converter.
139      *
140      * @param options options, may be null.
141      * @return instance of pattern converter.
142      */
143     public static DatePatternConverter newInstance(final String[] options) {
144         return new DatePatternConverter(options);
145     }
146 
147     private static Formatter createFormatter(final FixedDateFormat fixedDateFormat) {
148         return new FixedFormatter(fixedDateFormat);
149     }
150 
151     private static Formatter createFormatter(final String[] options) {
152         // if we get here, options is a non-null array with at least one element (first of which non-null)
153         Objects.requireNonNull(options);
154         if (options.length == 0) {
155             throw new IllegalArgumentException("options array must have at least one element");
156         }
157         Objects.requireNonNull(options[0]);
158         final String patternOption = options[0];
159         if (UNIX_FORMAT.equals(patternOption)) {
160             return new UnixFormatter();
161         }
162         if (UNIX_MILLIS_FORMAT.equals(patternOption)) {
163             return new UnixMillisFormatter();
164         }
165         // LOG4J2-1149: patternOption may be a name (if a time zone was specified)
166         final FixedDateFormat.FixedFormat fixedFormat = FixedDateFormat.FixedFormat.lookup(patternOption);
167         final String pattern = fixedFormat == null ? patternOption : fixedFormat.getPattern();
168 
169         // if the option list contains a TZ option, then set it.
170         TimeZone tz = null;
171         if (options.length > 1 && options[1] != null) {
172             tz = TimeZone.getTimeZone(options[1]);
173         }
174 
175         try {
176             final FastDateFormat tempFormat = FastDateFormat.getInstance(pattern, tz);
177             return new PatternFormatter(tempFormat);
178         } catch (final IllegalArgumentException e) {
179             LOGGER.warn("Could not instantiate FastDateFormat with pattern " + pattern, 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 date
190      * @param toAppendTo buffer to which formatted date is appended.
191      */
192     public void format(final Date date, final StringBuilder toAppendTo) {
193         format(date.getTime(), toAppendTo);
194     }
195 
196     /**
197      * {@inheritDoc}
198      */
199     @Override
200     public void format(final LogEvent event, final StringBuilder output) {
201         format(event.getTimeMillis(), output);
202     }
203 
204     public void format(final long timestampMillis, final StringBuilder output) {
205         CachedTime cached = cachedTime.get();
206         if (timestampMillis != cached.timestampMillis) {
207             final CachedTime newTime = new CachedTime(timestampMillis);
208             if (cachedTime.compareAndSet(cached, newTime)) {
209                 cached = newTime;
210             } else {
211                 cached = cachedTime.get();
212             }
213         }
214         output.append(cached.formatted);
215     }
216 
217     /**
218      * {@inheritDoc}
219      */
220     @Override
221     public void format(final Object obj, final StringBuilder output) {
222         if (obj instanceof Date) {
223             format((Date) obj, output);
224         }
225         super.format(obj, output);
226     }
227 
228     @Override
229     public void format(final StringBuilder toAppendTo, final Object... objects) {
230         for (final Object obj : objects) {
231             if (obj instanceof Date) {
232                 format(obj, toAppendTo);
233                 break;
234             }
235         }
236     }
237 
238     /**
239      * Gets the pattern string describing this date format.
240      *
241      * @return the pattern string describing this date format.
242      */
243     public String getPattern() {
244         return formatter.toPattern();
245     }
246 
247 }