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 abstract static class Formatter { 038 abstract String format(long timeMillis); 039 040 public String toPattern() { 041 return null; 042 } 043 } 044 045 private static final class PatternFormatter extends Formatter { 046 private final FastDateFormat fastDateFormat; 047 048 PatternFormatter(final FastDateFormat fastDateFormat) { 049 this.fastDateFormat = fastDateFormat; 050 } 051 052 @Override 053 String format(final long timeMillis) { 054 return fastDateFormat.format(timeMillis); 055 } 056 057 @Override 058 public String toPattern() { 059 return fastDateFormat.toPattern(); 060 } 061 } 062 063 private static final class FixedFormatter extends Formatter { 064 private final FixedDateFormat fixedDateFormat; 065 066 FixedFormatter(final FixedDateFormat fixedDateFormat) { 067 this.fixedDateFormat = fixedDateFormat; 068 } 069 070 @Override 071 String format(final long timeMillis) { 072 return fixedDateFormat.format(timeMillis); 073 } 074 075 @Override 076 public String toPattern() { 077 return fixedDateFormat.getFormat(); 078 } 079 } 080 081 private static final class UnixFormatter extends Formatter { 082 083 @Override 084 String format(final long timeMillis) { 085 return Long.toString(timeMillis / 1000); 086 } 087 } 088 089 private static final class UnixMillisFormatter extends Formatter { 090 091 @Override 092 String format(final long timeMillis) { 093 return Long.toString(timeMillis); 094 } 095 } 096 097 private final class CachedTime { 098 public long timestampMillis; 099 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}