1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.pattern;
18
19 import java.util.Arrays;
20 import java.util.Date;
21 import java.util.Objects;
22 import java.util.TimeZone;
23 import java.util.concurrent.atomic.AtomicReference;
24
25 import org.apache.logging.log4j.core.LogEvent;
26 import org.apache.logging.log4j.core.config.plugins.Plugin;
27 import org.apache.logging.log4j.core.util.Constants;
28 import org.apache.logging.log4j.core.util.datetime.FastDateFormat;
29 import org.apache.logging.log4j.core.util.datetime.FixedDateFormat;
30 import org.apache.logging.log4j.core.util.datetime.FixedDateFormat.FixedFormat;
31 import org.apache.logging.log4j.util.PerformanceSensitive;
32
33
34
35
36 @Plugin(name = "DatePatternConverter", category = PatternConverter.CATEGORY)
37 @ConverterKeys({"d", "date"})
38 @PerformanceSensitive("allocation")
39 public final class DatePatternConverter extends LogEventPatternConverter implements ArrayPatternConverter {
40
41 private abstract static class Formatter {
42 long previousTime;
43
44 abstract String format(long timeMillis);
45
46 abstract void formatToBuffer(long timeMillis, StringBuilder destination);
47
48 public String toPattern() {
49 return null;
50 }
51 }
52
53 private static final class PatternFormatter extends Formatter {
54 private final FastDateFormat fastDateFormat;
55
56
57 private final StringBuilder cachedBuffer = new StringBuilder(64);
58
59 PatternFormatter(final FastDateFormat fastDateFormat) {
60 this.fastDateFormat = fastDateFormat;
61 }
62
63 @Override
64 String format(final long timeMillis) {
65 return fastDateFormat.format(timeMillis);
66 }
67
68 @Override
69 void formatToBuffer(final long timeMillis, final StringBuilder destination) {
70 if (previousTime != timeMillis) {
71 cachedBuffer.setLength(0);
72 fastDateFormat.format(timeMillis, cachedBuffer);
73 }
74 destination.append(cachedBuffer);
75 }
76
77 @Override
78 public String toPattern() {
79 return fastDateFormat.getPattern();
80 }
81 }
82
83 private static final class FixedFormatter extends Formatter {
84 private final FixedDateFormat fixedDateFormat;
85
86
87 private final char[] cachedBuffer = new char[64];
88 private int length = 0;
89
90 FixedFormatter(final FixedDateFormat fixedDateFormat) {
91 this.fixedDateFormat = fixedDateFormat;
92 }
93
94 @Override
95 String format(final long timeMillis) {
96 return fixedDateFormat.format(timeMillis);
97 }
98
99 @Override
100 void formatToBuffer(final long timeMillis, final StringBuilder destination) {
101 if (previousTime != timeMillis) {
102 length = fixedDateFormat.format(timeMillis, cachedBuffer, 0);
103 }
104 destination.append(cachedBuffer, 0, length);
105 }
106
107 @Override
108 public String toPattern() {
109 return fixedDateFormat.getFormat();
110 }
111 }
112
113 private static final class UnixFormatter extends Formatter {
114
115 @Override
116 String format(final long timeMillis) {
117 return Long.toString(timeMillis / 1000);
118 }
119
120 @Override
121 void formatToBuffer(final long timeMillis, final StringBuilder destination) {
122 destination.append(timeMillis / 1000);
123 }
124 }
125
126 private static final class UnixMillisFormatter extends Formatter {
127
128 @Override
129 String format(final long timeMillis) {
130 return Long.toString(timeMillis);
131 }
132
133 @Override
134 void formatToBuffer(final long timeMillis, final StringBuilder destination) {
135 destination.append(timeMillis);
136 }
137 }
138
139 private final class CachedTime {
140 public long timestampMillis;
141 public String formatted;
142
143 public CachedTime(final long timestampMillis) {
144 this.timestampMillis = timestampMillis;
145 this.formatted = formatter.format(this.timestampMillis);
146 }
147 }
148
149
150
151
152 private static final String UNIX_FORMAT = "UNIX";
153
154
155
156
157 private static final String UNIX_MILLIS_FORMAT = "UNIX_MILLIS";
158
159 private final String[] options;
160 private final ThreadLocal<Formatter> threadLocalFormatter = new ThreadLocal<>();
161 private final AtomicReference<CachedTime> cachedTime;
162 private final Formatter formatter;
163
164
165
166
167
168
169 private DatePatternConverter(final String[] options) {
170 super("Date", "date");
171 this.options = options == null ? null : Arrays.copyOf(options, options.length);
172 this.formatter = createFormatter(options);
173 cachedTime = new AtomicReference<>(new CachedTime(System.currentTimeMillis()));
174 }
175
176 private Formatter createFormatter(final String[] options) {
177 final FixedDateFormat fixedDateFormat = FixedDateFormat.createIfSupported(options);
178 if (fixedDateFormat != null) {
179 return createFixedFormatter(fixedDateFormat);
180 }
181 return createNonFixedFormatter(options);
182 }
183
184
185
186
187
188
189
190 public static DatePatternConverter newInstance(final String[] options) {
191 return new DatePatternConverter(options);
192 }
193
194 private static Formatter createFixedFormatter(final FixedDateFormat fixedDateFormat) {
195 return new FixedFormatter(fixedDateFormat);
196 }
197
198 private static Formatter createNonFixedFormatter(final String[] options) {
199
200 Objects.requireNonNull(options);
201 if (options.length == 0) {
202 throw new IllegalArgumentException("options array must have at least one element");
203 }
204 Objects.requireNonNull(options[0]);
205 final String patternOption = options[0];
206 if (UNIX_FORMAT.equals(patternOption)) {
207 return new UnixFormatter();
208 }
209 if (UNIX_MILLIS_FORMAT.equals(patternOption)) {
210 return new UnixMillisFormatter();
211 }
212
213 final FixedDateFormat.FixedFormat fixedFormat = FixedDateFormat.FixedFormat.lookup(patternOption);
214 final String pattern = fixedFormat == null ? patternOption : fixedFormat.getPattern();
215
216
217 TimeZone tz = null;
218 if (options.length > 1 && options[1] != null) {
219 tz = TimeZone.getTimeZone(options[1]);
220 }
221
222 try {
223 final FastDateFormat tempFormat = FastDateFormat.getInstance(pattern, tz);
224 return new PatternFormatter(tempFormat);
225 } catch (final IllegalArgumentException e) {
226 LOGGER.warn("Could not instantiate FastDateFormat with pattern " + pattern, e);
227
228
229 return createFixedFormatter(FixedDateFormat.create(FixedFormat.DEFAULT, tz));
230 }
231 }
232
233
234
235
236
237
238
239 public void format(final Date date, final StringBuilder toAppendTo) {
240 format(date.getTime(), toAppendTo);
241 }
242
243
244
245
246 @Override
247 public void format(final LogEvent event, final StringBuilder output) {
248 format(event.getTimeMillis(), output);
249 }
250
251 public void format(final long timestampMillis, final StringBuilder output) {
252 if (Constants.ENABLE_THREADLOCALS) {
253 formatWithoutAllocation(timestampMillis, output);
254 } else {
255 formatWithoutThreadLocals(timestampMillis, output);
256 }
257 }
258
259 private void formatWithoutAllocation(final long timestampMillis, final StringBuilder output) {
260 getThreadLocalFormatter().formatToBuffer(timestampMillis, output);
261 }
262
263 private Formatter getThreadLocalFormatter() {
264 Formatter result = threadLocalFormatter.get();
265 if (result == null) {
266 result = createFormatter(options);
267 threadLocalFormatter.set(result);
268 }
269 return result;
270 }
271
272 private void formatWithoutThreadLocals(final long timestampMillis, final StringBuilder output) {
273 CachedTime cached = cachedTime.get();
274 if (timestampMillis != cached.timestampMillis) {
275 final CachedTime newTime = new CachedTime(timestampMillis);
276 if (cachedTime.compareAndSet(cached, newTime)) {
277 cached = newTime;
278 } else {
279 cached = cachedTime.get();
280 }
281 }
282 output.append(cached.formatted);
283 }
284
285
286
287
288 @Override
289 public void format(final Object obj, final StringBuilder output) {
290 if (obj instanceof Date) {
291 format((Date) obj, output);
292 }
293 super.format(obj, output);
294 }
295
296 @Override
297 public void format(final StringBuilder toAppendTo, final Object... objects) {
298 for (final Object obj : objects) {
299 if (obj instanceof Date) {
300 format(obj, toAppendTo);
301 break;
302 }
303 }
304 }
305
306
307
308
309
310
311 public String getPattern() {
312 return formatter.toPattern();
313 }
314
315 }