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.io.Serializable;
020import java.util.HashMap;
021import java.util.Map;
022import java.util.concurrent.TimeUnit;
023import java.util.zip.Deflater;
024
025import org.apache.logging.log4j.core.Appender;
026import org.apache.logging.log4j.core.Filter;
027import org.apache.logging.log4j.core.Layout;
028import org.apache.logging.log4j.core.LogEvent;
029import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
030import org.apache.logging.log4j.core.appender.rolling.RollingRandomAccessFileManager;
031import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
032import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
033import org.apache.logging.log4j.core.config.Configuration;
034import org.apache.logging.log4j.core.config.plugins.Plugin;
035import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
036import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
037import org.apache.logging.log4j.core.config.plugins.PluginElement;
038import org.apache.logging.log4j.core.config.plugins.PluginFactory;
039import org.apache.logging.log4j.core.layout.PatternLayout;
040import org.apache.logging.log4j.core.net.Advertiser;
041import org.apache.logging.log4j.core.util.Booleans;
042import org.apache.logging.log4j.core.util.Integers;
043
044/**
045 * An appender that writes to random access files and can roll over at
046 * intervals.
047 */
048@Plugin(name = "RollingRandomAccessFile", category = "Core", elementType = Appender.ELEMENT_TYPE, printObject = true)
049public final class RollingRandomAccessFileAppender extends AbstractOutputStreamAppender<RollingRandomAccessFileManager> {
050
051    private final String fileName;
052    private final String filePattern;
053    private final Object advertisement;
054    private final Advertiser advertiser;
055
056    private RollingRandomAccessFileAppender(final String name, final Layout<? extends Serializable> layout,
057            final Filter filter, final RollingRandomAccessFileManager manager, final String fileName,
058            final String filePattern, final boolean ignoreExceptions,
059            final boolean immediateFlush, final int bufferSize, final Advertiser advertiser) {
060        super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
061        if (advertiser != null) {
062            final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
063            configuration.put("contentType", layout.getContentType());
064            configuration.put("name", name);
065            advertisement = advertiser.advertise(configuration);
066        } else {
067            advertisement = null;
068        }
069        this.fileName = fileName;
070        this.filePattern = filePattern;
071        this.advertiser = advertiser;
072    }
073
074    @Override
075    public boolean stop(final long timeout, final TimeUnit timeUnit) {
076        setStopping();
077        super.stop(timeout, timeUnit, false);
078        if (advertiser != null) {
079            advertiser.unadvertise(advertisement);
080        }
081        setStopped();
082        return true;
083    }
084
085    /**
086     * Write the log entry rolling over the file when required.
087     *
088     * @param event The LogEvent.
089     */
090    @Override
091    public void append(final LogEvent event) {
092        final RollingRandomAccessFileManager manager = getManager();
093        manager.checkRollover(event);
094
095        // Leverage the nice batching behaviour of async Loggers/Appenders:
096        // we can signal the file manager that it needs to flush the buffer
097        // to disk at the end of a batch.
098        // From a user's point of view, this means that all log events are
099        // _always_ available in the log file, without incurring the overhead
100        // of immediateFlush=true.
101        manager.setEndOfBatch(event.isEndOfBatch()); // FIXME manager's EndOfBatch threadlocal can be deleted
102
103        // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass
104        super.append(event);
105    }
106
107    /**
108     * Returns the File name for the Appender.
109     *
110     * @return The file name.
111     */
112    public String getFileName() {
113        return fileName;
114    }
115
116    /**
117     * Returns the file pattern used when rolling over.
118     *
119     * @return The file pattern.
120     */
121    public String getFilePattern() {
122        return filePattern;
123    }
124
125    /**
126     * Returns the size of the file manager's buffer.
127     * @return the buffer size
128     */
129    public int getBufferSize() {
130        return getManager().getBufferSize();
131    }
132
133    /**
134     * Create a RollingRandomAccessFileAppender.
135     *
136     * @param fileName The name of the file that is actively written to.
137     *            (required).
138     * @param filePattern The pattern of the file name to use on rollover.
139     *            (required).
140     * @param append If true, events are appended to the file. If false, the
141     *            file is overwritten when opened. Defaults to "true"
142     * @param name The name of the Appender (required).
143     * @param immediateFlush When true, events are immediately flushed. Defaults
144     *            to "true".
145     * @param bufferSizeStr The buffer size, defaults to {@value RollingRandomAccessFileManager#DEFAULT_BUFFER_SIZE}.
146     * @param policy The triggering policy. (required).
147     * @param strategy The rollover strategy. Defaults to
148     *            DefaultRolloverStrategy.
149     * @param layout The layout to use (defaults to the default PatternLayout).
150     * @param filter The Filter or null.
151     * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
152     *               they are propagated to the caller.
153     * @param advertise "true" if the appender configuration should be
154     *            advertised, "false" otherwise.
155     * @param advertiseURI The advertised URI which can be used to retrieve the
156     *            file contents.
157     * @param config The Configuration.
158     * @return A RollingRandomAccessFileAppender.
159     */
160    @PluginFactory
161    public static RollingRandomAccessFileAppender createAppender(
162            @PluginAttribute("fileName") final String fileName,
163            @PluginAttribute("filePattern") final String filePattern,
164            @PluginAttribute("append") final String append,
165            @PluginAttribute("name") final String name,
166            @PluginAttribute("immediateFlush") final String immediateFlush,
167            @PluginAttribute("bufferSize") final String bufferSizeStr,
168            @PluginElement("Policy") final TriggeringPolicy policy,
169            @PluginElement("Strategy") RolloverStrategy strategy,
170            @PluginElement("Layout") Layout<? extends Serializable> layout,
171            @PluginElement("Filter") final Filter filter,
172            @PluginAttribute("ignoreExceptions") final String ignore,
173            @PluginAttribute("advertise") final String advertise,
174            @PluginAttribute("advertiseURI") final String advertiseURI,
175            @PluginConfiguration final Configuration config) {
176
177        final boolean isAppend = Booleans.parseBoolean(append, true);
178        final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
179        final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
180        final boolean isAdvertise = Boolean.parseBoolean(advertise);
181        final int bufferSize = Integers.parseInt(bufferSizeStr, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE);
182
183        if (name == null) {
184            LOGGER.error("No name provided for FileAppender");
185            return null;
186        }
187
188        if (fileName == null) {
189            LOGGER.error("No filename was provided for FileAppender with name " + name);
190            return null;
191        }
192
193        if (filePattern == null) {
194            LOGGER.error("No filename pattern provided for FileAppender with name " + name);
195            return null;
196        }
197
198        if (policy == null) {
199            LOGGER.error("A TriggeringPolicy must be provided");
200            return null;
201        }
202
203        if (strategy == null) {
204            strategy = DefaultRolloverStrategy.createStrategy(null, null, null,
205                    String.valueOf(Deflater.DEFAULT_COMPRESSION), null, true, config);
206        }
207
208        if (layout == null) {
209            layout = PatternLayout.createDefaultLayout();
210        }
211
212        final RollingRandomAccessFileManager manager = RollingRandomAccessFileManager.getRollingRandomAccessFileManager(
213                fileName, filePattern, isAppend, isFlush, bufferSize, policy, strategy, advertiseURI, layout, config);
214        if (manager == null) {
215            return null;
216        }
217
218        manager.initialize();
219
220        return new RollingRandomAccessFileAppender(name, layout, filter, manager,
221                fileName, filePattern, ignoreExceptions, isFlush, bufferSize,
222                isAdvertise ? config.getAdvertiser() : null);
223    }
224}