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;
023
024import org.apache.logging.log4j.core.Appender;
025import org.apache.logging.log4j.core.Core;
026import org.apache.logging.log4j.core.Filter;
027import org.apache.logging.log4j.core.Layout;
028import org.apache.logging.log4j.core.config.Configuration;
029import org.apache.logging.log4j.core.config.plugins.Plugin;
030import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
031import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
032import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
033import org.apache.logging.log4j.core.net.Advertiser;
034import org.apache.logging.log4j.core.util.Booleans;
035import org.apache.logging.log4j.core.util.Integers;
036
037/**
038 * File Appender.
039 */
040@Plugin(name = FileAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
041public final class FileAppender extends AbstractOutputStreamAppender<FileManager> {
042
043    public static final String PLUGIN_NAME = "File";
044
045    /**
046     * Builds FileAppender instances.
047     * 
048     * @param <B>
049     *            The type to build
050     */
051    public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B>
052            implements org.apache.logging.log4j.core.util.Builder<FileAppender> {
053
054        @PluginBuilderAttribute
055        @Required
056        private String fileName;
057
058        @PluginBuilderAttribute
059        private boolean append = true;
060
061        @PluginBuilderAttribute
062        private boolean locking;
063
064        @PluginBuilderAttribute
065        private boolean advertise;
066
067        @PluginBuilderAttribute
068        private String advertiseUri;
069
070        @PluginBuilderAttribute
071        private boolean createOnDemand;
072
073        @PluginBuilderAttribute
074        private String filePermissions;
075
076        @PluginBuilderAttribute
077        private String fileOwner;
078
079        @PluginBuilderAttribute
080        private String fileGroup;
081
082        @Override
083        public FileAppender build() {
084            boolean bufferedIo = isBufferedIo();
085            final int bufferSize = getBufferSize();
086            if (locking && bufferedIo) {
087                LOGGER.warn("Locking and buffering are mutually exclusive. No buffering will occur for {}", fileName);
088                bufferedIo = false;
089            }
090            if (!bufferedIo && bufferSize > 0) {
091                LOGGER.warn("The bufferSize is set to {} but bufferedIo is false: {}", bufferSize, bufferedIo);
092            }
093            final Layout<? extends Serializable> layout = getOrCreateLayout();
094
095            final FileManager manager = FileManager.getFileManager(fileName, append, locking, bufferedIo, createOnDemand,
096                    advertiseUri, layout, bufferSize, filePermissions, fileOwner, fileGroup, getConfiguration());
097            if (manager == null) {
098                return null;
099            }
100
101            return new FileAppender(getName(), layout, getFilter(), manager, fileName, isIgnoreExceptions(),
102                    !bufferedIo || isImmediateFlush(), advertise ? getConfiguration().getAdvertiser() : null);
103        }
104
105        public String getAdvertiseUri() {
106            return advertiseUri;
107        }
108
109        public String getFileName() {
110            return fileName;
111        }
112
113        public boolean isAdvertise() {
114            return advertise;
115        }
116
117        public boolean isAppend() {
118            return append;
119        }
120
121        public boolean isCreateOnDemand() {
122            return createOnDemand;
123        }
124
125        public boolean isLocking() {
126            return locking;
127        }
128
129        public String getFilePermissions() {
130            return filePermissions;
131        }
132
133        public String getFileOwner() {
134            return fileOwner;
135        }
136
137        public String getFileGroup() {
138            return fileGroup;
139        }
140
141        public B withAdvertise(final boolean advertise) {
142            this.advertise = advertise;
143            return asBuilder();
144        }
145
146        public B withAdvertiseUri(final String advertiseUri) {
147            this.advertiseUri = advertiseUri;
148            return asBuilder();
149        }
150
151        public B withAppend(final boolean append) {
152            this.append = append;
153            return asBuilder();
154        }
155
156        public B withFileName(final String fileName) {
157            this.fileName = fileName;
158            return asBuilder();
159        }
160
161        public B withCreateOnDemand(final boolean createOnDemand) {
162            this.createOnDemand = createOnDemand;
163            return asBuilder();
164        }
165
166        public B withLocking(final boolean locking) {
167            this.locking = locking;
168            return asBuilder();
169        }
170
171        public B withFilePermissions(final String filePermissions) {
172            this.filePermissions = filePermissions;
173            return asBuilder();
174        }
175
176        public B withFileOwner(final String fileOwner) {
177            this.fileOwner = fileOwner;
178            return asBuilder();
179        }
180
181        public B withFileGroup(final String fileGroup) {
182            this.fileGroup = fileGroup;
183            return asBuilder();
184        }
185
186    }
187    
188    private static final int DEFAULT_BUFFER_SIZE = 8192;
189    
190    /**
191     * Create a File Appender.
192     * @param fileName The name and path of the file.
193     * @param append "True" if the file should be appended to, "false" if it should be overwritten.
194     * The default is "true".
195     * @param locking "True" if the file should be locked. The default is "false".
196     * @param name The name of the Appender.
197     * @param immediateFlush "true" if the contents should be flushed on every write, "false" otherwise. The default
198     * is "true".
199     * @param ignoreExceptions If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
200     *               they are propagated to the caller.
201     * @param bufferedIo "true" if I/O should be buffered, "false" otherwise. The default is "true".
202     * @param bufferSizeStr buffer size for buffered IO (default is 8192).
203     * @param layout The layout to use to format the event. If no layout is provided the default PatternLayout
204     * will be used.
205     * @param filter The filter, if any, to use.
206     * @param advertise "true" if the appender configuration should be advertised, "false" otherwise.
207     * @param advertiseUri The advertised URI which can be used to retrieve the file contents.
208     * @param config The Configuration
209     * @return The FileAppender.
210     * @deprecated Use {@link #newBuilder()}
211     */
212    @Deprecated
213    public static <B extends Builder<B>> FileAppender createAppender(
214            // @formatter:off
215            final String fileName,
216            final String append,
217            final String locking,
218            final String name,
219            final String immediateFlush,
220            final String ignoreExceptions,
221            final String bufferedIo,
222            final String bufferSizeStr,
223            final Layout<? extends Serializable> layout,
224            final Filter filter,
225            final String advertise,
226            final String advertiseUri,
227            final Configuration config) {
228        return FileAppender.<B>newBuilder()
229            .withAdvertise(Boolean.parseBoolean(advertise))
230            .withAdvertiseUri(advertiseUri)
231            .withAppend(Booleans.parseBoolean(append, true))
232            .withBufferedIo(Booleans.parseBoolean(bufferedIo, true))
233            .withBufferSize(Integers.parseInt(bufferSizeStr, DEFAULT_BUFFER_SIZE))
234            .setConfiguration(config)
235            .withFileName(fileName)
236            .withFilter(filter)
237            .withIgnoreExceptions(Booleans.parseBoolean(ignoreExceptions, true))
238            .withImmediateFlush(Booleans.parseBoolean(immediateFlush, true))
239            .withLayout(layout)
240            .withLocking(Boolean.parseBoolean(locking))
241            .withName(name)
242            .build();
243        // @formatter:on
244    }
245    
246    @PluginBuilderFactory
247    public static <B extends Builder<B>> B newBuilder() {
248        return new Builder<B>().asBuilder();
249    }
250    
251    private final String fileName;
252
253    private final Advertiser advertiser;
254
255    private final Object advertisement;
256
257    private FileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
258            final FileManager manager, final String filename, final boolean ignoreExceptions,
259            final boolean immediateFlush, final Advertiser advertiser) {
260
261        super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
262        if (advertiser != null) {
263            final Map<String, String> configuration = new HashMap<>(layout.getContentFormat());
264            configuration.putAll(manager.getContentFormat());
265            configuration.put("contentType", layout.getContentType());
266            configuration.put("name", name);
267            advertisement = advertiser.advertise(configuration);
268        } else {
269            advertisement = null;
270        }
271        this.fileName = filename;
272        this.advertiser = advertiser;
273    }
274
275    /**
276     * Returns the file name this appender is associated with.
277     * @return The File name.
278     */
279    public String getFileName() {
280        return this.fileName;
281    }
282
283    @Override
284    public boolean stop(final long timeout, final TimeUnit timeUnit) {
285        setStopping();
286        super.stop(timeout, timeUnit, false);
287        if (advertiser != null) {
288            advertiser.unadvertise(advertisement);
289        }
290        setStopped();
291        return true;
292    }
293}