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