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 */
017package org.apache.logging.log4j.core.appender;
018
019import java.util.concurrent.locks.Lock;
020import java.util.concurrent.locks.ReadWriteLock;
021import java.util.concurrent.locks.ReentrantReadWriteLock;
022
023import org.apache.logging.log4j.core.Filter;
024import org.apache.logging.log4j.core.LogEvent;
025import org.apache.logging.log4j.core.StringLayout;
026
027/**
028 * Appends log events as strings to a writer.
029 * 
030 * @param <M>
031 *            The kind of {@link WriterManager} under management
032 */
033public abstract class AbstractWriterAppender<M extends WriterManager> extends AbstractAppender {
034
035    private static final long serialVersionUID = 1L;
036
037    /**
038     * Immediate flush means that the underlying writer will be flushed at the
039     * end of each append operation. Immediate flush is slower but ensures that
040     * each append request is actually written. If <code>immediateFlush</code>
041     * is set to {@code false}, then there is a good chance that the last few
042     * logs events are not actually written to persistent media if and when the
043     * application crashes.
044     */
045    protected final boolean immediateFlush;
046    private final M manager;
047    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
048    private final Lock readLock = readWriteLock.readLock();
049
050    /**
051     * Instantiates.
052     * 
053     * @param name
054     *            The name of the Appender.
055     * @param layout
056     *            The layout to format the message.
057     * @param manager
058     *            The OutputStreamManager.
059     */
060    protected AbstractWriterAppender(final String name, final StringLayout layout, final Filter filter,
061            final boolean ignoreExceptions, final boolean immediateFlush, final M manager) {
062        super(name, filter, layout, ignoreExceptions);
063        this.manager = manager;
064        this.immediateFlush = immediateFlush;
065    }
066
067    /**
068     * Actual writing occurs here.
069     * <p>
070     * Most subclasses will need to override this method.
071     * </p>
072     * 
073     * @param event
074     *            The LogEvent.
075     */
076    @Override
077    public void append(final LogEvent event) {
078        readLock.lock();
079        try {
080            final String str = getStringLayout().toSerializable(event);
081            if (str.length() > 0) {
082                manager.write(str);
083                if (this.immediateFlush || event.isEndOfBatch()) {
084                    manager.flush();
085                }
086            }
087        } catch (final AppenderLoggingException ex) {
088            error("Unable to write " + manager.getName() + " for appender " + getName() + ": " + ex);
089            throw ex;
090        } finally {
091            readLock.unlock();
092        }
093    }
094
095    /**
096     * Gets the manager.
097     * 
098     * @return the manager.
099     */
100    public M getManager() {
101        return manager;
102    }
103
104    public StringLayout getStringLayout() {
105        return (StringLayout) getLayout();
106    }
107
108    @Override
109    public void start() {
110        if (getLayout() == null) {
111            LOGGER.error("No layout set for the appender named [{}].", getName());
112        }
113        if (manager == null) {
114            LOGGER.error("No OutputStreamManager set for the appender named [{}].", getName());
115        }
116        super.start();
117    }
118
119    @Override
120    public void stop() {
121        super.stop();
122        manager.release();
123    }
124}