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.io.IOException; 020import java.util.HashMap; 021import java.util.Map; 022 023import org.apache.logging.log4j.Level; 024import org.apache.logging.log4j.Marker; 025import org.apache.logging.log4j.ThreadContext.ContextStack; 026import org.apache.logging.log4j.core.LogEvent; 027import org.apache.logging.log4j.core.config.Property; 028import org.apache.logging.log4j.core.impl.Log4jLogEvent; 029import org.apache.logging.log4j.core.impl.ThrowableProxy; 030import org.apache.logging.log4j.core.lookup.StrSubstitutor; 031import org.apache.logging.log4j.message.Message; 032import org.apache.logging.log4j.message.SimpleMessage; 033import org.apache.logging.log4j.util.Strings; 034 035import com.lmax.disruptor.EventFactory; 036 037/** 038 * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during 039 * the life of the RingBuffer. 040 */ 041public class RingBufferLogEvent implements LogEvent { 042 043 /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */ 044 public static final Factory FACTORY = new Factory(); 045 046 private static final long serialVersionUID = 8462119088943934758L; 047 048 /** 049 * Creates the events that will be put in the RingBuffer. 050 */ 051 private static class Factory implements EventFactory<RingBufferLogEvent> { 052 053 @Override 054 public RingBufferLogEvent newInstance() { 055 return new RingBufferLogEvent(); 056 } 057 } 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 private long nanoTime; 075 076 public void setValues(final AsyncLogger anAsyncLogger, final String aLoggerName, final Marker aMarker, 077 final String theFqcn, final Level aLevel, final Message msg, final Throwable aThrowable, 078 final Map<String, String> aMap, final ContextStack aContextStack, final String aThreadName, 079 final StackTraceElement aLocation, final long aCurrentTimeMillis, final long aNanoTime) { 080 this.asyncLogger = anAsyncLogger; 081 this.loggerName = aLoggerName; 082 this.marker = aMarker; 083 this.fqcn = theFqcn; 084 this.level = aLevel; 085 this.message = msg; 086 this.thrown = aThrowable; 087 this.thrownProxy = null; 088 this.contextMap = aMap; 089 this.contextStack = aContextStack; 090 this.threadName = aThreadName; 091 this.location = aLocation; 092 this.currentTimeMillis = aCurrentTimeMillis; 093 this.nanoTime = aNanoTime; 094 } 095 096 /** 097 * Event processor that reads the event from the ringbuffer can call this method. 098 * 099 * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer 100 */ 101 public void execute(final boolean endOfBatch) { 102 this.endOfBatch = endOfBatch; 103 asyncLogger.actualAsyncLog(this); 104 } 105 106 /** 107 * Returns {@code true} if this event is the end of a batch, {@code false} otherwise. 108 * 109 * @return {@code true} if this event is the end of a batch, {@code false} otherwise 110 */ 111 @Override 112 public boolean isEndOfBatch() { 113 return endOfBatch; 114 } 115 116 @Override 117 public void setEndOfBatch(final boolean endOfBatch) { 118 this.endOfBatch = endOfBatch; 119 } 120 121 @Override 122 public boolean isIncludeLocation() { 123 return includeLocation; 124 } 125 126 @Override 127 public void setIncludeLocation(final boolean includeLocation) { 128 this.includeLocation = includeLocation; 129 } 130 131 @Override 132 public String getLoggerName() { 133 return loggerName; 134 } 135 136 @Override 137 public Marker getMarker() { 138 return marker; 139 } 140 141 @Override 142 public String getLoggerFqcn() { 143 return fqcn; 144 } 145 146 @Override 147 public Level getLevel() { 148 if (level == null) { 149 level = Level.OFF; // LOG4J2-462, LOG4J2-465 150 } 151 return level; 152 } 153 154 @Override 155 public Message getMessage() { 156 if (message == null) { 157 message = new SimpleMessage(Strings.EMPTY); 158 } 159 return message; 160 } 161 162 @Override 163 public Throwable getThrown() { 164 // after deserialization, thrown is null but thrownProxy may be non-null 165 if (thrown == null) { 166 if (thrownProxy != null) { 167 thrown = thrownProxy.getThrowable(); 168 } 169 } 170 return thrown; 171 } 172 173 @Override 174 public ThrowableProxy getThrownProxy() { 175 // lazily instantiate the (expensive) ThrowableProxy 176 if (thrownProxy == null) { 177 if (thrown != null) { 178 thrownProxy = new ThrowableProxy(thrown); 179 } 180 } 181 return this.thrownProxy; 182 } 183 184 @Override 185 public Map<String, String> getContextMap() { 186 return contextMap; 187 } 188 189 @Override 190 public ContextStack getContextStack() { 191 return contextStack; 192 } 193 194 @Override 195 public String getThreadName() { 196 return threadName; 197 } 198 199 @Override 200 public StackTraceElement getSource() { 201 return location; 202 } 203 204 @Override 205 public long getTimeMillis() { 206 return currentTimeMillis; 207 } 208 209 @Override 210 public long getNanoTime() { 211 return nanoTime; 212 } 213 214 /** 215 * Merges the contents of the specified map into the contextMap, after replacing any variables in the property 216 * values with the StrSubstitutor-supplied actual values. 217 * 218 * @param properties configured properties 219 * @param strSubstitutor used to lookup values of variables in properties 220 */ 221 public void mergePropertiesIntoContextMap(final Map<Property, Boolean> properties, 222 final StrSubstitutor strSubstitutor) { 223 if (properties == null) { 224 return; // nothing to do 225 } 226 227 final Map<String, String> map = contextMap == null ? new HashMap<String, String>() 228 : new HashMap<>(contextMap); 229 230 for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) { 231 final Property prop = entry.getKey(); 232 if (map.containsKey(prop.getName())) { 233 continue; // contextMap overrides config properties 234 } 235 final String value = entry.getValue().booleanValue() ? strSubstitutor.replace(prop.getValue()) : prop 236 .getValue(); 237 map.put(prop.getName(), value); 238 } 239 contextMap = map; 240 } 241 242 /** 243 * Release references held by ring buffer to allow objects to be garbage-collected. 244 */ 245 public void clear() { 246 setValues(null, // asyncLogger 247 null, // loggerName 248 null, // marker 249 null, // fqcn 250 null, // level 251 null, // data 252 null, // t 253 null, // map 254 null, // contextStack 255 null, // threadName 256 null, // location 257 0, // currentTimeMillis 258 0 // nanoTime 259 ); 260 } 261 262 private void writeObject(final java.io.ObjectOutputStream out) throws IOException { 263 getThrownProxy(); // initialize the ThrowableProxy before serializing 264 out.defaultWriteObject(); 265 } 266 267 /** 268 * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}. 269 * 270 * @return a new immutable copy of the data in this {@code RingBufferLogEvent} 271 */ 272 public LogEvent createMemento() { 273 final LogEvent result = new Log4jLogEvent.Builder(this).build(); 274 return result; 275 } 276 277 /** 278 * Initializes the specified {@code Log4jLogEvent.Builder} from this {@code RingBufferLogEvent}. 279 * @param builder the builder whose fields to populate 280 */ 281 public void initializeBuilder(Log4jLogEvent.Builder builder) { 282 builder.setContextMap(contextMap) // 283 .setContextStack(contextStack) // 284 .setEndOfBatch(endOfBatch) // 285 .setIncludeLocation(includeLocation) // 286 .setLevel(getLevel()) // ensure non-null 287 .setLoggerFqcn(fqcn) // 288 .setLoggerName(loggerName) // 289 .setMarker(marker) // 290 .setMessage(getMessage()) // ensure non-null 291 .setNanoTime(nanoTime) // 292 .setSource(location) // 293 .setThreadName(threadName) // 294 .setThrown(getThrown()) // may deserialize from thrownProxy 295 .setThrownProxy(thrownProxy) // avoid unnecessarily creating thrownProxy 296 .setTimeMillis(currentTimeMillis); 297 } 298}