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.LogEvent; 029import org.apache.logging.log4j.core.config.Configuration; 030import org.apache.logging.log4j.core.config.Property; 031import org.apache.logging.log4j.core.config.plugins.Plugin; 032import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 033import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 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 = "RandomAccessFile", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) 042public final class RandomAccessFileAppender extends AbstractOutputStreamAppender<RandomAccessFileManager> { 043 044 /** 045 * Builds RandomAccessFileAppender instances. 046 * 047 * @param <B> 048 * The type to build 049 */ 050 public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B> 051 implements org.apache.logging.log4j.core.util.Builder<RandomAccessFileAppender> { 052 053 @PluginBuilderAttribute("fileName") 054 private String fileName; 055 056 @PluginBuilderAttribute("append") 057 private boolean append = true; 058 059 @PluginBuilderAttribute("advertise") 060 private boolean advertise; 061 062 @PluginBuilderAttribute("advertiseURI") 063 private String advertiseURI; 064 065 @Override 066 public RandomAccessFileAppender build() { 067 final String name = getName(); 068 if (name == null) { 069 LOGGER.error("No name provided for FileAppender"); 070 return null; 071 } 072 073 if (fileName == null) { 074 LOGGER.error("No filename provided for FileAppender with name " + name); 075 return null; 076 } 077 final Layout<? extends Serializable> layout = getOrCreateLayout(); 078 final boolean immediateFlush = isImmediateFlush(); 079 final RandomAccessFileManager manager = RandomAccessFileManager.getFileManager(fileName, append, 080 immediateFlush, getBufferSize(), advertiseURI, layout, null); 081 if (manager == null) { 082 return null; 083 } 084 085 return new RandomAccessFileAppender(name, layout, getFilter(), manager, fileName, isIgnoreExceptions(), 086 immediateFlush, advertise ? getConfiguration().getAdvertiser() : null, getPropertyArray()); 087 } 088 089 public B setFileName(final String fileName) { 090 this.fileName = fileName; 091 return asBuilder(); 092 } 093 094 public B setAppend(final boolean append) { 095 this.append = append; 096 return asBuilder(); 097 } 098 099 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}