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