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 */ 017package org.apache.logging.log4j.core.pattern; 018 019import java.util.Date; 020import java.util.Objects; 021import java.util.TimeZone; 022import java.util.concurrent.atomic.AtomicReference; 023 024import org.apache.logging.log4j.core.LogEvent; 025import org.apache.logging.log4j.core.config.plugins.Plugin; 026import org.apache.logging.log4j.core.util.datetime.FastDateFormat; 027import org.apache.logging.log4j.core.util.datetime.FixedDateFormat; 028import org.apache.logging.log4j.core.util.datetime.FixedDateFormat.FixedFormat; 029 030/** 031 * Converts and formats the event's date in a StringBuilder. 032 */ 033@Plugin(name = "DatePatternConverter", category = PatternConverter.CATEGORY) 034@ConverterKeys({ "d", "date" }) 035public final class DatePatternConverter extends LogEventPatternConverter implements ArrayPatternConverter { 036 037 private final class CachedTime { 038 public long timestampMillis; 039 public String formatted; 040 041 public CachedTime(final long timestampMillis) { 042 this.timestampMillis = timestampMillis; 043 this.formatted = formatter.format(this.timestampMillis); 044 } 045 } 046 047 private final AtomicReference<CachedTime> cachedTime; 048 049 private abstract static class Formatter { 050 abstract String format(long timeMillis); 051 052 public String toPattern() { 053 return null; 054 } 055 } 056 057 private static final class PatternFormatter extends Formatter { 058 private final FastDateFormat fastDateFormat; 059 060 PatternFormatter(final FastDateFormat fastDateFormat) { 061 this.fastDateFormat = fastDateFormat; 062 } 063 064 @Override 065 String format(final long timeMillis) { 066 return fastDateFormat.format(timeMillis); 067 } 068 069 @Override 070 public String toPattern() { 071 return fastDateFormat.toPattern(); 072 } 073 } 074 075 private static final class FixedFormatter extends Formatter { 076 private final FixedDateFormat fixedDateFormat; 077 078 FixedFormatter(final FixedDateFormat fixedDateFormat) { 079 this.fixedDateFormat = fixedDateFormat; 080 } 081 082 @Override 083 String format(final long timeMillis) { 084 return fixedDateFormat.format(timeMillis); 085 } 086 087 @Override 088 public String toPattern() { 089 return fixedDateFormat.getFormat(); 090 } 091 } 092 093 private static final class UnixFormatter extends Formatter { 094 095 @Override 096 String format(final long timeMillis) { 097 return Long.toString(timeMillis / 1000); 098 } 099 } 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}