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.appender;
018
019 import org.apache.logging.log4j.core.Filter;
020 import org.apache.logging.log4j.core.Layout;
021 import org.apache.logging.log4j.core.LogEvent;
022
023 import java.io.Serializable;
024 import java.util.concurrent.locks.Lock;
025 import java.util.concurrent.locks.ReadWriteLock;
026 import java.util.concurrent.locks.ReentrantReadWriteLock;
027
028 /**
029 * Appends log events as bytes to a byte output stream. The stream encoding is defined in the layout.
030 */
031 public abstract class AbstractOutputStreamAppender<T extends Serializable> extends AbstractAppender<T> {
032
033 /**
034 * Immediate flush means that the underlying writer or output stream
035 * will be flushed at the end of each append operation. Immediate
036 * flush is slower but ensures that each append request is actually
037 * written. If <code>immediateFlush</code> is set to
038 * {@code false}, then there is a good chance that the last few
039 * logs events are not actually written to persistent media if and
040 * when the application crashes.
041 */
042 protected final boolean immediateFlush;
043
044 private volatile OutputStreamManager manager;
045
046 private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
047 private final Lock readLock = rwLock.readLock();
048 private final Lock writeLock = rwLock.writeLock();
049
050 /**
051 * Instantiate a WriterAppender and set the output destination to a
052 * new {@link java.io.OutputStreamWriter} initialized with <code>os</code>
053 * as its {@link java.io.OutputStream}.
054 * @param name The name of the Appender.
055 * @param layout The layout to format the message.
056 * @param manager The OutputStreamManager.
057 */
058 protected AbstractOutputStreamAppender(final String name, final Layout<T> layout, final Filter filter,
059 final boolean handleException, final boolean immediateFlush,
060 final OutputStreamManager manager) {
061 super(name, filter, layout, handleException);
062 if (layout != null) {
063 manager.setHeader(layout.getHeader());
064 manager.setFooter(layout.getFooter());
065 }
066 this.manager = manager;
067 this.immediateFlush = immediateFlush;
068 }
069
070 protected OutputStreamManager getManager() {
071 return manager;
072 }
073
074 protected void replaceManager(final OutputStreamManager newManager) {
075
076 writeLock.lock();
077 try {
078 final OutputStreamManager old = manager;
079 manager = newManager;
080 old.release();
081 } finally {
082 writeLock.unlock();
083 }
084
085 }
086
087 @Override
088 public void start() {
089 if (getLayout() == null) {
090 LOGGER.error("No layout set for the appender named [" + getName() + "].");
091 }
092 if (manager == null) {
093 LOGGER.error("No OutputStreamManager set for the appender named [" + getName() + "].");
094 }
095 super.start();
096 }
097
098 @Override
099 public void stop() {
100 super.stop();
101 manager.release();
102 }
103
104 /**
105 * Actual writing occurs here.
106 * <p/>
107 * <p>Most subclasses of <code>AbstractOutputStreamAppender</code> will need to
108 * override this method.
109 * @param event The LogEvent.
110 */
111 @Override
112 public void append(final LogEvent event) {
113 readLock.lock();
114 try {
115 final byte[] bytes = getLayout().toByteArray(event);
116 if (bytes.length > 0) {
117 manager.write(bytes);
118 if (this.immediateFlush || event.isEndOfBatch()) {
119 manager.flush();
120 }
121 }
122 } catch (final AppenderRuntimeException ex) {
123 error("Unable to write to stream " + manager.getName() + " for appender " + getName());
124 throw ex;
125 } finally {
126 readLock.unlock();
127 }
128 }
129 }