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}