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.async; 018 019 import java.io.IOException; 020 import java.util.HashMap; 021 import java.util.Map; 022 023 import org.apache.logging.log4j.Level; 024 import org.apache.logging.log4j.Marker; 025 import org.apache.logging.log4j.ThreadContext.ContextStack; 026 import org.apache.logging.log4j.core.LogEvent; 027 import org.apache.logging.log4j.core.config.Property; 028 import org.apache.logging.log4j.core.impl.Log4jLogEvent; 029 import org.apache.logging.log4j.core.impl.ThrowableProxy; 030 import org.apache.logging.log4j.core.lookup.StrSubstitutor; 031 import org.apache.logging.log4j.message.Message; 032 import org.apache.logging.log4j.message.SimpleMessage; 033 import org.apache.logging.log4j.message.TimestampMessage; 034 import org.apache.logging.log4j.util.Strings; 035 036 import com.lmax.disruptor.EventFactory; 037 038 /** 039 * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during 040 * the life of the RingBuffer. 041 */ 042 public class RingBufferLogEvent implements LogEvent { 043 private static final long serialVersionUID = 8462119088943934758L; 044 045 /** 046 * Creates the events that will be put in the RingBuffer. 047 */ 048 private static class Factory implements EventFactory<RingBufferLogEvent> { 049 050 @Override 051 public RingBufferLogEvent newInstance() { 052 return new RingBufferLogEvent(); 053 } 054 } 055 056 /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */ 057 public static final Factory FACTORY = new Factory(); 058 059 private transient AsyncLogger asyncLogger; 060 private String loggerName; 061 private Marker marker; 062 private String fqcn; 063 private Level level; 064 private Message message; 065 private transient Throwable thrown; 066 private ThrowableProxy thrownProxy; 067 private Map<String, String> contextMap; 068 private ContextStack contextStack; 069 private String threadName; 070 private StackTraceElement location; 071 private long currentTimeMillis; 072 private boolean endOfBatch; 073 private boolean includeLocation; 074 075 public void setValues(final AsyncLogger asyncLogger, final String loggerName, final Marker marker, 076 final String fqcn, final Level level, final Message data, final Throwable throwable, 077 final Map<String, String> map, final ContextStack contextStack, final String threadName, 078 final StackTraceElement location, final long currentTimeMillis) { 079 this.asyncLogger = asyncLogger; 080 this.loggerName = loggerName; 081 this.marker = marker; 082 this.fqcn = fqcn; 083 this.level = level; 084 this.message = data; 085 this.thrown = throwable; 086 this.thrownProxy = null; 087 this.contextMap = map; 088 this.contextStack = contextStack; 089 this.threadName = threadName; 090 this.location = location; 091 this.currentTimeMillis = currentTimeMillis; 092 } 093 094 /** 095 * Event processor that reads the event from the ringbuffer can call this method. 096 * 097 * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer 098 */ 099 public void execute(final boolean endOfBatch) { 100 this.endOfBatch = endOfBatch; 101 asyncLogger.actualAsyncLog(this); 102 } 103 104 /** 105 * Returns {@code true} if this event is the end of a batch, {@code false} otherwise. 106 * 107 * @return {@code true} if this event is the end of a batch, {@code false} 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 getLoggerFqcn() { 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(Strings.EMPTY); 156 } 157 return message; 158 } 159 160 @Override 161 public Throwable getThrown() { 162 // after deserialization, thrown is null but thrownProxy may be non-null 163 if (thrown == null) { 164 if (thrownProxy != null) { 165 thrown = thrownProxy.getThrowable(); 166 } 167 } 168 return thrown; 169 } 170 171 @Override 172 public ThrowableProxy getThrownProxy() { 173 // lazily instantiate the (expensive) ThrowableProxy 174 if (thrownProxy == null) { 175 if (thrown != null) { 176 thrownProxy = new ThrowableProxy(thrown); 177 } 178 } 179 return this.thrownProxy; 180 } 181 182 @Override 183 public Map<String, String> getContextMap() { 184 return contextMap; 185 } 186 187 @Override 188 public ContextStack getContextStack() { 189 return contextStack; 190 } 191 192 @Override 193 public String getThreadName() { 194 return threadName; 195 } 196 197 @Override 198 public StackTraceElement getSource() { 199 return location; 200 } 201 202 @Override 203 public long getTimeMillis() { 204 final Message msg = getMessage(); 205 if (msg instanceof TimestampMessage) { // LOG4J2-455 206 return ((TimestampMessage) msg).getTimestamp(); 207 } 208 return currentTimeMillis; 209 } 210 211 /** 212 * Merges the contents of the specified map into the contextMap, after replacing any variables in the property 213 * values with the StrSubstitutor-supplied actual values. 214 * 215 * @param properties configured properties 216 * @param strSubstitutor used to lookup values of variables in properties 217 */ 218 public void mergePropertiesIntoContextMap(final Map<Property, Boolean> properties, 219 final StrSubstitutor strSubstitutor) { 220 if (properties == null) { 221 return; // nothing to do 222 } 223 224 final Map<String, String> map = contextMap == null ? new HashMap<String, String>() 225 : new HashMap<String, String>(contextMap); 226 227 for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) { 228 final Property prop = entry.getKey(); 229 if (map.containsKey(prop.getName())) { 230 continue; // contextMap overrides config properties 231 } 232 final String value = entry.getValue().booleanValue() ? strSubstitutor.replace(prop.getValue()) : prop 233 .getValue(); 234 map.put(prop.getName(), value); 235 } 236 contextMap = map; 237 } 238 239 /** 240 * Release references held by ring buffer to allow objects to be garbage-collected. 241 */ 242 public void clear() { 243 setValues(null, // asyncLogger 244 null, // loggerName 245 null, // marker 246 null, // fqcn 247 null, // level 248 null, // data 249 null, // t 250 null, // map 251 null, // contextStack 252 null, // threadName 253 null, // location 254 0 // currentTimeMillis 255 ); 256 } 257 258 private void writeObject(final java.io.ObjectOutputStream out) throws IOException { 259 getThrownProxy(); // initialize the ThrowableProxy before serializing 260 out.defaultWriteObject(); 261 } 262 263 /** 264 * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}. 265 * 266 * @return a new immutable copy of the data in this {@code RingBufferLogEvent} 267 */ 268 public LogEvent createMemento() { 269 // Ideally, would like to use the LogEventFactory here but signature does not match: 270 // results in factory re-creating the timestamp, context map and context stack, which we don't want. 271 return new Log4jLogEvent(loggerName, marker, fqcn, level, message, thrown, contextMap, contextStack, 272 threadName, location, currentTimeMillis); 273 } 274 }