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