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.zip.Deflater; 023 024import org.apache.logging.log4j.core.Filter; 025import org.apache.logging.log4j.core.Layout; 026import org.apache.logging.log4j.core.LogEvent; 027import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy; 028import org.apache.logging.log4j.core.appender.rolling.RollingRandomAccessFileManager; 029import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy; 030import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy; 031import org.apache.logging.log4j.core.config.Configuration; 032import org.apache.logging.log4j.core.config.plugins.Plugin; 033import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 034import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 035import org.apache.logging.log4j.core.config.plugins.PluginElement; 036import org.apache.logging.log4j.core.config.plugins.PluginFactory; 037import org.apache.logging.log4j.core.layout.PatternLayout; 038import org.apache.logging.log4j.core.net.Advertiser; 039import org.apache.logging.log4j.core.util.Booleans; 040import org.apache.logging.log4j.core.util.Integers; 041 042/** 043 * An appender that writes to random access files and can roll over at 044 * intervals. 045 */ 046@Plugin(name = "RollingRandomAccessFile", category = "Core", elementType = "appender", printObject = true) 047public final class RollingRandomAccessFileAppender extends AbstractOutputStreamAppender<RollingRandomAccessFileManager> { 048 049 private final String fileName; 050 private final String filePattern; 051 private final Object advertisement; 052 private final Advertiser advertiser; 053 054 private RollingRandomAccessFileAppender(final String name, final Layout<? extends Serializable> layout, 055 final Filter filter, final RollingRandomAccessFileManager manager, final String fileName, 056 final String filePattern, final boolean ignoreExceptions, 057 final boolean immediateFlush, final int bufferSize, final Advertiser advertiser) { 058 super(name, layout, filter, ignoreExceptions, immediateFlush, manager); 059 if (advertiser != null) { 060 final Map<String, String> configuration = new HashMap<>(layout.getContentFormat()); 061 configuration.put("contentType", layout.getContentType()); 062 configuration.put("name", name); 063 advertisement = advertiser.advertise(configuration); 064 } else { 065 advertisement = null; 066 } 067 this.fileName = fileName; 068 this.filePattern = filePattern; 069 this.advertiser = advertiser; 070 } 071 072 @Override 073 public void stop() { 074 super.stop(); 075 if (advertiser != null) { 076 advertiser.unadvertise(advertisement); 077 } 078 } 079 080 /** 081 * Write the log entry rolling over the file when required. 082 * 083 * @param event The LogEvent. 084 */ 085 @Override 086 public void append(final LogEvent event) { 087 final RollingRandomAccessFileManager manager = getManager(); 088 manager.checkRollover(event); 089 090 // Leverage the nice batching behaviour of async Loggers/Appenders: 091 // we can signal the file manager that it needs to flush the buffer 092 // to disk at the end of a batch. 093 // From a user's point of view, this means that all log events are 094 // _always_ available in the log file, without incurring the overhead 095 // of immediateFlush=true. 096 manager.setEndOfBatch(event.isEndOfBatch()); // FIXME manager's EndOfBatch threadlocal can be deleted 097 098 // LOG4J2-1292 utilize gc-free Layout.encode() method: taken care of in superclass 099 super.append(event); 100 } 101 102 /** 103 * Returns the File name for the Appender. 104 * 105 * @return The file name. 106 */ 107 public String getFileName() { 108 return fileName; 109 } 110 111 /** 112 * Returns the file pattern used when rolling over. 113 * 114 * @return The file pattern. 115 */ 116 public String getFilePattern() { 117 return filePattern; 118 } 119 120 /** 121 * Returns the size of the file manager's buffer. 122 * @return the buffer size 123 */ 124 public int getBufferSize() { 125 return getManager().getBufferSize(); 126 } 127 128 /** 129 * Create a RollingRandomAccessFileAppender. 130 * 131 * @param fileName The name of the file that is actively written to. 132 * (required). 133 * @param filePattern The pattern of the file name to use on rollover. 134 * (required). 135 * @param append If true, events are appended to the file. If false, the 136 * file is overwritten when opened. Defaults to "true" 137 * @param name The name of the Appender (required). 138 * @param immediateFlush When true, events are immediately flushed. Defaults 139 * to "true". 140 * @param bufferSizeStr The buffer size, defaults to {@value RollingRandomAccessFileManager#DEFAULT_BUFFER_SIZE}. 141 * @param policy The triggering policy. (required). 142 * @param strategy The rollover strategy. Defaults to 143 * DefaultRolloverStrategy. 144 * @param layout The layout to use (defaults to the default PatternLayout). 145 * @param filter The Filter or null. 146 * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise 147 * they are propagated to the caller. 148 * @param advertise "true" if the appender configuration should be 149 * advertised, "false" otherwise. 150 * @param advertiseURI The advertised URI which can be used to retrieve the 151 * file contents. 152 * @param config The Configuration. 153 * @return A RollingRandomAccessFileAppender. 154 */ 155 @PluginFactory 156 public static RollingRandomAccessFileAppender createAppender( 157 @PluginAttribute("fileName") final String fileName, 158 @PluginAttribute("filePattern") final String filePattern, 159 @PluginAttribute("append") final String append, 160 @PluginAttribute("name") final String name, 161 @PluginAttribute("immediateFlush") final String immediateFlush, 162 @PluginAttribute("bufferSize") final String bufferSizeStr, 163 @PluginElement("Policy") final TriggeringPolicy policy, 164 @PluginElement("Strategy") RolloverStrategy strategy, 165 @PluginElement("Layout") Layout<? extends Serializable> layout, 166 @PluginElement("Filter") final Filter filter, 167 @PluginAttribute("ignoreExceptions") final String ignore, 168 @PluginAttribute("advertise") final String advertise, 169 @PluginAttribute("advertiseURI") final String advertiseURI, 170 @PluginConfiguration final Configuration config) { 171 172 final boolean isAppend = Booleans.parseBoolean(append, true); 173 final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true); 174 final boolean isFlush = Booleans.parseBoolean(immediateFlush, true); 175 final boolean isAdvertise = Boolean.parseBoolean(advertise); 176 final int bufferSize = Integers.parseInt(bufferSizeStr, RollingRandomAccessFileManager.DEFAULT_BUFFER_SIZE); 177 178 if (name == null) { 179 LOGGER.error("No name provided for FileAppender"); 180 return null; 181 } 182 183 if (fileName == null) { 184 LOGGER.error("No filename was provided for FileAppender with name " + name); 185 return null; 186 } 187 188 if (filePattern == null) { 189 LOGGER.error("No filename pattern provided for FileAppender with name " + name); 190 return null; 191 } 192 193 if (policy == null) { 194 LOGGER.error("A TriggeringPolicy must be provided"); 195 return null; 196 } 197 198 if (strategy == null) { 199 strategy = DefaultRolloverStrategy.createStrategy(null, null, null, 200 String.valueOf(Deflater.DEFAULT_COMPRESSION), null, true, config); 201 } 202 203 if (layout == null) { 204 layout = PatternLayout.createDefaultLayout(); 205 } 206 207 final RollingRandomAccessFileManager manager = RollingRandomAccessFileManager.getRollingRandomAccessFileManager( 208 fileName, filePattern, isAppend, isFlush, bufferSize, policy, strategy, advertiseURI, layout); 209 if (manager == null) { 210 return null; 211 } 212 213 return new RollingRandomAccessFileAppender(name, layout, filter, manager, 214 fileName, filePattern, ignoreExceptions, isFlush, bufferSize, 215 isAdvertise ? config.getAdvertiser() : null); 216 } 217}