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