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