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