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.log4j.builders.appender; 018 019import static org.apache.log4j.builders.BuilderManager.CATEGORY; 020import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM; 021import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG; 022import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG; 023import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR; 024import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG; 025import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR; 026import static org.apache.log4j.xml.XmlConfiguration.forEachElement; 027 028import java.util.Properties; 029import java.util.concurrent.atomic.AtomicBoolean; 030import java.util.concurrent.atomic.AtomicInteger; 031import java.util.concurrent.atomic.AtomicReference; 032 033import org.apache.log4j.Appender; 034import org.apache.log4j.Layout; 035import org.apache.log4j.bridge.AppenderWrapper; 036import org.apache.log4j.bridge.LayoutAdapter; 037import org.apache.log4j.bridge.LayoutWrapper; 038import org.apache.log4j.builders.AbstractBuilder; 039import org.apache.log4j.config.Log4j1Configuration; 040import org.apache.log4j.config.PropertiesConfiguration; 041import org.apache.log4j.spi.Filter; 042import org.apache.log4j.xml.XmlConfiguration; 043import org.apache.logging.log4j.Logger; 044import org.apache.logging.log4j.core.appender.RollingFileAppender; 045import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy; 046import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy; 047import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy; 048import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy; 049import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy; 050import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy; 051import org.apache.logging.log4j.core.config.plugins.Plugin; 052import org.apache.logging.log4j.status.StatusLogger; 053import org.w3c.dom.Element; 054 055 056/** 057 * Build a File Appender 058 */ 059@Plugin(name = "org.apache.log4j.RollingFileAppender", category = CATEGORY) 060public class RollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder { 061 062 private static final Logger LOGGER = StatusLogger.getLogger(); 063 064 public RollingFileAppenderBuilder() { 065 } 066 067 public RollingFileAppenderBuilder(String prefix, Properties props) { 068 super(prefix, props); 069 } 070 071 @Override 072 public Appender parseAppender(Element appenderElement, XmlConfiguration config) { 073 String name = appenderElement.getAttribute(NAME_ATTR); 074 AtomicReference<Layout> layout = new AtomicReference<>(); 075 AtomicReference<Filter> filter = new AtomicReference<>(); 076 AtomicReference<String> fileName = new AtomicReference<>(); 077 AtomicBoolean immediateFlush = new AtomicBoolean(); 078 AtomicBoolean append = new AtomicBoolean(); 079 AtomicBoolean bufferedIo = new AtomicBoolean(); 080 AtomicInteger bufferSize = new AtomicInteger(8192); 081 AtomicReference<String> maxSize = new AtomicReference<>(); 082 AtomicReference<String> maxBackups = new AtomicReference<>(); 083 AtomicReference<String> level = new AtomicReference<>(); 084 forEachElement(appenderElement.getChildNodes(), currentElement -> { 085 switch (currentElement.getTagName()) { 086 case LAYOUT_TAG: 087 layout.set(config.parseLayout(currentElement)); 088 break; 089 case FILTER_TAG: 090 filter.set(config.parseFilters(currentElement)); 091 break; 092 case PARAM_TAG: { 093 switch (currentElement.getAttribute(NAME_ATTR)) { 094 case FILE_PARAM: 095 fileName.set(currentElement.getAttribute(VALUE_ATTR)); 096 break; 097 case APPEND_PARAM: { 098 String bool = currentElement.getAttribute(VALUE_ATTR); 099 if (bool != null) { 100 append.set(Boolean.parseBoolean(bool)); 101 } else { 102 LOGGER.warn("No value provided for append parameter"); 103 } 104 break; 105 } 106 case BUFFERED_IO_PARAM: { 107 String bool = currentElement.getAttribute(VALUE_ATTR); 108 if (bool != null) { 109 bufferedIo.set(Boolean.parseBoolean(bool)); 110 } else { 111 LOGGER.warn("No value provided for bufferedIo parameter"); 112 } 113 break; 114 } 115 case BUFFER_SIZE_PARAM: { 116 String size = currentElement.getAttribute(VALUE_ATTR); 117 if (size != null) { 118 bufferSize.set(Integer.parseInt(size)); 119 } else { 120 LOGGER.warn("No value provide for bufferSize parameter"); 121 } 122 break; 123 } 124 case MAX_BACKUP_INDEX: { 125 String size = currentElement.getAttribute(VALUE_ATTR); 126 if (size != null) { 127 maxBackups.set(size); 128 } else { 129 LOGGER.warn("No value provide for maxBackupIndex parameter"); 130 } 131 break; 132 } 133 case MAX_SIZE_PARAM: { 134 String size = currentElement.getAttribute(VALUE_ATTR); 135 if (size != null) { 136 maxSize.set(size); 137 } else { 138 LOGGER.warn("No value provide for bufferSize parameter"); 139 } 140 break; 141 } 142 case THRESHOLD_PARAM: { 143 String value = currentElement.getAttribute(VALUE_ATTR); 144 if (value == null) { 145 LOGGER.warn("No value supplied for Threshold parameter, ignoring."); 146 } else { 147 level.set(value); 148 } 149 break; 150 } 151 } 152 break; 153 } 154 } 155 }); 156 return createAppender(name, config, layout.get(), filter.get(), bufferedIo.get(), immediateFlush.get(), 157 fileName.get(), level.get(), maxSize.get(), maxBackups.get()); 158 } 159 160 161 @Override 162 public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix, 163 final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) { 164 Layout layout = configuration.parseLayout(layoutPrefix, name, props); 165 Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name); 166 String fileName = getProperty(FILE_PARAM); 167 String level = getProperty(THRESHOLD_PARAM); 168 boolean immediateFlush = false; 169 boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM); 170 String maxSize = getProperty(MAX_SIZE_PARAM); 171 String maxBackups = getProperty(MAX_BACKUP_INDEX); 172 return createAppender(name, configuration, layout, filter, bufferedIo, immediateFlush, fileName, level, maxSize, 173 maxBackups); 174 } 175 176 private Appender createAppender(final String name, final Log4j1Configuration config, final Layout layout, 177 final Filter filter, final boolean bufferedIo, boolean immediateFlush, final String fileName, 178 final String level, final String maxSize, final String maxBackups) { 179 org.apache.logging.log4j.core.Layout<?> fileLayout = null; 180 if (!bufferedIo) { 181 immediateFlush = true; 182 } 183 if (layout instanceof LayoutWrapper) { 184 fileLayout = ((LayoutWrapper) layout).getLayout(); 185 } else if (layout != null) { 186 fileLayout = new LayoutAdapter(layout); 187 } 188 org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter); 189 if (fileName == null) { 190 LOGGER.warn("Unable to create File Appender, no file name provided"); 191 return null; 192 } 193 String filePattern = fileName +"%d{yyy-MM-dd}"; 194 TriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder().withModulate(true).build(); 195 SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize); 196 CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(sizePolicy, timePolicy); 197 RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder() 198 .withConfig(config) 199 .withMax(maxBackups) 200 .build(); 201 return new AppenderWrapper(RollingFileAppender.newBuilder() 202 .setName(name) 203 .setConfiguration(config) 204 .setLayout(fileLayout) 205 .setFilter(fileFilter) 206 .withBufferedIo(bufferedIo) 207 .withImmediateFlush(immediateFlush) 208 .withFileName(fileName) 209 .withFilePattern(filePattern) 210 .withPolicy(policy) 211 .withStrategy(strategy) 212 .build()); 213 } 214}