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