You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/01/14 18:33:15 UTC

[logging-log4j2] 01/02: Add classes for BC.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit c959ef9de38cc697c311f3efaaaac31fa302a7a0
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Jan 14 13:32:17 2022 -0500

    Add classes for BC.
---
 .../main/java/org/apache/log4j/FileAppender.java   | 306 +++++++++++++++++++++
 .../java/org/apache/log4j/RollingFileAppender.java | 253 +++++++++++++++++
 2 files changed, 559 insertions(+)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java
new file mode 100644
index 0000000..40f46a9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.Writer;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorCode;
+
+/**
+ * FileAppender appends log events to a file.
+ * <p>
+ * Support for <code>java.io.Writer</code> and console appending has been deprecated and then removed. See the
+ * replacement solutions: {@link WriterAppender} and {@link ConsoleAppender}.
+ * </p>
+ */
+public class FileAppender extends WriterAppender {
+
+    /**
+     * Controls file truncatation. The default value for this variable is <code>true</code>, meaning that by default a
+     * <code>FileAppender</code> will append to an existing file and not truncate it.
+     * <p>
+     * This option is meaningful only if the FileAppender opens the file.
+     * </p>
+     */
+    protected boolean fileAppend = true;
+
+    /**
+     * The name of the log file.
+     */
+    protected String fileName = null;
+
+    /**
+     * Do we do bufferedIO?
+     */
+    protected boolean bufferedIO = false;
+
+    /**
+     * Determines the size of IO buffer be. Default is 8K.
+     */
+    protected int bufferSize = 8 * 1024;
+
+    /**
+     * The default constructor does not do anything.
+     */
+    public FileAppender() {
+    }
+
+    /**
+     * Constructs a FileAppender and open the file designated by <code>filename</code>. The opened filename will become the
+     * output destination for this appender.
+     * <p>
+     * The file will be appended to.
+     * </p>
+     */
+    public FileAppender(Layout layout, String filename) throws IOException {
+        this(layout, filename, true);
+    }
+
+    /**
+     * Constructs a FileAppender and open the file designated by <code>filename</code>. The opened filename will become the
+     * output destination for this appender.
+     * <p>
+     * If the <code>append</code> parameter is true, the file will be appended to. Otherwise, the file designated by
+     * <code>filename</code> will be truncated before being opened.
+     * </p>
+     */
+    public FileAppender(Layout layout, String filename, boolean append) throws IOException {
+        this.layout = layout;
+        this.setFile(filename, append, false, bufferSize);
+    }
+
+    /**
+     * Constructs a <code>FileAppender</code> and open the file designated by <code>filename</code>. The opened filename
+     * will become the output destination for this appender.
+     * <p>
+     * If the <code>append</code> parameter is true, the file will be appended to. Otherwise, the file designated by
+     * <code>filename</code> will be truncated before being opened.
+     * </p>
+     * <p>
+     * If the <code>bufferedIO</code> parameter is <code>true</code>, then buffered IO will be used to write to the output
+     * file.
+     * </p>
+     */
+    public FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
+        this.layout = layout;
+        this.setFile(filename, append, bufferedIO, bufferSize);
+    }
+
+    /**
+     * If the value of <b>File</b> is not <code>null</code>, then {@link #setFile} is called with the values of <b>File</b>
+     * and <b>Append</b> properties.
+     * 
+     * @since 0.8.1
+     */
+    public void activateOptions() {
+        if (fileName != null) {
+            try {
+                setFile(fileName, fileAppend, bufferedIO, bufferSize);
+            } catch (java.io.IOException e) {
+                errorHandler.error("setFile(" + fileName + "," + fileAppend + ") call failed.", e, ErrorCode.FILE_OPEN_FAILURE);
+            }
+        } else {
+            // LogLog.error("File option not set for appender ["+name+"].");
+            LogLog.warn("File option not set for appender [" + name + "].");
+            LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
+        }
+    }
+
+    /**
+     * Closes the previously opened file.
+     */
+    protected void closeFile() {
+        if (this.qw != null) {
+            try {
+                this.qw.close();
+            } catch (java.io.IOException e) {
+                if (e instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                // Exceptionally, it does not make sense to delegate to an
+                // ErrorHandler. Since a closed appender is basically dead.
+                LogLog.error("Could not close " + qw, e);
+            }
+        }
+    }
+
+    /**
+     * Returns the value of the <b>Append</b> option.
+     */
+    public boolean getAppend() {
+        return fileAppend;
+    }
+
+    /**
+     * Get the value of the <b>BufferedIO</b> option.
+     * 
+     * <p>
+     * BufferedIO will significatnly increase performance on heavily loaded systems.
+     * </p>
+     */
+    public boolean getBufferedIO() {
+        return this.bufferedIO;
+    }
+
+    /**
+     * Get the size of the IO buffer.
+     */
+    public int getBufferSize() {
+        return this.bufferSize;
+    }
+
+    /** Returns the value of the <b>File</b> option. */
+    public String getFile() {
+        return fileName;
+    }
+
+    /**
+     * Close any previously opened file and call the parent's <code>reset</code>.
+     */
+    protected void reset() {
+        closeFile();
+        this.fileName = null;
+        super.reset();
+    }
+
+    /**
+     * The <b>Append</b> option takes a boolean value. It is set to <code>true</code> by default. If true, then
+     * <code>File</code> will be opened in append mode by {@link #setFile setFile} (see above). Otherwise, {@link #setFile
+     * setFile} will open <code>File</code> in truncate mode.
+     * 
+     * <p>
+     * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the options are set.
+     * </p>
+     */
+    public void setAppend(boolean flag) {
+        fileAppend = flag;
+    }
+
+    /**
+     * The <b>BufferedIO</b> option takes a boolean value. It is set to <code>false</code> by default. If true, then
+     * <code>File</code> will be opened and the resulting {@link java.io.Writer} wrapped around a {@link BufferedWriter}.
+     * 
+     * BufferedIO will significatnly increase performance on heavily loaded systems.
+     * 
+     */
+    public void setBufferedIO(boolean bufferedIO) {
+        this.bufferedIO = bufferedIO;
+        if (bufferedIO) {
+            immediateFlush = false;
+        }
+    }
+
+    /**
+     * Set the size of the IO buffer.
+     */
+    public void setBufferSize(int bufferSize) {
+        this.bufferSize = bufferSize;
+    }
+
+    /**
+     * The <b>File</b> property takes a string value which should be the name of the file to append to.
+     * <p>
+     * <font color="#DD0044"><b>Note that the special values "System.out" or "System.err" are no longer honored.</b></font>
+     * </p>
+     * <p>
+     * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the options are set.
+     * </p>
+     */
+    public void setFile(String file) {
+        // Trim spaces from both ends. The users probably does not want
+        // trailing spaces in file names.
+        String val = file.trim();
+        fileName = val;
+    }
+
+    /**
+     * Sets and <i>opens</i> the file where the log output will go. The specified file must be writable.
+     * <p>
+     * If there was already an opened file, then the previous file is closed first.
+     * </p>
+     * <p>
+     * <b>Do not use this method directly. To configure a FileAppender or one of its subclasses, set its properties one by
+     * one and then call activateOptions.</b>
+     * </p>
+     * 
+     * @param fileName The path to the log file.
+     * @param append If true will append to fileName. Otherwise will truncate fileName.
+     */
+    public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
+        LogLog.debug("setFile called: " + fileName + ", " + append);
+
+        // It does not make sense to have immediate flush and bufferedIO.
+        if (bufferedIO) {
+            setImmediateFlush(false);
+        }
+
+        reset();
+        FileOutputStream ostream = null;
+        try {
+            //
+            // attempt to create file
+            //
+            ostream = new FileOutputStream(fileName, append);
+        } catch (FileNotFoundException ex) {
+            //
+            // if parent directory does not exist then
+            // attempt to create it and try to create file
+            // see bug 9150
+            //
+            String parentName = new File(fileName).getParent();
+            if (parentName != null) {
+                File parentDir = new File(parentName);
+                if (!parentDir.exists() && parentDir.mkdirs()) {
+                    ostream = new FileOutputStream(fileName, append);
+                } else {
+                    throw ex;
+                }
+            } else {
+                throw ex;
+            }
+        }
+        Writer fw = createWriter(ostream);
+        if (bufferedIO) {
+            fw = new BufferedWriter(fw, bufferSize);
+        }
+        this.setQWForFiles(fw);
+        this.fileName = fileName;
+        this.fileAppend = append;
+        this.bufferedIO = bufferedIO;
+        this.bufferSize = bufferSize;
+        writeHeader();
+        LogLog.debug("setFile ended");
+    }
+
+    /**
+     * Sets the quiet writer being used.
+     * 
+     * This method is overriden by {@link RollingFileAppender}.
+     */
+    protected void setQWForFiles(Writer writer) {
+        this.qw = new QuietWriter(writer, errorHandler);
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java
new file mode 100644
index 0000000..964c519
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.io.File;
+import java.io.InterruptedIOException;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.CountingQuietWriter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * RollingFileAppender extends FileAppender to backup the log files when they reach a certain size.
+ * 
+ * The log4j extras companion includes alternatives which should be considered for new deployments and which are
+ * discussed in the documentation for org.apache.log4j.rolling.RollingFileAppender.
+ */
+public class RollingFileAppender extends FileAppender {
+
+    /**
+     * The default maximum file size is 10MB.
+     */
+    protected long maxFileSize = 10 * 1024 * 1024;
+
+    /**
+     * There is one backup file by default.
+     */
+    protected int maxBackupIndex = 1;
+
+    private long nextRollover = 0;
+
+    /**
+     * The default constructor simply calls its {@link FileAppender#FileAppender parents constructor}.
+     */
+    public RollingFileAppender() {
+        super();
+    }
+
+    /**
+     * Constructs a RollingFileAppender and open the file designated by <code>filename</code>. The opened filename will
+     * become the ouput destination for this appender.
+     * 
+     * <p>
+     * If the <code>append</code> parameter is true, the file will be appended to. Otherwise, the file desginated by
+     * <code>filename</code> will be truncated before being opened.
+     * </p>
+     */
+    public RollingFileAppender(Layout layout, String filename, boolean append) throws IOException {
+        super(layout, filename, append);
+    }
+
+    /**
+     * Constructs a FileAppender and open the file designated by <code>filename</code>. The opened filename will become the
+     * output destination for this appender.
+     * 
+     * <p>
+     * The file will be appended to.
+     * </p>
+     */
+    public RollingFileAppender(Layout layout, String filename) throws IOException {
+        super(layout, filename);
+    }
+
+    /**
+     * Gets the value of the <b>MaxBackupIndex</b> option.
+     */
+    public int getMaxBackupIndex() {
+        return maxBackupIndex;
+    }
+
+    /**
+     * Gets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+     * 
+     * @since 1.1
+     */
+    public long getMaximumFileSize() {
+        return maxFileSize;
+    }
+
+    /**
+     * Implements the usual roll over behaviour.
+     * <p>
+     * If <code>MaxBackupIndex</code> is positive, then files {<code>File.1</code>, ...,
+     * <code>File.MaxBackupIndex -1</code>} are renamed to {<code>File.2</code>, ..., <code>File.MaxBackupIndex</code>}.
+     * Moreover, <code>File</code> is renamed <code>File.1</code> and closed. A new <code>File</code> is created to receive
+     * further log output.
+     * </p>
+     * <p>
+     * If <code>MaxBackupIndex</code> is equal to zero, then the <code>File</code> is truncated with no backup files
+     * created.
+     * </p>
+     */
+    public // synchronization not necessary since doAppend is alreasy synched
+    void rollOver() {
+        File target;
+        File file;
+
+        if (qw != null) {
+            long size = ((CountingQuietWriter) qw).getCount();
+            LogLog.debug("rolling over count=" + size);
+            // if operation fails, do not roll again until
+            // maxFileSize more bytes are written
+            nextRollover = size + maxFileSize;
+        }
+        LogLog.debug("maxBackupIndex=" + maxBackupIndex);
+
+        boolean renameSucceeded = true;
+        // If maxBackups <= 0, then there is no file renaming to be done.
+        if (maxBackupIndex > 0) {
+            // Delete the oldest file, to keep Windows happy.
+            file = new File(fileName + '.' + maxBackupIndex);
+            if (file.exists())
+                renameSucceeded = file.delete();
+
+            // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
+            for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
+                file = new File(fileName + "." + i);
+                if (file.exists()) {
+                    target = new File(fileName + '.' + (i + 1));
+                    LogLog.debug("Renaming file " + file + " to " + target);
+                    renameSucceeded = file.renameTo(target);
+                }
+            }
+
+            if (renameSucceeded) {
+                // Rename fileName to fileName.1
+                target = new File(fileName + "." + 1);
+
+                this.closeFile(); // keep windows happy.
+
+                file = new File(fileName);
+                LogLog.debug("Renaming file " + file + " to " + target);
+                renameSucceeded = file.renameTo(target);
+                //
+                // if file rename failed, reopen file with append = true
+                //
+                if (!renameSucceeded) {
+                    try {
+                        this.setFile(fileName, true, bufferedIO, bufferSize);
+                    } catch (IOException e) {
+                        if (e instanceof InterruptedIOException) {
+                            Thread.currentThread().interrupt();
+                        }
+                        LogLog.error("setFile(" + fileName + ", true) call failed.", e);
+                    }
+                }
+            }
+        }
+
+        //
+        // if all renames were successful, then
+        //
+        if (renameSucceeded) {
+            try {
+                // This will also close the file. This is OK since multiple
+                // close operations are safe.
+                this.setFile(fileName, false, bufferedIO, bufferSize);
+                nextRollover = 0;
+            } catch (IOException e) {
+                if (e instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                LogLog.error("setFile(" + fileName + ", false) call failed.", e);
+            }
+        }
+    }
+
+    public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize) throws IOException {
+        super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
+        if (append) {
+            File f = new File(fileName);
+            ((CountingQuietWriter) qw).setCount(f.length());
+        }
+    }
+
+    /**
+     * Sets the maximum number of backup files to keep around.
+     * 
+     * <p>
+     * The <b>MaxBackupIndex</b> option determines how many backup files are kept before the oldest is erased. This option
+     * takes a positive integer value. If set to zero, then there will be no backup files and the log file will be truncated
+     * when it reaches <code>MaxFileSize</code>.
+     * </p>
+     */
+    public void setMaxBackupIndex(int maxBackups) {
+        this.maxBackupIndex = maxBackups;
+    }
+
+    /**
+     * Sets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+     * 
+     * <p>
+     * This method is equivalent to {@link #setMaxFileSize} except that it is required for differentiating the setter taking
+     * a <code>long</code> argument from the setter taking a <code>String</code> argument by the JavaBeans
+     * {@link java.beans.Introspector Introspector}.
+     * </p>
+     * 
+     * @see #setMaxFileSize(String)
+     */
+    public void setMaximumFileSize(long maxFileSize) {
+        this.maxFileSize = maxFileSize;
+    }
+
+    /**
+     * Sets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+     * 
+     * <p>
+     * In configuration files, the <b>MaxFileSize</b> option takes an long integer in the range 0 - 2^63. You can specify
+     * the value with the suffixes "KB", "MB" or "GB" so that the integer is interpreted being expressed respectively in
+     * kilobytes, megabytes or gigabytes. For example, the value "10KB" will be interpreted as 10240.
+     * </p>
+     */
+    public void setMaxFileSize(String value) {
+        maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
+    }
+
+    protected void setQWForFiles(Writer writer) {
+        this.qw = new CountingQuietWriter(writer, errorHandler);
+    }
+
+    /**
+     * This method differentiates RollingFileAppender from its super class.
+     * 
+     * @since 0.9.0
+     */
+    protected void subAppend(LoggingEvent event) {
+        super.subAppend(event);
+        if (fileName != null && qw != null) {
+            long size = ((CountingQuietWriter) qw).getCount();
+            if (size >= maxFileSize && size >= nextRollover) {
+                rollOver();
+            }
+        }
+    }
+}