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.RollingFileManager; 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.config.plugins.validation.constraints.Required; 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 files and can roll over at intervals. 048 */ 049@Plugin(name = RollingFileAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) 050public final class RollingFileAppender extends AbstractOutputStreamAppender<RollingFileManager> { 051 052 public static final String PLUGIN_NAME = "RollingFile"; 053 054 /** 055 * Builds FileAppender instances. 056 * 057 * @param <B> 058 * The type to build 059 * @since 2.7 060 */ 061 public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B> 062 implements org.apache.logging.log4j.core.util.Builder<RollingFileAppender> { 063 064 @PluginBuilderAttribute 065 private String fileName; 066 067 @PluginBuilderAttribute 068 @Required 069 private String filePattern; 070 071 @PluginBuilderAttribute 072 private boolean append = true; 073 074 @PluginBuilderAttribute 075 private boolean locking; 076 077 @PluginElement("Policy") 078 @Required 079 private TriggeringPolicy policy; 080 081 @PluginElement("Strategy") 082 private RolloverStrategy strategy; 083 084 @PluginBuilderAttribute 085 private boolean advertise; 086 087 @PluginBuilderAttribute 088 private String advertiseUri; 089 090 @PluginBuilderAttribute 091 private boolean createOnDemand; 092 093 @PluginBuilderAttribute 094 private String filePermissions; 095 096 @PluginBuilderAttribute 097 private String fileOwner; 098 099 @PluginBuilderAttribute 100 private String fileGroup; 101 102 @Override 103 public RollingFileAppender build() { 104 // Even though some variables may be annotated with @Required, we must still perform validation here for 105 // call sites that build builders programmatically. 106 final boolean isBufferedIo = isBufferedIo(); 107 final int bufferSize = getBufferSize(); 108 if (getName() == null) { 109 LOGGER.error("RollingFileAppender '{}': No name provided.", getName()); 110 return null; 111 } 112 113 if (!isBufferedIo && bufferSize > 0) { 114 LOGGER.warn("RollingFileAppender '{}': The bufferSize is set to {} but bufferedIO is not true", getName(), bufferSize); 115 } 116 117 if (filePattern == null) { 118 LOGGER.error("RollingFileAppender '{}': No file name pattern provided.", getName()); 119 return null; 120 } 121 122 if (policy == null) { 123 LOGGER.error("RollingFileAppender '{}': No TriggeringPolicy provided.", getName()); 124 return null; 125 } 126 127 if (strategy == null) { 128 if (fileName != null) { 129 strategy = DefaultRolloverStrategy.newBuilder() 130 .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION)) 131 .withConfig(getConfiguration()) 132 .build(); 133 } else { 134 strategy = DirectWriteRolloverStrategy.newBuilder() 135 .withCompressionLevelStr(String.valueOf(Deflater.DEFAULT_COMPRESSION)) 136 .withConfig(getConfiguration()) 137 .build(); 138 } 139 } else if (fileName == null && !(strategy instanceof DirectFileRolloverStrategy)) { 140 LOGGER.error("RollingFileAppender '{}': When no file name is provided a DirectFilenameRolloverStrategy must be configured"); 141 return null; 142 } 143 144 final Layout<? extends Serializable> layout = getOrCreateLayout(); 145 final RollingFileManager manager = RollingFileManager.getFileManager(fileName, filePattern, append, 146 isBufferedIo, policy, strategy, advertiseUri, layout, bufferSize, isImmediateFlush(), 147 createOnDemand, filePermissions, fileOwner, fileGroup, getConfiguration()); 148 if (manager == null) { 149 return null; 150 } 151 152 manager.initialize(); 153 154 return new RollingFileAppender(getName(), layout, getFilter(), manager, fileName, filePattern, 155 isIgnoreExceptions(), isImmediateFlush(), advertise ? getConfiguration().getAdvertiser() : null); 156 } 157 158 public String getAdvertiseUri() { 159 return advertiseUri; 160 } 161 162 public String getFileName() { 163 return fileName; 164 } 165 166 public boolean isAdvertise() { 167 return advertise; 168 } 169 170 public boolean isAppend() { 171 return append; 172 } 173 174 public boolean isCreateOnDemand() { 175 return createOnDemand; 176 } 177 178 public boolean isLocking() { 179 return locking; 180 } 181 182 public String getFilePermissions() { 183 return filePermissions; 184 } 185 186 public String getFileOwner() { 187 return fileOwner; 188 } 189 190 public String getFileGroup() { 191 return fileGroup; 192 } 193 194 public B withAdvertise(final boolean advertise) { 195 this.advertise = advertise; 196 return asBuilder(); 197 } 198 199 public B withAdvertiseUri(final String advertiseUri) { 200 this.advertiseUri = advertiseUri; 201 return asBuilder(); 202 } 203 204 public B withAppend(final boolean append) { 205 this.append = append; 206 return asBuilder(); 207 } 208 209 public B withFileName(final String fileName) { 210 this.fileName = fileName; 211 return asBuilder(); 212 } 213 214 public B withCreateOnDemand(final boolean createOnDemand) { 215 this.createOnDemand = createOnDemand; 216 return asBuilder(); 217 } 218 219 public B withLocking(final boolean locking) { 220 this.locking = locking; 221 return asBuilder(); 222 } 223 224 public String getFilePattern() { 225 return filePattern; 226 } 227 228 public TriggeringPolicy getPolicy() { 229 return policy; 230 } 231 232 public RolloverStrategy getStrategy() { 233 return strategy; 234 } 235 236 public B withFilePattern(final String filePattern) { 237 this.filePattern = filePattern; 238 return asBuilder(); 239 } 240 241 public B withPolicy(final TriggeringPolicy policy) { 242 this.policy = policy; 243 return asBuilder(); 244 } 245 246 public B withStrategy(final RolloverStrategy strategy) { 247 this.strategy = strategy; 248 return asBuilder(); 249 } 250 251 public B withFilePermissions(final String filePermissions) { 252 this.filePermissions = filePermissions; 253 return asBuilder(); 254 } 255 256 public B withFileOwner(final String fileOwner) { 257 this.fileOwner = fileOwner; 258 return asBuilder(); 259 } 260 261 public B withFileGroup(final String fileGroup) { 262 this.fileGroup = fileGroup; 263 return asBuilder(); 264 } 265 266 } 267 268 private static final int DEFAULT_BUFFER_SIZE = 8192; 269 270 private final String fileName; 271 private final String filePattern; 272 private Object advertisement; 273 private final Advertiser advertiser; 274 275 private RollingFileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, 276 final RollingFileManager manager, final String fileName, final String filePattern, 277 final boolean ignoreExceptions, final boolean immediateFlush, final Advertiser advertiser) { 278 super(name, layout, filter, ignoreExceptions, immediateFlush, manager); 279 if (advertiser != null) { 280 final Map<String, String> configuration = new HashMap<>(layout.getContentFormat()); 281 configuration.put("contentType", layout.getContentType()); 282 configuration.put("name", name); 283 advertisement = advertiser.advertise(configuration); 284 } 285 this.fileName = fileName; 286 this.filePattern = filePattern; 287 this.advertiser = advertiser; 288 } 289 290 @Override 291 public boolean stop(final long timeout, final TimeUnit timeUnit) { 292 setStopping(); 293 final boolean stopped = super.stop(timeout, timeUnit, false); 294 if (advertiser != null) { 295 advertiser.unadvertise(advertisement); 296 } 297 setStopped(); 298 return stopped; 299 } 300 301 /** 302 * Writes the log entry rolling over the file when required. 303 304 * @param event The LogEvent. 305 */ 306 @Override 307 public void append(final LogEvent event) { 308 getManager().checkRollover(event); 309 super.append(event); 310 } 311 312 /** 313 * Returns the File name for the Appender. 314 * @return The file name. 315 */ 316 public String getFileName() { 317 return fileName; 318 } 319 320 /** 321 * Returns the file pattern used when rolling over. 322 * @return The file pattern. 323 */ 324 public String getFilePattern() { 325 return filePattern; 326 } 327 328 /** 329 * Returns the triggering policy. 330 * @param <T> TriggeringPolicy type 331 * @return The TriggeringPolicy 332 */ 333 public <T extends TriggeringPolicy> T getTriggeringPolicy() { 334 return getManager().getTriggeringPolicy(); 335 } 336 337 /** 338 * Creates a RollingFileAppender. 339 * @param fileName The name of the file that is actively written to. (required). 340 * @param filePattern The pattern of the file name to use on rollover. (required). 341 * @param append If true, events are appended to the file. If false, the file 342 * is overwritten when opened. Defaults to "true" 343 * @param name The name of the Appender (required). 344 * @param bufferedIO When true, I/O will be buffered. Defaults to "true". 345 * @param bufferSizeStr buffer size for buffered IO (default is 8192). 346 * @param immediateFlush When true, events are immediately flushed. Defaults to "true". 347 * @param policy The triggering policy. (required). 348 * @param strategy The rollover strategy. Defaults to DefaultRolloverStrategy. 349 * @param layout The layout to use (defaults to the default PatternLayout). 350 * @param filter The Filter or null. 351 * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise 352 * they are propagated to the caller. 353 * @param advertise "true" if the appender configuration should be advertised, "false" otherwise. 354 * @param advertiseUri The advertised URI which can be used to retrieve the file contents. 355 * @param config The Configuration. 356 * @return A RollingFileAppender. 357 * @deprecated Use {@link #newBuilder()}. 358 */ 359 @Deprecated 360 public static <B extends Builder<B>> RollingFileAppender createAppender( 361 // @formatter:off 362 final String fileName, 363 final String filePattern, 364 final String append, 365 final String name, 366 final String bufferedIO, 367 final String bufferSizeStr, 368 final String immediateFlush, 369 final TriggeringPolicy policy, 370 final RolloverStrategy strategy, 371 final Layout<? extends Serializable> layout, 372 final Filter filter, 373 final String ignore, 374 final String advertise, 375 final String advertiseUri, 376 final Configuration config) { 377 // @formatter:on 378 final int bufferSize = Integers.parseInt(bufferSizeStr, DEFAULT_BUFFER_SIZE); 379 // @formatter:off 380 return RollingFileAppender.<B>newBuilder() 381 .withAdvertise(Boolean.parseBoolean(advertise)) 382 .withAdvertiseUri(advertiseUri) 383 .withAppend(Booleans.parseBoolean(append, true)) 384 .withBufferedIo(Booleans.parseBoolean(bufferedIO, true)) 385 .withBufferSize(bufferSize) 386 .setConfiguration(config) 387 .withFileName(fileName) 388 .withFilePattern(filePattern) 389 .withFilter(filter) 390 .withIgnoreExceptions(Booleans.parseBoolean(ignore, true)) 391 .withImmediateFlush(Booleans.parseBoolean(immediateFlush, true)) 392 .withLayout(layout) 393 .withCreateOnDemand(false) 394 .withLocking(false) 395 .withName(name) 396 .withPolicy(policy) 397 .withStrategy(strategy) 398 .build(); 399 // @formatter:on 400 } 401 402 /** 403 * Creates a new Builder. 404 * 405 * @return a new Builder. 406 * @since 2.7 407 */ 408 @PluginBuilderFactory 409 public static <B extends Builder<B>> B newBuilder() { 410 return new Builder<B>().asBuilder(); 411 } 412}