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