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