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 074 public void setValues(final AsyncLogger asyncLogger, final String loggerName, final Marker marker, 075 final String fqcn, final Level level, final Message data, final Throwable throwable, 076 final Map<String, String> map, final ContextStack contextStack, final String threadName, 077 final StackTraceElement location, final long currentTimeMillis) { 078 this.asyncLogger = asyncLogger; 079 this.loggerName = loggerName; 080 this.marker = marker; 081 this.fqcn = fqcn; 082 this.level = level; 083 this.message = data; 084 this.thrown = throwable; 085 this.thrownProxy = null; 086 this.contextMap = map; 087 this.contextStack = contextStack; 088 this.threadName = threadName; 089 this.location = location; 090 this.currentTimeMillis = currentTimeMillis; 091 } 092 093 /** 094 * Event processor that reads the event from the ringbuffer can call this method. 095 * 096 * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer 097 */ 098 public void execute(final boolean endOfBatch) { 099 this.endOfBatch = endOfBatch; 100 asyncLogger.actualAsyncLog(this); 101 } 102 103 /** 104 * Returns {@code true} if this event is the end of a batch, {@code false} otherwise. 105 * 106 * @return {@code true} if this event is the end of a batch, {@code false} otherwise 107 */ 108 @Override 109 public boolean isEndOfBatch() { 110 return endOfBatch; 111 } 112 113 @Override 114 public void setEndOfBatch(final boolean endOfBatch) { 115 this.endOfBatch = endOfBatch; 116 } 117 118 @Override 119 public boolean isIncludeLocation() { 120 return includeLocation; 121 } 122 123 @Override 124 public void setIncludeLocation(final boolean includeLocation) { 125 this.includeLocation = includeLocation; 126 } 127 128 @Override 129 public String getLoggerName() { 130 return loggerName; 131 } 132 133 @Override 134 public Marker getMarker() { 135 return marker; 136 } 137 138 @Override 139 public String getLoggerFqcn() { 140 return fqcn; 141 } 142 143 @Override 144 public Level getLevel() { 145 if (level == null) { 146 level = Level.OFF; // LOG4J2-462, LOG4J2-465 147 } 148 return level; 149 } 150 151 @Override 152 public Message getMessage() { 153 if (message == null) { 154 message = new SimpleMessage(Strings.EMPTY); 155 } 156 return message; 157 } 158 159 @Override 160 public Throwable getThrown() { 161 // after deserialization, thrown is null but thrownProxy may be non-null 162 if (thrown == null) { 163 if (thrownProxy != null) { 164 thrown = thrownProxy.getThrowable(); 165 } 166 } 167 return thrown; 168 } 169 170 @Override 171 public ThrowableProxy getThrownProxy() { 172 // lazily instantiate the (expensive) ThrowableProxy 173 if (thrownProxy == null) { 174 if (thrown != null) { 175 thrownProxy = new ThrowableProxy(thrown); 176 } 177 } 178 return this.thrownProxy; 179 } 180 181 @Override 182 public Map<String, String> getContextMap() { 183 return contextMap; 184 } 185 186 @Override 187 public ContextStack getContextStack() { 188 return contextStack; 189 } 190 191 @Override 192 public String getThreadName() { 193 return threadName; 194 } 195 196 @Override 197 public StackTraceElement getSource() { 198 return location; 199 } 200 201 @Override 202 public long getTimeMillis() { 203 return currentTimeMillis; 204 } 205 206 /** 207 * Merges the contents of the specified map into the contextMap, after replacing any variables in the property 208 * values with the StrSubstitutor-supplied actual values. 209 * 210 * @param properties configured properties 211 * @param strSubstitutor used to lookup values of variables in properties 212 */ 213 public void mergePropertiesIntoContextMap(final Map<Property, Boolean> properties, 214 final StrSubstitutor strSubstitutor) { 215 if (properties == null) { 216 return; // nothing to do 217 } 218 219 final Map<String, String> map = contextMap == null ? new HashMap<String, String>() 220 : new HashMap<String, String>(contextMap); 221 222 for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) { 223 final Property prop = entry.getKey(); 224 if (map.containsKey(prop.getName())) { 225 continue; // contextMap overrides config properties 226 } 227 final String value = entry.getValue().booleanValue() ? strSubstitutor.replace(prop.getValue()) : prop 228 .getValue(); 229 map.put(prop.getName(), value); 230 } 231 contextMap = map; 232 } 233 234 /** 235 * Release references held by ring buffer to allow objects to be garbage-collected. 236 */ 237 public void clear() { 238 setValues(null, // asyncLogger 239 null, // loggerName 240 null, // marker 241 null, // fqcn 242 null, // level 243 null, // data 244 null, // t 245 null, // map 246 null, // contextStack 247 null, // threadName 248 null, // location 249 0 // currentTimeMillis 250 ); 251 } 252 253 private void writeObject(final java.io.ObjectOutputStream out) throws IOException { 254 getThrownProxy(); // initialize the ThrowableProxy before serializing 255 out.defaultWriteObject(); 256 } 257 258 /** 259 * Creates and returns a new immutable copy of this {@code RingBufferLogEvent}. 260 * 261 * @return a new immutable copy of the data in this {@code RingBufferLogEvent} 262 */ 263 public LogEvent createMemento() { 264 // Ideally, would like to use the LogEventFactory here but signature does not match: 265 // results in factory re-creating the timestamp, context map and context stack, which we don't want. 266 return new Log4jLogEvent(loggerName, marker, fqcn, level, message, thrown, contextMap, contextStack, 267 threadName, location, currentTimeMillis); 268 } 269}