View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.core.async;
    18
    19  import java.io.IOException;
    20  import java.util.HashMap;
    21  import java.util.Map;
    22
    23  import org.apache.logging.log4j.Level;
    24  import org.apache.logging.log4j.Marker;
    25  import org.apache.logging.log4j.ThreadContext.ContextStack;
    26  import org.apache.logging.log4j.core.LogEvent;
    27  import org.apache.logging.log4j.core.config.Property;
    28  import org.apache.logging.log4j.core.impl.Log4jLogEvent;
    29  import org.apache.logging.log4j.core.impl.ThrowableProxy;
    30  import org.apache.logging.log4j.core.lookup.StrSubstitutor;
    31  import org.apache.logging.log4j.message.Message;
    32  import org.apache.logging.log4j.message.SimpleMessage;
    33  import org.apache.logging.log4j.util.Strings;
    34
    35  import com.lmax.disruptor.EventFactory;
    36
    37  /**
    38   * When the Disruptor is started, the RingBuffer is populated with event objects. These objects are then re-used during
    39   * the life of the RingBuffer.
    40   */
    41  public class RingBufferLogEvent implements LogEvent {
    42      private static final long serialVersionUID = 8462119088943934758L;
    43
    44      /**
    45       * Creates the events that will be put in the RingBuffer.
    46       */
    47      private static class Factory implements EventFactory<RingBufferLogEvent> {
    48
    49          @Override
    50          public RingBufferLogEvent newInstance() {
    51              return new RingBufferLogEvent();
    52          }
    53      }
    54
    55      /** The {@code EventFactory} for {@code RingBufferLogEvent}s. */
    56      public static final Factory FACTORY = new Factory();
    57
    58      private transient AsyncLogger asyncLogger;
    59      private String loggerName;
    60      private Marker marker;
    61      private String fqcn;
    62      private Level level;
    63      private Message message;
    64      private transient Throwable thrown;
    65      private ThrowableProxy thrownProxy;
    66      private Map<String, String> contextMap;
    67      private ContextStack contextStack;
    68      private String threadName;
    69      private StackTraceElement location;
    70      private long currentTimeMillis;
    71      private boolean endOfBatch;
    72      private boolean includeLocation;
    73
    74      public void setValues(final AsyncLogger asyncLogger, final String loggerName, final Marker marker,
    75              final String fqcn, final Level level, final Message data, final Throwable throwable,
    76              final Map<String, String> map, final ContextStack contextStack, final String threadName,
    77              final StackTraceElement location, final long currentTimeMillis) {
    78          this.asyncLogger = asyncLogger;
    79          this.loggerName = loggerName;
    80          this.marker = marker;
    81          this.fqcn = fqcn;
    82          this.level = level;
    83          this.message = data;
    84          this.thrown = throwable;
    85          this.thrownProxy = null;
    86          this.contextMap = map;
    87          this.contextStack = contextStack;
    88          this.threadName = threadName;
    89          this.location = location;
    90          this.currentTimeMillis = currentTimeMillis;
    91      }
    92
    93      /**
    94       * Event processor that reads the event from the ringbuffer can call this method.
    95       *
    96       * @param endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
    97       */
    98      public void execute(final boolean endOfBatch) {
    99          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 }