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