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; 023 024import org.apache.logging.log4j.core.Appender; 025import org.apache.logging.log4j.core.Core; 026import org.apache.logging.log4j.core.Filter; 027import org.apache.logging.log4j.core.Layout; 028import org.apache.logging.log4j.core.config.Configuration; 029import org.apache.logging.log4j.core.config.plugins.Plugin; 030import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 031import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 032import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; 033import org.apache.logging.log4j.core.net.Advertiser; 034import org.apache.logging.log4j.core.util.Booleans; 035import org.apache.logging.log4j.core.util.Integers; 036 037/** 038 * File Appender. 039 */ 040@Plugin(name = FileAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) 041public final class FileAppender extends AbstractOutputStreamAppender<FileManager> { 042 043 public static final String PLUGIN_NAME = "File"; 044 045 /** 046 * Builds FileAppender instances. 047 * 048 * @param <B> 049 * The type to build 050 */ 051 public static class Builder<B extends Builder<B>> extends AbstractOutputStreamAppender.Builder<B> 052 implements org.apache.logging.log4j.core.util.Builder<FileAppender> { 053 054 @PluginBuilderAttribute 055 @Required 056 private String fileName; 057 058 @PluginBuilderAttribute 059 private boolean append = true; 060 061 @PluginBuilderAttribute 062 private boolean locking; 063 064 @PluginBuilderAttribute 065 private boolean advertise; 066 067 @PluginBuilderAttribute 068 private String advertiseUri; 069 070 @PluginBuilderAttribute 071 private boolean createOnDemand; 072 073 @PluginBuilderAttribute 074 private String filePermissions; 075 076 @PluginBuilderAttribute 077 private String fileOwner; 078 079 @PluginBuilderAttribute 080 private String fileGroup; 081 082 @Override 083 public FileAppender build() { 084 boolean bufferedIo = isBufferedIo(); 085 final int bufferSize = getBufferSize(); 086 if (locking && bufferedIo) { 087 LOGGER.warn("Locking and buffering are mutually exclusive. No buffering will occur for {}", fileName); 088 bufferedIo = false; 089 } 090 if (!bufferedIo && bufferSize > 0) { 091 LOGGER.warn("The bufferSize is set to {} but bufferedIo is false: {}", bufferSize, bufferedIo); 092 } 093 final Layout<? extends Serializable> layout = getOrCreateLayout(); 094 095 final FileManager manager = FileManager.getFileManager(fileName, append, locking, bufferedIo, createOnDemand, 096 advertiseUri, layout, bufferSize, filePermissions, fileOwner, fileGroup, getConfiguration()); 097 if (manager == null) { 098 return null; 099 } 100 101 return new FileAppender(getName(), layout, getFilter(), manager, fileName, isIgnoreExceptions(), 102 !bufferedIo || isImmediateFlush(), advertise ? getConfiguration().getAdvertiser() : null); 103 } 104 105 public String getAdvertiseUri() { 106 return advertiseUri; 107 } 108 109 public String getFileName() { 110 return fileName; 111 } 112 113 public boolean isAdvertise() { 114 return advertise; 115 } 116 117 public boolean isAppend() { 118 return append; 119 } 120 121 public boolean isCreateOnDemand() { 122 return createOnDemand; 123 } 124 125 public boolean isLocking() { 126 return locking; 127 } 128 129 public String getFilePermissions() { 130 return filePermissions; 131 } 132 133 public String getFileOwner() { 134 return fileOwner; 135 } 136 137 public String getFileGroup() { 138 return fileGroup; 139 } 140 141 public B withAdvertise(final boolean advertise) { 142 this.advertise = advertise; 143 return asBuilder(); 144 } 145 146 public B withAdvertiseUri(final String advertiseUri) { 147 this.advertiseUri = advertiseUri; 148 return asBuilder(); 149 } 150 151 public B withAppend(final boolean append) { 152 this.append = append; 153 return asBuilder(); 154 } 155 156 public B withFileName(final String fileName) { 157 this.fileName = fileName; 158 return asBuilder(); 159 } 160 161 public B withCreateOnDemand(final boolean createOnDemand) { 162 this.createOnDemand = createOnDemand; 163 return asBuilder(); 164 } 165 166 public B withLocking(final boolean locking) { 167 this.locking = locking; 168 return asBuilder(); 169 } 170 171 public B withFilePermissions(final String filePermissions) { 172 this.filePermissions = filePermissions; 173 return asBuilder(); 174 } 175 176 public B withFileOwner(final String fileOwner) { 177 this.fileOwner = fileOwner; 178 return asBuilder(); 179 } 180 181 public B withFileGroup(final String fileGroup) { 182 this.fileGroup = fileGroup; 183 return asBuilder(); 184 } 185 186 } 187 188 private static final int DEFAULT_BUFFER_SIZE = 8192; 189 190 /** 191 * Create a File Appender. 192 * @param fileName The name and path of the file. 193 * @param append "True" if the file should be appended to, "false" if it should be overwritten. 194 * The default is "true". 195 * @param locking "True" if the file should be locked. The default is "false". 196 * @param name The name of the Appender. 197 * @param immediateFlush "true" if the contents should be flushed on every write, "false" otherwise. The default 198 * is "true". 199 * @param ignoreExceptions If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise 200 * they are propagated to the caller. 201 * @param bufferedIo "true" if I/O should be buffered, "false" otherwise. The default is "true". 202 * @param bufferSizeStr buffer size for buffered IO (default is 8192). 203 * @param layout The layout to use to format the event. If no layout is provided the default PatternLayout 204 * will be used. 205 * @param filter The filter, if any, to use. 206 * @param advertise "true" if the appender configuration should be advertised, "false" otherwise. 207 * @param advertiseUri The advertised URI which can be used to retrieve the file contents. 208 * @param config The Configuration 209 * @return The FileAppender. 210 * @deprecated Use {@link #newBuilder()} 211 */ 212 @Deprecated 213 public static <B extends Builder<B>> FileAppender createAppender( 214 // @formatter:off 215 final String fileName, 216 final String append, 217 final String locking, 218 final String name, 219 final String immediateFlush, 220 final String ignoreExceptions, 221 final String bufferedIo, 222 final String bufferSizeStr, 223 final Layout<? extends Serializable> layout, 224 final Filter filter, 225 final String advertise, 226 final String advertiseUri, 227 final Configuration config) { 228 return FileAppender.<B>newBuilder() 229 .withAdvertise(Boolean.parseBoolean(advertise)) 230 .withAdvertiseUri(advertiseUri) 231 .withAppend(Booleans.parseBoolean(append, true)) 232 .withBufferedIo(Booleans.parseBoolean(bufferedIo, true)) 233 .withBufferSize(Integers.parseInt(bufferSizeStr, DEFAULT_BUFFER_SIZE)) 234 .setConfiguration(config) 235 .withFileName(fileName) 236 .withFilter(filter) 237 .withIgnoreExceptions(Booleans.parseBoolean(ignoreExceptions, true)) 238 .withImmediateFlush(Booleans.parseBoolean(immediateFlush, true)) 239 .withLayout(layout) 240 .withLocking(Boolean.parseBoolean(locking)) 241 .withName(name) 242 .build(); 243 // @formatter:on 244 } 245 246 @PluginBuilderFactory 247 public static <B extends Builder<B>> B newBuilder() { 248 return new Builder<B>().asBuilder(); 249 } 250 251 private final String fileName; 252 253 private final Advertiser advertiser; 254 255 private final Object advertisement; 256 257 private FileAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, 258 final FileManager manager, final String filename, final boolean ignoreExceptions, 259 final boolean immediateFlush, final Advertiser advertiser) { 260 261 super(name, layout, filter, ignoreExceptions, immediateFlush, manager); 262 if (advertiser != null) { 263 final Map<String, String> configuration = new HashMap<>(layout.getContentFormat()); 264 configuration.putAll(manager.getContentFormat()); 265 configuration.put("contentType", layout.getContentType()); 266 configuration.put("name", name); 267 advertisement = advertiser.advertise(configuration); 268 } else { 269 advertisement = null; 270 } 271 this.fileName = filename; 272 this.advertiser = advertiser; 273 } 274 275 /** 276 * Returns the file name this appender is associated with. 277 * @return The File name. 278 */ 279 public String getFileName() { 280 return this.fileName; 281 } 282 283 @Override 284 public boolean stop(final long timeout, final TimeUnit timeUnit) { 285 setStopping(); 286 super.stop(timeout, timeUnit, false); 287 if (advertiser != null) { 288 advertiser.unadvertise(advertisement); 289 } 290 setStopped(); 291 return true; 292 } 293}