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    }