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