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