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.Core;
027import org.apache.logging.log4j.core.Filter;
028import org.apache.logging.log4j.core.Layout;
029import org.apache.logging.log4j.core.LogEvent;
030import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
031import org.apache.logging.log4j.core.appender.rolling.DirectFileRolloverStrategy;
032import org.apache.logging.log4j.core.appender.rolling.DirectWriteRolloverStrategy;
033import org.apache.logging.log4j.core.appender.rolling.RollingRandomAccessFileManager;
034import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
035import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
036import org.apache.logging.log4j.core.config.Configuration;
037import org.apache.logging.log4j.core.config.Property;
038import org.apache.logging.log4j.core.config.plugins.Plugin;
039import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
040import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
041import org.apache.logging.log4j.core.config.plugins.PluginElement;
042import org.apache.logging.log4j.core.net.Advertiser;
043import org.apache.logging.log4j.core.util.Booleans;
044import org.apache.logging.log4j.core.util.Integers;
045
046/**
047 * An appender that writes to random access files and can roll over at
048 * intervals.
049 */
050@Plugin(name = "RollingRandomAccessFile", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
051public final class RollingRandomAccessFileAppender extends AbstractOutputStreamAppender<RollingRandomAccessFileManager> {
052
053    public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B>
054            implements org.apache.logging.log4j.core.util.Builder<RollingRandomAccessFileAppender> {
055
056        public Builder() {
057            withBufferSize(RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE);
058            setIgnoreExceptions(true);
059            withImmediateFlush(true);
060        }
061
062        @PluginBuilderAttribute("fileName")
063        private String fileName;
064
065        @PluginBuilderAttribute("filePattern")
066        private String filePattern;
067
068        @PluginBuilderAttribute("append")
069        private boolean append = true;
070
071        @PluginElement("Policy")
072        private TriggeringPolicy policy;
073
074        @PluginElement("Strategy")
075        private RolloverStrategy strategy;
076
077        @PluginBuilderAttribute("advertise")
078        private boolean advertise;
079
080        @PluginBuilderAttribute("advertiseURI")
081        private String advertiseURI;
082
083        @PluginBuilderAttribute
084        private String filePermissions;
085
086        @PluginBuilderAttribute
087        private String fileOwner;
088
089        @PluginBuilderAttribute
090        private String fileGroup;
091
092        @Override
093        public RollingRandomAccessFileAppender build() {
094            final String name = getName();
095            if (name == null) {
096                LOGGER.error("No name provided for FileAppender");
097                return null;
098            }
099
100            if (strategy == null) {
101                if (fileName != null) {
102                    strategy = DefaultRolloverStrategy.newBuilder()
103                            .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION))
104                            .withConfig(getConfiguration())
105                            .build();
106                } else {
107                    strategy = DirectWriteRolloverStrategy.newBuilder()
108                            .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION))
109                            .withConfig(getConfiguration())
110                            .build();
111                }
112            } else if (fileName == null && !(strategy instanceof DirectFileRolloverStrategy)) {
113                LOGGER.error("RollingFileAppender '{}': When no file name is provided a DirectFileRolloverStrategy must be configured");
114                return null;
115            }
116
117            if (filePattern == null) {
118                LOGGER.error("No filename pattern provided for FileAppender with name " + name);
119                return null;
120            }
121
122            if (policy == null) {
123                LOGGER.error("A TriggeringPolicy must be provided");
124                return null;
125            }
126
127            final Layout<? extends Serializable> layout = getOrCreateLayout();
128
129            final boolean immediateFlush = isImmediateFlush();
130            final int bufferSize = getBufferSize();
131            final RollingRandomAccessFileManager manager = RollingRandomAccessFileManager
132                    .getRollingRandomAccessFileManager(fileName, filePattern, append, immediateFlush, bufferSize, policy,
133                            strategy, advertiseURI, layout,
134                            filePermissions, fileOwner, fileGroup, getConfiguration());
135            if (manager == null) {
136                return null;
137            }
138
139            manager.initialize();
140
141            return new RollingRandomAccessFileAppender(name, layout, getFilter(), manager, fileName, filePattern,
142                    isIgnoreExceptions(), immediateFlush, bufferSize,
143                    advertise ? getConfiguration().getAdvertiser() : null, getPropertyArray());
144        }
145
146        public B withFileName(final String fileName) {
147            this.fileName = fileName;
148            return asBuilder();
149        }
150
151        public B withFilePattern(final String filePattern) {
152            this.filePattern = filePattern;
153            return asBuilder();
154        }
155
156        public B withAppend(final boolean append) {
157            this.append = append;
158            return asBuilder();
159        }
160
161        public B withPolicy(final TriggeringPolicy policy) {
162            this.policy = policy;
163            return asBuilder();
164        }
165
166        public B withStrategy(final RolloverStrategy strategy) {
167            this.strategy = strategy;
168            return asBuilder();
169        }
170
171        public B withAdvertise(final boolean advertise) {
172            this.advertise = advertise;
173            return asBuilder();
174        }
175
176        public B withAdvertiseURI(final String advertiseURI) {
177            this.advertiseURI = advertiseURI;
178            return asBuilder();
179        }
180
181        public B withFilePermissions(final String filePermissions) {
182            this.filePermissions = filePermissions;
183            return asBuilder();
184        }
185
186        public B withFileOwner(final String fileOwner) {
187            this.fileOwner = fileOwner;
188            return asBuilder();
189        }
190
191        public B withFileGroup(final String fileGroup) {
192            this.fileGroup = fileGroup;
193            return asBuilder();
194        }
195
196    }
197
198    private final String fileName;
199    private final String filePattern;
200    private final Object advertisement;
201    private final Advertiser advertiser;
202
203    private RollingRandomAccessFileAppender(final String name, final Layout<? extends Serializable> layout,
204            final Filter filter, final RollingRandomAccessFileManager manager, final String fileName,
205            final String filePattern, final boolean ignoreExceptions, final boolean immediateFlush,
206            final int bufferSize, final Advertiser advertiser, final Property[] properties) {
207        super(name, layout, filter, ignoreExceptions, immediateFlush, properties, manager);
208        if (advertiser != null) {
209            final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
210            configuration.put("contentType", layout.getContentType());
211            configuration.put("name", name);
212            advertisement = advertiser.advertise(configuration);
213        } else {
214            advertisement = null;
215        }
216        this.fileName = fileName;
217        this.filePattern = filePattern;
218        this.advertiser = advertiser;
219    }
220
221    @Override
222    public boolean stop(final long timeout, final TimeUnit timeUnit) {
223        setStopping();
224        super.stop(timeout, timeUnit, false);
225        if (advertiser != null) {
226            advertiser.unadvertise(advertisement);
227        }
228        setStopped();
229        return true;
230    }
231
232    /**
233     * Write the log entry rolling over the file when required.
234     *
235     * @param event The LogEvent.
236     */
237    @Override
238    public void append(final LogEvent event) {
239        final RollingRandomAccessFileManager manager = getManager();
240        manager.checkRollover(event);
241
242        // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass
243        super.append(event);
244    }
245
246    /**
247     * Returns the File name for the Appender.
248     *
249     * @return The file name.
250     */
251    public String getFileName() {
252        return fileName;
253    }
254
255    /**
256     * Returns the file pattern used when rolling over.
257     *
258     * @return The file pattern.
259     */
260    public String getFilePattern() {
261        return filePattern;
262    }
263
264    /**
265     * Returns the size of the file manager's buffer.
266     * @return the buffer size
267     */
268    public int getBufferSize() {
269        return getManager().getBufferSize();
270    }
271
272    /**
273     * Create a RollingRandomAccessFileAppender.
274     *
275     * @param fileName The name of the file that is actively written to.
276     *            (required).
277     * @param filePattern The pattern of the file name to use on rollover.
278     *            (required).
279     * @param append If true, events are appended to the file. If false, the
280     *            file is overwritten when opened. Defaults to "true"
281     * @param name The name of the Appender (required).
282     * @param immediateFlush When true, events are immediately flushed. Defaults
283     *            to "true".
284     * @param bufferSizeStr The buffer size, defaults to {@value RollingRandomAccessFileManager#DEFAULT_BUFFER_SIZE}.
285     * @param policy The triggering policy. (required).
286     * @param strategy The rollover strategy. Defaults to
287     *            DefaultRolloverStrategy.
288     * @param layout The layout to use (defaults to the default PatternLayout).
289     * @param filter The Filter or null.
290     * @param ignoreExceptions If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
291     *               they are propagated to the caller.
292     * @param advertise "true" if the appender configuration should be
293     *            advertised, "false" otherwise.
294     * @param advertiseURI The advertised URI which can be used to retrieve the
295     *            file contents.
296     * @param configuration The Configuration.
297     * @return A RollingRandomAccessFileAppender.
298     * @deprecated Use {@link #newBuilder()}.
299     */
300    @Deprecated
301    public static <B extends Builder<B>> RollingRandomAccessFileAppender createAppender(
302            final String fileName,
303            final String filePattern,
304            final String append,
305            final String name,
306            final String immediateFlush,
307            final String bufferSizeStr,
308            final TriggeringPolicy policy,
309            final RolloverStrategy strategy,
310            final Layout<? extends Serializable> layout,
311            final Filter filter,
312            final String ignoreExceptions,
313            final String advertise,
314            final String advertiseURI,
315            final Configuration configuration) {
316
317        final boolean isAppend = Booleans.parseBoolean(append, true);
318        final boolean isIgnoreExceptions = Booleans.parseBoolean(ignoreExceptions, true);
319        final boolean isImmediateFlush = Booleans.parseBoolean(immediateFlush, true);
320        final boolean isAdvertise = Boolean.parseBoolean(advertise);
321        final int bufferSize = Integers.parseInt(bufferSizeStr, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE);
322
323        return RollingRandomAccessFileAppender.<B>newBuilder()
324           .withAdvertise(isAdvertise)
325           .withAdvertiseURI(advertiseURI)
326           .withAppend(isAppend)
327           .withBufferSize(bufferSize)
328           .setConfiguration(configuration)
329           .withFileName(fileName)
330           .withFilePattern(filePattern).setFilter(filter).setIgnoreExceptions(isIgnoreExceptions)
331           .withImmediateFlush(isImmediateFlush).setLayout(layout).setName(name)
332           .withPolicy(policy)
333           .withStrategy(strategy)
334           .build();
335    }
336
337    @PluginBuilderFactory
338    public static <B extends Builder<B>> B newBuilder() {
339        return new Builder<B>().asBuilder();
340    }
341
342}