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 withBufferSize(RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE); 058 setIgnoreExceptions(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 DirectFileRolloverStrategy 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, 143 advertise ? getConfiguration().getAdvertiser() : null, getPropertyArray()); 144 } 145 146 public B withFileName(final String fileName) { 147 this.fileName = fileName; 148 return asBuilder(); 149 } 150 151 public B withFilePattern(final String filePattern) { 152 this.filePattern = filePattern; 153 return asBuilder(); 154 } 155 156 public B withAppend(final boolean append) { 157 this.append = append; 158 return asBuilder(); 159 } 160 161 public B withPolicy(final TriggeringPolicy policy) { 162 this.policy = policy; 163 return asBuilder(); 164 } 165 166 public B withStrategy(final RolloverStrategy strategy) { 167 this.strategy = strategy; 168 return asBuilder(); 169 } 170 171 public B withAdvertise(final boolean advertise) { 172 this.advertise = advertise; 173 return asBuilder(); 174 } 175 176 public B withAdvertiseURI(final String advertiseURI) { 177 this.advertiseURI = advertiseURI; 178 return asBuilder(); 179 } 180 181 public B withFilePermissions(final String filePermissions) { 182 this.filePermissions = filePermissions; 183 return asBuilder(); 184 } 185 186 public B withFileOwner(final String fileOwner) { 187 this.fileOwner = fileOwner; 188 return asBuilder(); 189 } 190 191 public B withFileGroup(final String fileGroup) { 192 this.fileGroup = fileGroup; 193 return asBuilder(); 194 } 195 196 } 197 198 private final String fileName; 199 private final String filePattern; 200 private final Object advertisement; 201 private final Advertiser advertiser; 202 203 private RollingRandomAccessFileAppender(final String name, final Layout<? extends Serializable> layout, 204 final Filter filter, final RollingRandomAccessFileManager manager, final String fileName, 205 final String filePattern, final boolean ignoreExceptions, final boolean immediateFlush, 206 final int bufferSize, final Advertiser advertiser, final Property[] properties) { 207 super(name, layout, filter, ignoreExceptions, immediateFlush, properties, manager); 208 if (advertiser != null) { 209 final Map<String, String> configuration = new HashMap<>(layout.getContentFormat()); 210 configuration.put("contentType", layout.getContentType()); 211 configuration.put("name", name); 212 advertisement = advertiser.advertise(configuration); 213 } else { 214 advertisement = null; 215 } 216 this.fileName = fileName; 217 this.filePattern = filePattern; 218 this.advertiser = advertiser; 219 } 220 221 @Override 222 public boolean stop(final long timeout, final TimeUnit timeUnit) { 223 setStopping(); 224 super.stop(timeout, timeUnit, false); 225 if (advertiser != null) { 226 advertiser.unadvertise(advertisement); 227 } 228 setStopped(); 229 return true; 230 } 231 232 /** 233 * Write the log entry rolling over the file when required. 234 * 235 * @param event The LogEvent. 236 */ 237 @Override 238 public void append(final LogEvent event) { 239 final RollingRandomAccessFileManager manager = getManager(); 240 manager.checkRollover(event); 241 242 // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass 243 super.append(event); 244 } 245 246 /** 247 * Returns the File name for the Appender. 248 * 249 * @return The file name. 250 */ 251 public String getFileName() { 252 return fileName; 253 } 254 255 /** 256 * Returns the file pattern used when rolling over. 257 * 258 * @return The file pattern. 259 */ 260 public String getFilePattern() { 261 return filePattern; 262 } 263 264 /** 265 * Returns the size of the file manager's buffer. 266 * @return the buffer size 267 */ 268 public int getBufferSize() { 269 return getManager().getBufferSize(); 270 } 271 272 /** 273 * Create a RollingRandomAccessFileAppender. 274 * 275 * @param fileName The name of the file that is actively written to. 276 * (required). 277 * @param filePattern The pattern of the file name to use on rollover. 278 * (required). 279 * @param append If true, events are appended to the file. If false, the 280 * file is overwritten when opened. Defaults to "true" 281 * @param name The name of the Appender (required). 282 * @param immediateFlush When true, events are immediately flushed. Defaults 283 * to "true". 284 * @param bufferSizeStr The buffer size, defaults to {@value RollingRandomAccessFileManager#DEFAULT_BUFFER_SIZE}. 285 * @param policy The triggering policy. (required). 286 * @param strategy The rollover strategy. Defaults to 287 * DefaultRolloverStrategy. 288 * @param layout The layout to use (defaults to the default PatternLayout). 289 * @param filter The Filter or null. 290 * @param ignoreExceptions If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise 291 * they are propagated to the caller. 292 * @param advertise "true" if the appender configuration should be 293 * advertised, "false" otherwise. 294 * @param advertiseURI The advertised URI which can be used to retrieve the 295 * file contents. 296 * @param configuration The Configuration. 297 * @return A RollingRandomAccessFileAppender. 298 * @deprecated Use {@link #newBuilder()}. 299 */ 300 @Deprecated 301 public static <B extends Builder<B>> RollingRandomAccessFileAppender createAppender( 302 final String fileName, 303 final String filePattern, 304 final String append, 305 final String name, 306 final String immediateFlush, 307 final String bufferSizeStr, 308 final TriggeringPolicy policy, 309 final RolloverStrategy strategy, 310 final Layout<? extends Serializable> layout, 311 final Filter filter, 312 final String ignoreExceptions, 313 final String advertise, 314 final String advertiseURI, 315 final Configuration configuration) { 316 317 final boolean isAppend = Booleans.parseBoolean(append, true); 318 final boolean isIgnoreExceptions = Booleans.parseBoolean(ignoreExceptions, true); 319 final boolean isImmediateFlush = Booleans.parseBoolean(immediateFlush, true); 320 final boolean isAdvertise = Boolean.parseBoolean(advertise); 321 final int bufferSize = Integers.parseInt(bufferSizeStr, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE); 322 323 return RollingRandomAccessFileAppender.<B>newBuilder() 324 .withAdvertise(isAdvertise) 325 .withAdvertiseURI(advertiseURI) 326 .withAppend(isAppend) 327 .withBufferSize(bufferSize) 328 .setConfiguration(configuration) 329 .withFileName(fileName) 330 .withFilePattern(filePattern).setFilter(filter).setIgnoreExceptions(isIgnoreExceptions) 331 .withImmediateFlush(isImmediateFlush).setLayout(layout).setName(name) 332 .withPolicy(policy) 333 .withStrategy(strategy) 334 .build(); 335 } 336 337 @PluginBuilderFactory 338 public static <B extends Builder<B>> B newBuilder() { 339 return new Builder<B>().asBuilder(); 340 } 341 342}