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