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(final String name, final Layout layout, final Filter filter,
058                                               final boolean handleException, final boolean immediateFlush,
059                                               final OutputStreamManager manager) {
060            super(name, filter, layout, handleException);
061            if (layout != null) {
062                manager.setHeader(layout.getHeader());
063                manager.setFooter(layout.getFooter());
064            }
065            this.manager = manager;
066            this.immediateFlush = immediateFlush;
067        }
068    
069        protected OutputStreamManager getManager() {
070            return manager;
071        }
072    
073        protected void replaceManager(final OutputStreamManager newManager) {
074    
075            writeLock.lock();
076            try {
077                final OutputStreamManager old = manager;
078                manager = newManager;
079                old.release();
080            } finally {
081                writeLock.unlock();
082            }
083    
084        }
085    
086        @Override
087        public void start() {
088            if (getLayout() == null) {
089                LOGGER.error("No layout set for the appender named [" + getName() + "].");
090            }
091            if (manager == null) {
092                LOGGER.error("No OutputStreamManager set for the appender named [" + getName() + "].");
093            }
094            super.start();
095        }
096    
097        @Override
098        public void stop() {
099            super.stop();
100            manager.release();
101        }
102    
103        /**
104         * Actual writing occurs here.
105         * <p/>
106         * <p>Most subclasses of <code>AbstractOutputStreamAppender</code> will need to
107         * override this method.
108         * @param event The LogEvent.
109         */
110        public void append(final LogEvent event) {
111            readLock.lock();
112            try {
113                final byte[] bytes = getLayout().toByteArray(event);
114                if (bytes.length > 0) {
115                    manager.write(bytes);
116                    if (this.immediateFlush) {
117                        manager.flush();
118                    }
119                }
120            } catch (final AppenderRuntimeException ex) {
121                error("Unable to write to stream " + manager.getName() + " for appender " + getName());
122                throw ex;
123            } finally {
124                readLock.unlock();
125            }
126        }
127    }