View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.appender;
18  
19  import java.io.Serializable;
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.concurrent.TimeUnit;
23  
24  import org.apache.logging.log4j.core.Appender;
25  import org.apache.logging.log4j.core.Core;
26  import org.apache.logging.log4j.core.Filter;
27  import org.apache.logging.log4j.core.Layout;
28  import org.apache.logging.log4j.core.LogEvent;
29  import org.apache.logging.log4j.core.config.Configuration;
30  import org.apache.logging.log4j.core.config.Property;
31  import org.apache.logging.log4j.core.config.plugins.Plugin;
32  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
33  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
34  import org.apache.logging.log4j.core.net.Advertiser;
35  import org.apache.logging.log4j.core.util.Booleans;
36  import org.apache.logging.log4j.core.util.Integers;
37  
38  /**
39   * File Appender.
40   */
41  @Plugin(name = "RandomAccessFile", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
42  public final class RandomAccessFileAppender extends AbstractOutputStreamAppender<RandomAccessFileManager> {
43  
44      /**
45       * Builds RandomAccessFileAppender instances.
46       *
47       * @param <B>
48       *            The type to build
49       */
50      public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B>
51              implements org.apache.logging.log4j.core.util.Builder<RandomAccessFileAppender> {
52  
53          @PluginBuilderAttribute("fileName")
54          private String fileName;
55  
56          @PluginBuilderAttribute("append")
57          private boolean append = true;
58  
59          @PluginBuilderAttribute("advertise")
60          private boolean advertise;
61  
62          @PluginBuilderAttribute("advertiseURI")
63          private String advertiseURI;
64  
65          @Override
66          public RandomAccessFileAppender build() {
67              final String name = getName();
68              if (name == null) {
69                  LOGGER.error("No name provided for FileAppender");
70                  return null;
71              }
72  
73              if (fileName == null) {
74                  LOGGER.error("No filename provided for FileAppender with name " + name);
75                  return null;
76              }
77              final Layout<? extends Serializable> layout = getOrCreateLayout();
78              final boolean immediateFlush = isImmediateFlush();
79              final RandomAccessFileManager manager = RandomAccessFileManager.getFileManager(fileName, append,
80                      immediateFlush, getBufferSize(), advertiseURI, layout, null);
81              if (manager == null) {
82                  return null;
83              }
84  
85              return new RandomAccessFileAppender(name, layout, getFilter(), manager, fileName, isIgnoreExceptions(),
86                      immediateFlush, advertise ? getConfiguration().getAdvertiser() : null, getPropertyArray());
87          }
88  
89          public B setFileName(final String fileName) {
90              this.fileName = fileName;
91              return asBuilder();
92          }
93  
94          public B setAppend(final boolean append) {
95              this.append = append;
96              return asBuilder();
97          }
98  
99          public B setAdvertise(final boolean advertise) {
100             this.advertise = advertise;
101             return asBuilder();
102         }
103 
104         public B setAdvertiseURI(final String advertiseURI) {
105             this.advertiseURI = advertiseURI;
106             return asBuilder();
107         }
108 
109     }
110 
111     private final String fileName;
112     private Object advertisement;
113     private final Advertiser advertiser;
114 
115     private RandomAccessFileAppender(final String name, final Layout<? extends Serializable> layout,
116             final Filter filter, final RandomAccessFileManager manager, final String filename,
117             final boolean ignoreExceptions, final boolean immediateFlush, final Advertiser advertiser,
118             final Property[] properties) {
119 
120         super(name, layout, filter, ignoreExceptions, immediateFlush, properties, manager);
121         if (advertiser != null) {
122             final Map<String, String> configuration = new HashMap<>(
123                     layout.getContentFormat());
124             configuration.putAll(manager.getContentFormat());
125             configuration.put("contentType", layout.getContentType());
126             configuration.put("name", name);
127             advertisement = advertiser.advertise(configuration);
128         }
129         this.fileName = filename;
130         this.advertiser = advertiser;
131     }
132 
133     @Override
134     public boolean stop(final long timeout, final TimeUnit timeUnit) {
135         setStopping();
136         super.stop(timeout, timeUnit, false);
137         if (advertiser != null) {
138             advertiser.unadvertise(advertisement);
139         }
140         setStopped();
141         return true;
142     }
143 
144     /**
145      * Write the log entry rolling over the file when required.
146      *
147      * @param event The LogEvent.
148      */
149     @Override
150     public void append(final LogEvent event) {
151 
152         // Leverage the nice batching behaviour of async Loggers/Appenders:
153         // we can signal the file manager that it needs to flush the buffer
154         // to disk at the end of a batch.
155         // From a user's point of view, this means that all log events are
156         // _always_ available in the log file, without incurring the overhead
157         // of immediateFlush=true.
158         getManager().setEndOfBatch(event.isEndOfBatch()); // FIXME manager's EndOfBatch threadlocal can be deleted
159 
160         // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass
161         super.append(event);
162     }
163 
164     /**
165      * Returns the file name this appender is associated with.
166      *
167      * @return The File name.
168      */
169     public String getFileName() {
170         return this.fileName;
171     }
172 
173     /**
174      * Returns the size of the file manager's buffer.
175      * @return the buffer size
176      */
177     public int getBufferSize() {
178         return getManager().getBufferSize();
179     }
180 
181     // difference from standard File Appender:
182     // locking is not supported and buffering cannot be switched off
183     /**
184      * Create a File Appender.
185      *
186      * @param fileName The name and path of the file.
187      * @param append "True" if the file should be appended to, "false" if it
188      *            should be overwritten. The default is "true".
189      * @param name The name of the Appender.
190      * @param immediateFlush "true" if the contents should be flushed on every
191      *            write, "false" otherwise. The default is "true".
192      * @param bufferSizeStr The buffer size, defaults to {@value RandomAccessFileManager#DEFAULT_BUFFER_SIZE}.
193      * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
194      *               they are propagated to the caller.
195      * @param layout The layout to use to format the event. If no layout is
196      *            provided the default PatternLayout will be used.
197      * @param filter The filter, if any, to use.
198      * @param advertise "true" if the appender configuration should be
199      *            advertised, "false" otherwise.
200      * @param advertiseURI The advertised URI which can be used to retrieve the
201      *            file contents.
202      * @param configuration The Configuration.
203      * @return The FileAppender.
204      * @deprecated Use {@link #newBuilder()}.
205      */
206     @Deprecated
207     public static <B extends Builder<B>> RandomAccessFileAppender createAppender(
208             final String fileName,
209             final String append,
210             final String name,
211             final String immediateFlush,
212             final String bufferSizeStr,
213             final String ignore,
214             final Layout<? extends Serializable> layout,
215             final Filter filter,
216             final String advertise,
217             final String advertiseURI,
218             final Configuration configuration) {
219 
220         final boolean isAppend = Booleans.parseBoolean(append, true);
221         final boolean isFlush = Booleans.parseBoolean(immediateFlush, true);
222         final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
223         final boolean isAdvertise = Boolean.parseBoolean(advertise);
224         final int bufferSize = Integers.parseInt(bufferSizeStr, RandomAccessFileManager.DEFAULT_BUFFER_SIZE);
225 
226         return RandomAccessFileAppender.<B>newBuilder()
227         .setAdvertise(isAdvertise)
228         .setAdvertiseURI(advertiseURI)
229         .setAppend(isAppend)
230         .withBufferSize(bufferSize)
231         .setConfiguration(configuration)
232         .setFileName(fileName).setFilter(filter).setIgnoreExceptions(ignoreExceptions)
233             .withImmediateFlush(isFlush).setLayout(layout).setName(name)
234             .build();
235     }
236 
237     /**
238      * Creates a builder for a RandomAccessFileAppender.
239      * @return a builder for a RandomAccessFileAppender.
240      */
241     @PluginBuilderFactory
242     public static <B extends Builder<B>> B newBuilder() {
243         return new Builder<B>().asBuilder();
244     }
245 
246 }