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.text.SimpleDateFormat; 020import java.util.Date; 021import java.util.TimeZone; 022 023import org.apache.logging.log4j.core.LogEvent; 024import org.apache.logging.log4j.core.config.plugins.Plugin; 025 026/** 027 * Convert and format the event's date in a StringBuilder. 028 */ 029@Plugin(name = "DatePatternConverter", category = PatternConverter.CATEGORY) 030@ConverterKeys({ "d", "date" }) 031public final class DatePatternConverter extends LogEventPatternConverter implements ArrayPatternConverter { 032 033 private abstract static class Formatter { 034 abstract String format(long time); 035 036 public String toPattern() { 037 return null; 038 } 039 } 040 041 private static class PatternFormatter extends Formatter { 042 private final SimpleDateFormat simpleDateFormat; 043 044 PatternFormatter(final SimpleDateFormat simpleDateFormat) { 045 this.simpleDateFormat = simpleDateFormat; 046 } 047 048 @Override 049 String format(final long time) { 050 return simpleDateFormat.format(Long.valueOf(time)); 051 } 052 053 @Override 054 public String toPattern() { 055 return simpleDateFormat.toPattern(); 056 } 057 } 058 059 private static class UnixFormatter extends Formatter { 060 061 @Override 062 String format(final long time) { 063 return Long.toString(time / 1000); 064 } 065 066 } 067 068 private static class UnixMillisFormatter extends Formatter { 069 070 @Override 071 String format(final long time) { 072 return Long.toString(time); 073 } 074 075 } 076 077 /** 078 * ABSOLUTE string literal. 079 */ 080 private static final String ABSOLUTE_FORMAT = "ABSOLUTE"; 081 082 /** 083 * SimpleTimePattern for ABSOLUTE. 084 */ 085 private static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS"; 086 087 /** 088 * COMPACT string literal. 089 */ 090 private static final String COMPACT_FORMAT = "COMPACT"; 091 092 /** 093 * SimpleTimePattern for COMPACT. 094 */ 095 private static final String COMPACT_PATTERN = "yyyyMMddHHmmssSSS"; 096 097 /** 098 * DATE string literal. 099 */ 100 private static final String DATE_AND_TIME_FORMAT = "DATE"; 101 102 /** 103 * SimpleTimePattern for DATE. 104 */ 105 private static final String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS"; 106 107 /** 108 * DEFAULT string literal. 109 */ 110 private static final String DEFAULT_FORMAT = "DEFAULT"; 111 112 /** 113 * SimpleTimePattern for DEFAULT. 114 */ 115 // package private for unit tests 116 static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS"; 117 118 /** 119 * ISO8601_BASIC string literal. 120 */ 121 private static final String ISO8601_BASIC_FORMAT = "ISO8601_BASIC"; 122 123 /** 124 * SimpleTimePattern for ISO8601_BASIC. 125 */ 126 private static final String ISO8601_BASIC_PATTERN = "yyyyMMdd'T'HHmmss,SSS"; 127 128 /** 129 * ISO8601 string literal. 130 */ 131 // package private for unit tests 132 static final String ISO8601_FORMAT = "ISO8601"; 133 134 /** 135 * SimpleTimePattern for ISO8601. 136 */ 137 // package private for unit tests 138 static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss,SSS"; 139 140 /** 141 * UNIX formatter in seconds (standard). 142 */ 143 private static final String UNIX_FORMAT = "UNIX"; 144 145 /** 146 * UNIX formatter in milliseconds 147 */ 148 private static final String UNIX_MILLIS_FORMAT = "UNIX_MILLIS"; 149 150 /** 151 * Obtains an instance of pattern converter. 152 * 153 * @param options 154 * options, may be null. 155 * @return instance of pattern converter. 156 */ 157 public static DatePatternConverter newInstance(final String[] options) { 158 return new DatePatternConverter(options); 159 } 160 161 /** 162 * Date format. 163 */ 164 private String cachedDateString; 165 166 private final Formatter formatter; 167 168 private long lastTimestamp; 169 170 /** 171 * Private constructor. 172 * 173 * @param options 174 * options, may be null. 175 */ 176 private DatePatternConverter(final String[] options) { 177 super("Date", "date"); 178 179 // null patternOption is OK. 180 final String patternOption = options != null && options.length > 0 ? options[0] : null; 181 182 String pattern = null; 183 Formatter tempFormatter = null; 184 185 if (patternOption == null || patternOption.equalsIgnoreCase(DEFAULT_FORMAT)) { 186 pattern = DEFAULT_PATTERN; 187 } else if (patternOption.equalsIgnoreCase(ISO8601_FORMAT)) { 188 pattern = ISO8601_PATTERN; 189 } else if (patternOption.equalsIgnoreCase(ISO8601_BASIC_FORMAT)) { 190 pattern = ISO8601_BASIC_PATTERN; 191 } else if (patternOption.equalsIgnoreCase(ABSOLUTE_FORMAT)) { 192 pattern = ABSOLUTE_TIME_PATTERN; 193 } else if (patternOption.equalsIgnoreCase(DATE_AND_TIME_FORMAT)) { 194 pattern = DATE_AND_TIME_PATTERN; 195 } else if (patternOption.equalsIgnoreCase(COMPACT_FORMAT)) { 196 pattern = COMPACT_PATTERN; 197 } else if (patternOption.equalsIgnoreCase(UNIX_FORMAT)) { 198 tempFormatter = new UnixFormatter(); 199 } else if (patternOption.equalsIgnoreCase(UNIX_MILLIS_FORMAT)) { 200 tempFormatter = new UnixMillisFormatter(); 201 } else { 202 pattern = patternOption; 203 } 204 205 if (pattern != null) { 206 SimpleDateFormat tempFormat; 207 208 try { 209 tempFormat = new SimpleDateFormat(pattern); 210 } catch (final IllegalArgumentException e) { 211 LOGGER.warn("Could not instantiate SimpleDateFormat with pattern " + patternOption, e); 212 213 // default to the DEFAULT format 214 tempFormat = new SimpleDateFormat(DEFAULT_PATTERN); 215 } 216 217 // if the option list contains a TZ option, then set it. 218 if (options != null && options.length > 1) { 219 final TimeZone tz = TimeZone.getTimeZone(options[1]); 220 tempFormat.setTimeZone(tz); 221 } 222 tempFormatter = new PatternFormatter(tempFormat); 223 } 224 formatter = tempFormatter; 225 } 226 227 /** 228 * Append formatted date to string buffer. 229 * 230 * @param date 231 * date 232 * @param toAppendTo 233 * buffer to which formatted date is appended. 234 */ 235 public void format(final Date date, final StringBuilder toAppendTo) { 236 synchronized (this) { 237 toAppendTo.append(formatter.format(date.getTime())); 238 } 239 } 240 241 /** 242 * {@inheritDoc} 243 */ 244 @Override 245 public void format(final LogEvent event, final StringBuilder output) { 246 final long timestamp = event.getTimeMillis(); 247 248 synchronized (this) { 249 if (timestamp != lastTimestamp) { 250 lastTimestamp = timestamp; 251 cachedDateString = formatter.format(timestamp); 252 } 253 } 254 output.append(cachedDateString); 255 } 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override 261 public void format(final Object obj, final StringBuilder output) { 262 if (obj instanceof Date) { 263 format((Date) obj, output); 264 } 265 super.format(obj, output); 266 } 267 268 @Override 269 public void format(final StringBuilder toAppendTo, final Object... objects) { 270 for (final Object obj : objects) { 271 if (obj instanceof Date) { 272 format(obj, toAppendTo); 273 break; 274 } 275 } 276 } 277 278 /** 279 * Gets the pattern string describing this date format. 280 * 281 * @return the pattern string describing this date format. 282 */ 283 public String getPattern() { 284 return formatter.toPattern(); 285 } 286 287}