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