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.async;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.logging.log4j.Level;
023import org.apache.logging.log4j.Marker;
024import org.apache.logging.log4j.ThreadContext.ContextStack;
025import org.apache.logging.log4j.core.LogEvent;
026import org.apache.logging.log4j.core.config.Property;
027import org.apache.logging.log4j.core.lookup.StrSubstitutor;
028import org.apache.logging.log4j.message.Message;
029import org.apache.logging.log4j.message.SimpleMessage;
030import org.apache.logging.log4j.message.TimestampMessage;
031
032import com.lmax.disruptor.EventFactory;
033
034/**
035 * When the Disruptor is started, the RingBuffer is populated with event
036 * objects. These objects are then re-used during the life of the RingBuffer.
037 */
038public class RingBufferLogEvent implements LogEvent {
039    private static final long serialVersionUID = 8462119088943934758L;
040
041    /**
042     * Creates the events that will be put in the RingBuffer.
043     */
044    private static class Factory implements EventFactory<RingBufferLogEvent> {
045    
046        @Override
047        public RingBufferLogEvent newInstance() {
048            return new RingBufferLogEvent();
049        }
050    }
051
052    /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
053    public static final Factory FACTORY = new Factory();
054
055    private AsyncLogger asyncLogger;
056    private String loggerName;
057    private Marker marker;
058    private String fqcn;
059    private Level level;
060    private Message message;
061    private Throwable thrown;
062    private Map<String, String> contextMap;
063    private ContextStack contextStack;
064    private String threadName;
065    private StackTraceElement location;
066    private long currentTimeMillis;
067    private boolean endOfBatch;
068    private boolean includeLocation;
069
070    public void setValues(final AsyncLogger asyncLogger,
071            final String loggerName, final Marker marker, final String fqcn,
072            final Level level, final Message data, final Throwable t,
073            final Map<String, String> map, final ContextStack contextStack,
074            final String threadName, final StackTraceElement location,
075            final long currentTimeMillis) {
076        this.asyncLogger = asyncLogger;
077        this.loggerName = loggerName;
078        this.marker = marker;
079        this.fqcn = fqcn;
080        this.level = level;
081        this.message = data;
082        this.thrown = t;
083        this.contextMap = map;
084        this.contextStack = contextStack;
085        this.threadName = threadName;
086        this.location = location;
087        this.currentTimeMillis = currentTimeMillis;
088    }
089
090    /**
091     * Event processor that reads the event from the ringbuffer can call this
092     * method.
093     *
094     * @param endOfBatch flag to indicate if this is the last event in a batch
095     *            from the RingBuffer
096     */
097    public void execute(final boolean endOfBatch) {
098        this.endOfBatch = endOfBatch;
099        asyncLogger.actualAsyncLog(this);
100    }
101
102    /**
103     * Returns {@code true} if this event is the end of a batch, {@code false}
104     * otherwise.
105     *
106     * @return {@code true} if this event is the end of a batch, {@code false}
107     *         otherwise
108     */
109    @Override
110    public boolean isEndOfBatch() {
111        return endOfBatch;
112    }
113
114    @Override
115    public void setEndOfBatch(final boolean endOfBatch) {
116        this.endOfBatch = endOfBatch;
117    }
118
119    @Override
120    public boolean isIncludeLocation() {
121        return includeLocation;
122    }
123
124    @Override
125    public void setIncludeLocation(final boolean includeLocation) {
126        this.includeLocation = includeLocation;
127    }
128
129    @Override
130    public String getLoggerName() {
131        return loggerName;
132    }
133
134    @Override
135    public Marker getMarker() {
136        return marker;
137    }
138
139    @Override
140    public String getFQCN() {
141        return fqcn;
142    }
143
144    @Override
145    public Level getLevel() {
146        if (level == null) {
147            level = Level.OFF; // LOG4J2-462, LOG4J2-465
148        }
149        return level;
150    }
151
152    @Override
153    public Message getMessage() {
154        if (message == null) {
155            message = new SimpleMessage("");
156        }
157        return message;
158    }
159
160    @Override
161    public Throwable getThrown() {
162        return thrown;
163    }
164
165    @Override
166    public Map<String, String> getContextMap() {
167        return contextMap;
168    }
169
170    @Override
171    public ContextStack getContextStack() {
172        return contextStack;
173    }
174
175    @Override
176    public String getThreadName() {
177        return threadName;
178    }
179
180    @Override
181    public StackTraceElement getSource() {
182        return location;
183    }
184
185    @Override
186    public long getMillis() {
187        Message msg = getMessage();
188        if (msg instanceof TimestampMessage) { // LOG4J2-455
189            return ((TimestampMessage) msg).getTimestamp();
190        }
191        return currentTimeMillis;
192    }
193
194    /**
195     * Merges the contents of the specified map into the contextMap, after
196     * replacing any variables in the property values with the
197     * StrSubstitutor-supplied actual values.
198     *
199     * @param properties configured properties
200     * @param strSubstitutor used to lookup values of variables in properties
201     */
202    public void mergePropertiesIntoContextMap(
203            final Map<Property, Boolean> properties,
204            final StrSubstitutor strSubstitutor) {
205        if (properties == null) {
206            return; // nothing to do
207        }
208
209        final Map<String, String> map = (contextMap == null) ? new HashMap<String, String>()
210                : new HashMap<String, String>(contextMap);
211
212        for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) {
213            final Property prop = entry.getKey();
214            if (map.containsKey(prop.getName())) {
215                continue; // contextMap overrides config properties
216            }
217            final String value = entry.getValue() ? strSubstitutor.replace(prop
218                    .getValue()) : prop.getValue();
219            map.put(prop.getName(), value);
220        }
221        contextMap = map;
222    }
223
224    /**
225     * Release references held by ring buffer to allow objects to be
226     * garbage-collected.
227     */
228    public void clear() {
229        setValues(null, // asyncLogger
230                null, // loggerName
231                null, // marker
232                null, // fqcn
233                null, // level
234                null, // data
235                null, // t
236                null, // map
237                null, // contextStack
238                null, // threadName
239                null, // location
240                0 // currentTimeMillis
241        );
242    }
243}