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 */ 017 package org.apache.logging.log4j.core.impl; 018 019 import org.apache.logging.log4j.Level; 020 import org.apache.logging.log4j.ThreadContext; 021 import org.apache.logging.log4j.Marker; 022 import org.apache.logging.log4j.core.LogEvent; 023 import org.apache.logging.log4j.core.config.Property; 024 import org.apache.logging.log4j.message.LoggerNameAwareMessage; 025 import org.apache.logging.log4j.message.Message; 026 import org.apache.logging.log4j.message.TimestampMessage; 027 028 import java.io.InvalidObjectException; 029 import java.io.ObjectInputStream; 030 import java.io.Serializable; 031 import java.util.Collections; 032 import java.util.HashMap; 033 import java.util.List; 034 import java.util.Map; 035 036 /** 037 * Implementation of a LogEvent. 038 */ 039 public class Log4jLogEvent implements LogEvent, Serializable { 040 041 private static final long serialVersionUID = -1351367343806656055L; 042 private static final String NOT_AVAIL = "?"; 043 private final String fqcnOfLogger; 044 private final Marker marker; 045 private final Level level; 046 private final String name; 047 private final Message message; 048 private final long timestamp; 049 private final ThrowableProxy throwable; 050 private final Map<String, String> mdc; 051 private final ThreadContext.ContextStack ndc; 052 private String threadName = null; 053 private StackTraceElement location; 054 private boolean includeLocation; 055 private boolean endOfBatch = false; 056 057 /** 058 * Constructor. 059 * @param loggerName The name of the Logger. 060 * @param marker The Marker or null. 061 * @param fqcn The fully qualified class name of the caller. 062 * @param level The logging Level. 063 * @param message The Message. 064 * @param t A Throwable or null. 065 */ 066 public Log4jLogEvent(final String loggerName, final Marker marker, final String fqcn, final Level level, 067 final Message message, final Throwable t) { 068 this(loggerName, marker, fqcn, level, message, null, t); 069 } 070 071 /** 072 * Constructor. 073 * @param loggerName The name of the Logger. 074 * @param marker The Marker or null. 075 * @param fqcn The fully qualified class name of the caller. 076 * @param level The logging Level. 077 * @param message The Message. 078 * @param properties properties to add to the event. 079 * @param t A Throwable or null. 080 */ 081 public Log4jLogEvent(final String loggerName, final Marker marker, final String fqcn, final Level level, 082 final Message message, final List<Property> properties, final Throwable t) { 083 this(loggerName, marker, fqcn, level, message, t, 084 createMap(properties), 085 ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), null, 086 null, System.currentTimeMillis()); 087 } 088 089 /** 090 * Constructor. 091 * @param loggerName The name of the Logger. 092 * @param marker The Marker or null. 093 * @param fqcn The fully qualified class name of the caller. 094 * @param level The logging Level. 095 * @param message The Message. 096 * @param t A Throwable or null. 097 * @param mdc The mapped diagnostic context. 098 * @param ndc the nested diagnostic context. 099 * @param threadName The name of the thread. 100 * @param location The locations of the caller. 101 * @param timestamp The timestamp of the event. 102 */ 103 public Log4jLogEvent(final String loggerName, final Marker marker, final String fqcn, final Level level, 104 final Message message, final Throwable t, 105 final Map<String, String> mdc, final ThreadContext.ContextStack ndc, final String threadName, 106 final StackTraceElement location, final long timestamp) { 107 name = loggerName; 108 this.marker = marker; 109 this.fqcnOfLogger = fqcn; 110 this.level = level; 111 this.message = message; 112 this.throwable = t == null ? null : t instanceof ThrowableProxy ? (ThrowableProxy) t : new ThrowableProxy(t); 113 this.mdc = mdc; 114 this.ndc = ndc; 115 this.timestamp = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp; 116 this.threadName = threadName; 117 this.location = location; 118 if (message != null && message instanceof LoggerNameAwareMessage) { 119 ((LoggerNameAwareMessage) message).setLoggerName(name); 120 } 121 } 122 123 private static Map<String, String> createMap(final List<Property> properties) { 124 final Map<String, String> contextMap = ThreadContext.getImmutableContext(); 125 if (contextMap == null && (properties == null || properties.size() == 0)) { 126 return null; 127 } 128 if (properties == null || properties.size() == 0) { 129 return contextMap; // contextMap is not null 130 } 131 final Map<String, String> map = new HashMap<String, String>(contextMap); 132 133 for (final Property prop : properties) { 134 if (!map.containsKey(prop.getName())) { 135 map.put(prop.getName(), prop.getValue()); 136 } 137 } 138 return Collections.unmodifiableMap(map); 139 } 140 141 /** 142 * Returns the logging Level. 143 * @return the Level associated with this event. 144 */ 145 @Override 146 public Level getLevel() { 147 return level; 148 } 149 150 /** 151 * Returns the name of the Logger used to generate the event. 152 * @return The Logger name. 153 */ 154 @Override 155 public String getLoggerName() { 156 return name; 157 } 158 159 /** 160 * Returns the Message associated with the event. 161 * @return The Message. 162 */ 163 @Override 164 public Message getMessage() { 165 return message; 166 } 167 168 /** 169 * Returns the name of the Thread on which the event was generated. 170 * @return The name of the Thread. 171 */ 172 @Override 173 public String getThreadName() { 174 if (threadName == null) { 175 threadName = Thread.currentThread().getName(); 176 } 177 return threadName; 178 } 179 180 /** 181 * Returns the time in milliseconds from the epoch when the event occurred. 182 * @return The time the event occurred. 183 */ 184 @Override 185 public long getMillis() { 186 return timestamp; 187 } 188 189 /** 190 * Returns the Throwable associated with the event, or null. 191 * @return The Throwable associated with the event. 192 */ 193 @Override 194 public Throwable getThrown() { 195 return throwable; 196 } 197 198 /** 199 * Returns the Marker associated with the event, or null. 200 * @return the Marker associated with the event. 201 */ 202 @Override 203 public Marker getMarker() { 204 return marker; 205 } 206 207 /** 208 * The fully qualified class name of the class that was called by the caller. 209 * @return the fully qualified class name of the class that is performing logging. 210 */ 211 @Override 212 public String getFQCN() { 213 return fqcnOfLogger; 214 } 215 216 /** 217 * Returns the immutable copy of the ThreadContext Map. 218 * @return The context Map. 219 */ 220 @Override 221 public Map<String, String> getContextMap() { 222 return mdc == null ? ThreadContext.EMPTY_MAP : mdc; 223 } 224 225 /** 226 * Returns an immutable copy of the ThreadContext stack. 227 * @return The context Stack. 228 */ 229 @Override 230 public ThreadContext.ContextStack getContextStack() { 231 return ndc == null ? ThreadContext.EMPTY_STACK : ndc; 232 } 233 234 /** 235 * Returns the StackTraceElement for the caller. This will be the entry that occurs right 236 * before the first occurrence of FQCN as a class name. 237 * @return the StackTraceElement for the caller. 238 */ 239 @Override 240 public StackTraceElement getSource() { 241 if (location != null) { 242 return location; 243 } 244 if (fqcnOfLogger == null || !includeLocation) { 245 return null; 246 } 247 location = calcLocation(fqcnOfLogger); 248 return location; 249 } 250 251 public static StackTraceElement calcLocation(String fqcnOfLogger) { 252 if (fqcnOfLogger == null) { 253 return null; 254 } 255 final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 256 boolean next = false; 257 for (final StackTraceElement element : stackTrace) { 258 final String className = element.getClassName(); 259 if (next) { 260 if (fqcnOfLogger.equals(className)) { 261 continue; 262 } 263 return element; 264 } 265 266 if (fqcnOfLogger.equals(className)) { 267 next = true; 268 } else if (NOT_AVAIL.equals(className)) { 269 break; 270 } 271 } 272 return null; 273 } 274 275 @Override 276 public boolean isIncludeLocation() { 277 return includeLocation; 278 } 279 280 @Override 281 public void setIncludeLocation(boolean includeLocation) { 282 this.includeLocation = includeLocation; 283 } 284 285 @Override 286 public boolean isEndOfBatch() { 287 return endOfBatch; 288 } 289 290 @Override 291 public void setEndOfBatch(boolean endOfBatch) { 292 this.endOfBatch = endOfBatch; 293 } 294 295 /** 296 * Creates a LogEventProxy that can be serialized. 297 * @return a LogEventProxy. 298 */ 299 protected Object writeReplace() { 300 return new LogEventProxy(this, this.includeLocation); 301 } 302 303 public static Serializable serialize(final Log4jLogEvent event, 304 final boolean includeLocation) { 305 return new LogEventProxy(event, includeLocation); 306 } 307 308 public static Log4jLogEvent deserialize(final Serializable event) { 309 if (event == null) { 310 throw new NullPointerException("Event cannot be null"); 311 } 312 if (event instanceof LogEventProxy) { 313 final LogEventProxy proxy = (LogEventProxy) event; 314 Log4jLogEvent result = new Log4jLogEvent(proxy.name, proxy.marker, 315 proxy.fqcnOfLogger, proxy.level, proxy.message, 316 proxy.throwable, proxy.mdc, proxy.ndc, proxy.threadName, 317 proxy.location, proxy.timestamp); 318 result.setEndOfBatch(proxy.isEndOfBatch); 319 result.setIncludeLocation(proxy.isLocationRequired); 320 return result; 321 } 322 throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString()); 323 } 324 325 private void readObject(final ObjectInputStream stream) throws InvalidObjectException { 326 throw new InvalidObjectException("Proxy required"); 327 } 328 329 @Override 330 public String toString() { 331 final StringBuilder sb = new StringBuilder(); 332 final String n = name.length() == 0 ? "root" : name; 333 sb.append("Logger=").append(n); 334 sb.append(" Level=").append(level.name()); 335 sb.append(" Message=").append(message.getFormattedMessage()); 336 return sb.toString(); 337 } 338 339 /** 340 * Proxy pattern used to serialize the LogEvent. 341 */ 342 private static class LogEventProxy implements Serializable { 343 344 private static final long serialVersionUID = -7139032940312647146L; 345 private final String fqcnOfLogger; 346 private final Marker marker; 347 private final Level level; 348 private final String name; 349 private final Message message; 350 private final long timestamp; 351 private final Throwable throwable; 352 private final Map<String, String> mdc; 353 private final ThreadContext.ContextStack ndc; 354 private final String threadName; 355 private final StackTraceElement location; 356 private final boolean isLocationRequired; 357 private final boolean isEndOfBatch; 358 359 public LogEventProxy(final Log4jLogEvent event, boolean includeLocation) { 360 this.fqcnOfLogger = event.fqcnOfLogger; 361 this.marker = event.marker; 362 this.level = event.level; 363 this.name = event.name; 364 this.message = event.message; 365 this.timestamp = event.timestamp; 366 this.throwable = event.throwable; 367 this.mdc = event.mdc; 368 this.ndc = event.ndc; 369 this.location = includeLocation ? event.getSource() : null; 370 this.threadName = event.getThreadName(); 371 this.isLocationRequired = includeLocation; 372 this.isEndOfBatch = event.endOfBatch; 373 } 374 375 /** 376 * Returns a Log4jLogEvent using the data in the proxy. 377 * @return Log4jLogEvent. 378 */ 379 protected Object readResolve() { 380 Log4jLogEvent result = new Log4jLogEvent(name, marker, fqcnOfLogger, 381 level, message, throwable, mdc, ndc, threadName, location, 382 timestamp); 383 result.setEndOfBatch(isEndOfBatch); 384 result.setIncludeLocation(isLocationRequired); 385 return result; 386 } 387 } 388 }