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 java.io.InvalidObjectException;
020 import java.io.ObjectInputStream;
021 import java.io.Serializable;
022 import java.util.Collections;
023 import java.util.HashMap;
024 import java.util.List;
025 import java.util.Map;
026
027 import org.apache.logging.log4j.Level;
028 import org.apache.logging.log4j.Marker;
029 import org.apache.logging.log4j.ThreadContext;
030 import org.apache.logging.log4j.core.LogEvent;
031 import org.apache.logging.log4j.core.config.Property;
032 import org.apache.logging.log4j.core.util.Clock;
033 import org.apache.logging.log4j.core.util.ClockFactory;
034 import org.apache.logging.log4j.message.LoggerNameAwareMessage;
035 import org.apache.logging.log4j.message.Message;
036 import org.apache.logging.log4j.message.TimestampMessage;
037 import org.apache.logging.log4j.util.Strings;
038
039 /**
040 * Implementation of a LogEvent.
041 */
042 public class Log4jLogEvent implements LogEvent {
043
044 private static final long serialVersionUID = -1351367343806656055L;
045 private static final Clock clock = ClockFactory.getClock();
046 private final String loggerFqcn;
047 private final Marker marker;
048 private final Level level;
049 private final String loggerName;
050 private final Message message;
051 private final long timeMillis;
052 private transient final Throwable thrown;
053 private ThrowableProxy thrownProxy;
054 private final Map<String, String> contextMap;
055 private final ThreadContext.ContextStack contextStack;
056 private String threadName = null;
057 private StackTraceElement source;
058 private boolean includeLocation;
059 private boolean endOfBatch = false;
060
061 public static class Builder implements org.apache.logging.log4j.core.util.Builder<LogEvent> {
062
063 private String loggerFqcn;
064 private Marker marker;
065 private Level level;
066 private String loggerName;
067 private Message message;
068 private Throwable thrown;
069
070 public Builder setLoggerFqcn(final String loggerFqcn) {
071 this.loggerFqcn = loggerFqcn;
072 return this;
073 }
074
075 public Builder setMarker(final Marker marker) {
076 this.marker = marker;
077 return this;
078 }
079
080 public Builder setLevel(final Level level) {
081 this.level = level;
082 return this;
083 }
084
085 public Builder setLoggerName(final String loggerName) {
086 this.loggerName = loggerName;
087 return this;
088 }
089
090 public Builder setMessage(final Message message) {
091 this.message = message;
092 return this;
093 }
094
095 public Builder setThrown(final Throwable thrown) {
096 this.thrown = thrown;
097 return this;
098 }
099
100 @Override
101 public Log4jLogEvent build() {
102 return new Log4jLogEvent(loggerName, marker, loggerFqcn, level, message, thrown);
103 }
104 }
105
106 public static Builder newBuilder() {
107 return new Builder();
108 }
109
110 public Log4jLogEvent() {
111 this(clock.currentTimeMillis());
112 }
113
114 /**
115 *
116 */
117 public Log4jLogEvent(final long timestamp) {
118 this(Strings.EMPTY, null, Strings.EMPTY, null, null, (Throwable) null, null, null, null, null, timestamp);
119 }
120
121 /**
122 * Constructor.
123 * @param loggerName The name of the Logger.
124 * @param marker The Marker or null.
125 * @param loggerFQCN The fully qualified class name of the caller.
126 * @param level The logging Level.
127 * @param message The Message.
128 * @param t A Throwable or null.
129 */
130 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
131 final Message message, final Throwable t) {
132 this(loggerName, marker, loggerFQCN, level, message, null, t);
133 }
134
135 /**
136 * Constructor.
137 * @param loggerName The name of the Logger.
138 * @param marker The Marker or null.
139 * @param loggerFQCN The fully qualified class name of the caller.
140 * @param level The logging Level.
141 * @param message The Message.
142 * @param properties properties to add to the event.
143 * @param t A Throwable or null.
144 */
145 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
146 final Message message, final List<Property> properties, final Throwable t) {
147 this(loggerName, marker, loggerFQCN, level, message, t,
148 createMap(properties),
149 ThreadContext.getDepth() == 0 ? null : ThreadContext.cloneStack(), null,
150 null,
151 // LOG4J2-628 use log4j.Clock for timestamps
152 // LOG4J2-744 unless TimestampMessage already has one
153 message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() :
154 clock.currentTimeMillis());
155 }
156
157 /**
158 * Constructor.
159 * @param loggerName The name of the Logger.
160 * @param marker The Marker or null.
161 * @param loggerFQCN The fully qualified class name of the caller.
162 * @param level The logging Level.
163 * @param message The Message.
164 * @param t A Throwable or null.
165 * @param mdc The mapped diagnostic context.
166 * @param ndc the nested diagnostic context.
167 * @param threadName The name of the thread.
168 * @param location The locations of the caller.
169 * @param timestamp The timestamp of the event.
170 */
171 public Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
172 final Message message, final Throwable t, final Map<String, String> mdc,
173 final ThreadContext.ContextStack ndc, final String threadName,
174 final StackTraceElement location, final long timestamp) {
175 this(loggerName, marker, loggerFQCN, level, message, t, null, mdc, ndc, threadName,
176 location, timestamp);
177 }
178
179 /**
180 * Create a new LogEvent.
181 * @param loggerName The name of the Logger.
182 * @param marker The Marker or null.
183 * @param loggerFQCN The fully qualified class name of the caller.
184 * @param level The logging Level.
185 * @param message The Message.
186 * @param thrown A Throwable or null.
187 * @param thrownProxy A ThrowableProxy or null.
188 * @param mdc The mapped diagnostic context.
189 * @param ndc the nested diagnostic context.
190 * @param threadName The name of the thread.
191 * @param location The locations of the caller.
192 * @param timestamp The timestamp of the event.
193 */
194 public static Log4jLogEvent createEvent(final String loggerName, final Marker marker, final String loggerFQCN,
195 final Level level, final Message message, final Throwable thrown,
196 final ThrowableProxy thrownProxy,
197 final Map<String, String> mdc, final ThreadContext.ContextStack ndc,
198 final String threadName, final StackTraceElement location,
199 final long timestamp) {
200 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
201 thrownProxy, mdc, ndc, threadName, location, timestamp);
202 return result;
203 }
204
205 /**
206 * Constructor.
207 * @param loggerName The name of the Logger.
208 * @param marker The Marker or null.
209 * @param loggerFQCN The fully qualified class name of the caller.
210 * @param level The logging Level.
211 * @param message The Message.
212 * @param thrown A Throwable or null.
213 * @param thrownProxy A ThrowableProxy or null.
214 * @param contextMap The mapped diagnostic context.
215 * @param contextStack the nested diagnostic context.
216 * @param threadName The name of the thread.
217 * @param source The locations of the caller.
218 * @param timestamp The timestamp of the event.
219 */
220 private Log4jLogEvent(final String loggerName, final Marker marker, final String loggerFQCN, final Level level,
221 final Message message, final Throwable thrown, final ThrowableProxy thrownProxy,
222 final Map<String, String> contextMap, final ThreadContext.ContextStack contextStack,
223 final String threadName, final StackTraceElement source, final long timestamp) {
224 this.loggerName = loggerName;
225 this.marker = marker;
226 this.loggerFqcn = loggerFQCN;
227 this.level = (level == null) ? Level.OFF : level; // LOG4J2-462, LOG4J2-465
228 this.message = message;
229 this.thrown = thrown;
230 this.thrownProxy = thrownProxy;
231 this.contextMap = contextMap == null ? ThreadContext.EMPTY_MAP : contextMap;
232 this.contextStack = contextStack == null ? ThreadContext.EMPTY_STACK : contextStack;
233 this.timeMillis = message instanceof TimestampMessage ? ((TimestampMessage) message).getTimestamp() : timestamp;
234 this.threadName = threadName;
235 this.source = source;
236 if (message != null && message instanceof LoggerNameAwareMessage) {
237 ((LoggerNameAwareMessage) message).setLoggerName(loggerName);
238 }
239 }
240
241 private static Map<String, String> createMap(final List<Property> properties) {
242 final Map<String, String> contextMap = ThreadContext.getImmutableContext();
243 if (contextMap == null && (properties == null || properties.isEmpty())) {
244 return null;
245 }
246 if (properties == null || properties.isEmpty()) {
247 return contextMap; // contextMap is not null
248 }
249 final Map<String, String> map = new HashMap<String, String>(contextMap);
250
251 for (final Property prop : properties) {
252 if (!map.containsKey(prop.getName())) {
253 map.put(prop.getName(), prop.getValue());
254 }
255 }
256 return Collections.unmodifiableMap(map);
257 }
258
259 /**
260 * Returns the logging Level.
261 * @return the Level associated with this event.
262 */
263 @Override
264 public Level getLevel() {
265 return level;
266 }
267
268 /**
269 * Returns the name of the Logger used to generate the event.
270 * @return The Logger name.
271 */
272 @Override
273 public String getLoggerName() {
274 return loggerName;
275 }
276
277 /**
278 * Returns the Message associated with the event.
279 * @return The Message.
280 */
281 @Override
282 public Message getMessage() {
283 return message;
284 }
285
286 /**
287 * Returns the name of the Thread on which the event was generated.
288 * @return The name of the Thread.
289 */
290 @Override
291 public String getThreadName() {
292 if (threadName == null) {
293 threadName = Thread.currentThread().getName();
294 }
295 return threadName;
296 }
297
298 /**
299 * Returns the time in milliseconds from the epoch when the event occurred.
300 * @return The time the event occurred.
301 */
302 @Override
303 public long getTimeMillis() {
304 return timeMillis;
305 }
306
307 /**
308 * Returns the Throwable associated with the event, or null.
309 * @return The Throwable associated with the event.
310 */
311 @Override
312 public Throwable getThrown() {
313 return thrown;
314 }
315
316 /**
317 * Returns the ThrowableProxy associated with the event, or null.
318 * @return The ThrowableProxy associated with the event.
319 */
320 @Override
321 public ThrowableProxy getThrownProxy() {
322 if (thrownProxy == null && thrown != null) {
323 thrownProxy = new ThrowableProxy(thrown);
324 }
325 return thrownProxy;
326 }
327
328
329 /**
330 * Returns the Marker associated with the event, or null.
331 * @return the Marker associated with the event.
332 */
333 @Override
334 public Marker getMarker() {
335 return marker;
336 }
337
338 /**
339 * The fully qualified class name of the class that was called by the caller.
340 * @return the fully qualified class name of the class that is performing logging.
341 */
342 @Override
343 public String getLoggerFqcn() {
344 return loggerFqcn;
345 }
346
347 /**
348 * Returns the immutable copy of the ThreadContext Map.
349 * @return The context Map.
350 */
351 @Override
352 public Map<String, String> getContextMap() {
353 return contextMap;
354 }
355
356 /**
357 * Returns an immutable copy of the ThreadContext stack.
358 * @return The context Stack.
359 */
360 @Override
361 public ThreadContext.ContextStack getContextStack() {
362 return contextStack;
363 }
364
365 /**
366 * Returns the StackTraceElement for the caller. This will be the entry that occurs right
367 * before the first occurrence of FQCN as a class name.
368 * @return the StackTraceElement for the caller.
369 */
370 @Override
371 public StackTraceElement getSource() {
372 if (source != null) {
373 return source;
374 }
375 if (loggerFqcn == null || !includeLocation) {
376 return null;
377 }
378 source = calcLocation(loggerFqcn);
379 return source;
380 }
381
382 public static StackTraceElement calcLocation(final String fqcnOfLogger) {
383 if (fqcnOfLogger == null) {
384 return null;
385 }
386 final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
387 StackTraceElement last = null;
388 for (int i = stackTrace.length - 1; i > 0; i--) {
389 final String className = stackTrace[i].getClassName();
390 if (fqcnOfLogger.equals(className)) {
391 return last;
392 }
393 last = stackTrace[i];
394 }
395 return null;
396 }
397
398 @Override
399 public boolean isIncludeLocation() {
400 return includeLocation;
401 }
402
403 @Override
404 public void setIncludeLocation(final boolean includeLocation) {
405 this.includeLocation = includeLocation;
406 }
407
408 @Override
409 public boolean isEndOfBatch() {
410 return endOfBatch;
411 }
412
413 @Override
414 public void setEndOfBatch(final boolean endOfBatch) {
415 this.endOfBatch = endOfBatch;
416 }
417
418 /**
419 * Creates a LogEventProxy that can be serialized.
420 * @return a LogEventProxy.
421 */
422 protected Object writeReplace() {
423 getThrownProxy(); // ensure ThrowableProxy is initialized
424 return new LogEventProxy(this, this.includeLocation);
425 }
426
427 public static Serializable serialize(final Log4jLogEvent event,
428 final boolean includeLocation) {
429 event.getThrownProxy(); // ensure ThrowableProxy is initialized
430 return new LogEventProxy(event, includeLocation);
431 }
432
433 public static boolean canDeserialize(final Serializable event) {
434 return event instanceof LogEventProxy;
435 }
436
437 public static Log4jLogEvent deserialize(final Serializable event) {
438 if (event == null) {
439 throw new NullPointerException("Event cannot be null");
440 }
441 if (event instanceof LogEventProxy) {
442 final LogEventProxy proxy = (LogEventProxy) event;
443 final Log4jLogEvent result = new Log4jLogEvent(proxy.loggerName, proxy.marker,
444 proxy.loggerFQCN, proxy.level, proxy.message,
445 proxy.thrown, proxy.thrownProxy, proxy.contextMap, proxy.contextStack, proxy.threadName,
446 proxy.source, proxy.timeMillis);
447 result.setEndOfBatch(proxy.isEndOfBatch);
448 result.setIncludeLocation(proxy.isLocationRequired);
449 return result;
450 }
451 throw new IllegalArgumentException("Event is not a serialized LogEvent: " + event.toString());
452 }
453
454 private void readObject(final ObjectInputStream stream) throws InvalidObjectException {
455 throw new InvalidObjectException("Proxy required");
456 }
457
458 @Override
459 public String toString() {
460 final StringBuilder sb = new StringBuilder();
461 final String n = loggerName.isEmpty() ? "root" : loggerName;
462 sb.append("Logger=").append(n);
463 sb.append(" Level=").append(level.name());
464 sb.append(" Message=").append(message.getFormattedMessage());
465 return sb.toString();
466 }
467
468 @Override
469 public boolean equals(final Object o) {
470 if (this == o) {
471 return true;
472 }
473 if (o == null || getClass() != o.getClass()) {
474 return false;
475 }
476
477 final Log4jLogEvent that = (Log4jLogEvent) o;
478
479 if (endOfBatch != that.endOfBatch) {
480 return false;
481 }
482 if (includeLocation != that.includeLocation) {
483 return false;
484 }
485 if (timeMillis != that.timeMillis) {
486 return false;
487 }
488 if (loggerFqcn != null ? !loggerFqcn.equals(that.loggerFqcn) : that.loggerFqcn != null) {
489 return false;
490 }
491 if (level != null ? !level.equals(that.level) : that.level != null) {
492 return false;
493 }
494 if (source != null ? !source.equals(that.source) : that.source != null) {
495 return false;
496 }
497 if (marker != null ? !marker.equals(that.marker) : that.marker != null) {
498 return false;
499 }
500 if (contextMap != null ? !contextMap.equals(that.contextMap) : that.contextMap != null) {
501 return false;
502 }
503 if (!message.equals(that.message)) {
504 return false;
505 }
506 if (!loggerName.equals(that.loggerName)) {
507 return false;
508 }
509 if (contextStack != null ? !contextStack.equals(that.contextStack) : that.contextStack != null) {
510 return false;
511 }
512 if (threadName != null ? !threadName.equals(that.threadName) : that.threadName != null) {
513 return false;
514 }
515 if (thrown != null ? !thrown.equals(that.thrown) : that.thrown != null) {
516 return false;
517 }
518 if (thrownProxy != null ? !thrownProxy.equals(that.thrownProxy) : that.thrownProxy != null) {
519 return false;
520 }
521
522 return true;
523 }
524
525 @Override
526 public int hashCode() {
527 int result = loggerFqcn != null ? loggerFqcn.hashCode() : 0;
528 result = 31 * result + (marker != null ? marker.hashCode() : 0);
529 result = 31 * result + (level != null ? level.hashCode() : 0);
530 result = 31 * result + loggerName.hashCode();
531 result = 31 * result + message.hashCode();
532 result = 31 * result + (int) (timeMillis ^ (timeMillis >>> 32));
533 result = 31 * result + (thrown != null ? thrown.hashCode() : 0);
534 result = 31 * result + (thrownProxy != null ? thrownProxy.hashCode() : 0);
535 result = 31 * result + (contextMap != null ? contextMap.hashCode() : 0);
536 result = 31 * result + (contextStack != null ? contextStack.hashCode() : 0);
537 result = 31 * result + (threadName != null ? threadName.hashCode() : 0);
538 result = 31 * result + (source != null ? source.hashCode() : 0);
539 result = 31 * result + (includeLocation ? 1 : 0);
540 result = 31 * result + (endOfBatch ? 1 : 0);
541 return result;
542 }
543
544 /**
545 * Proxy pattern used to serialize the LogEvent.
546 */
547 private static class LogEventProxy implements Serializable {
548
549 private static final long serialVersionUID = -7139032940312647146L;
550 private final String loggerFQCN;
551 private final Marker marker;
552 private final Level level;
553 private final String loggerName;
554 private final Message message;
555 private final long timeMillis;
556 private final transient Throwable thrown;
557 private final ThrowableProxy thrownProxy;
558 private final Map<String, String> contextMap;
559 private final ThreadContext.ContextStack contextStack;
560 private final String threadName;
561 private final StackTraceElement source;
562 private final boolean isLocationRequired;
563 private final boolean isEndOfBatch;
564
565 public LogEventProxy(final Log4jLogEvent event, final boolean includeLocation) {
566 this.loggerFQCN = event.loggerFqcn;
567 this.marker = event.marker;
568 this.level = event.level;
569 this.loggerName = event.loggerName;
570 this.message = event.message;
571 this.timeMillis = event.timeMillis;
572 this.thrown = event.thrown;
573 this.thrownProxy = event.thrownProxy;
574 this.contextMap = event.contextMap;
575 this.contextStack = event.contextStack;
576 this.source = includeLocation ? event.getSource() : null;
577 this.threadName = event.getThreadName();
578 this.isLocationRequired = includeLocation;
579 this.isEndOfBatch = event.endOfBatch;
580 }
581
582 /**
583 * Returns a Log4jLogEvent using the data in the proxy.
584 * @return Log4jLogEvent.
585 */
586 protected Object readResolve() {
587 final Log4jLogEvent result = new Log4jLogEvent(loggerName, marker, loggerFQCN, level, message, thrown,
588 thrownProxy, contextMap, contextStack, threadName, source, timeMillis);
589 result.setEndOfBatch(isEndOfBatch);
590 result.setIncludeLocation(isLocationRequired);
591 return result;
592 }
593 }
594 }