ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/valves/AccessLogValve.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/valves/AccessLogValve.java
Status
scanned
Type
file
Name
AccessLogValve.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
21082 (20.6 KB)
MD5
1e714aabe98a56135b3fdfe839d62b70
SHA1
5d2fdeacbd58072a7692ed9a383814c2fb04be32
SHA256
6cbebde2a90c0e01fca42a054d378e279c9906b9c204f8bfe02ec0e638914c78
SHA512

      
    
SHA1_git
6658bcdb8ea70eb16e144ea407f2db02630c86e9
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
AccessLogValve.java | 20.6 KB |

/* * 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.catalina.valves; import java.io.BufferedWriter; import java.io.CharArrayWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Objects; import java.util.TimeZone; import org.apache.catalina.LifecycleException; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.buf.B2CConverter; /** * This is a concrete implementation of {@link AbstractAccessLogValve} that outputs the access log to a file. The * features of this implementation include: * <ul> * <li>Automatic date-based rollover of log files</li> * <li>Optional log file rotation</li> * </ul> * <p> * For UNIX users, another field called <code>checkExists</code> is also available. If set to true, the log file's * existence will be checked before each logging. This way an external log rotator can move the file somewhere and * Tomcat will start with a new file. * </p> * <p> * For JMX junkies, a public method called <code>rotate</code> has been made available to allow you to tell this * instance to move the existing log file to somewhere else and start writing a new log file. * </p> */ public class AccessLogValve extends AbstractAccessLogValve { private static final Log log = LogFactory.getLog(AccessLogValve.class); // ------------------------------------------------------ Constructor public AccessLogValve() { super(); } // ----------------------------------------------------- Instance Variables /** * The as-of date for the currently open log file, or a zero-length string if there is no open log file. */ private volatile String dateStamp = ""; /** * The directory in which log files are created. */ private String directory = "logs"; /** * The prefix that is added to log file filenames. */ protected volatile String prefix = "access_log"; /** * Should we rotate our log file? Default is true (like old behavior) */ protected boolean rotatable = true; /** * Should we defer inclusion of the date stamp in the file name until rotate time? Default is false. */ protected boolean renameOnRotate = false; /** * Buffered logging. */ private boolean buffered = true; /** * The suffix that is added to log file filenames. */ protected volatile String suffix = ""; /** * The PrintWriter to which we are currently logging, if any. */ protected PrintWriter writer = null; /** * A date formatter to format a Date using the format given by <code>fileDateFormat</code>. */ protected SimpleDateFormat fileDateFormatter = null; /** * The current log file we are writing to. Helpful when checkExists is true. */ protected File currentLogFile = null; /** * Instant when the log daily rotation was last checked. */ private volatile long rotationLastChecked = 0L; /** * Do we check for log file existence? Helpful if an external agent renames the log file so we can automagically * recreate it. */ private boolean checkExists = false; /** * Date format to place in log file name. */ protected String fileDateFormat = ".yyyy-MM-dd"; /** * Character set used by the log file. If it is <code>null</code>, UTF-8 will be used. An empty string will be * treated as <code>null</code> when this property is assigned. */ protected volatile String encoding = null; /** * The number of days to retain the access log files before they are removed. */ private int maxDays = -1; private volatile boolean checkForOldLogs = false; // ------------------------------------------------------------- Properties public int getMaxDays() { return maxDays; } public void setMaxDays(int maxDays) { this.maxDays = maxDays; } /** * @return the directory in which we create log files. */ public String getDirectory() { return directory; } /** * Set the directory in which we create log files. * * @param directory The new log file directory */ public void setDirectory(String directory) { this.directory = directory; } /** * Check for file existence before logging. * * @return <code>true</code> if file existence is checked first */ public boolean isCheckExists() { return checkExists; } /** * Set whether to check for log file existence before logging. * * @param checkExists true meaning to check for file existence. */ public void setCheckExists(boolean checkExists) { this.checkExists = checkExists; } /** * @return the log file prefix. */ public String getPrefix() { return prefix; } /** * Set the log file prefix. * * @param prefix The new log file prefix */ public void setPrefix(String prefix) { this.prefix = prefix; } /** * Should we rotate the access log. * * @return <code>true</code> if the access log should be rotated */ public boolean isRotatable() { return rotatable; } /** * Configure whether the access log should be rotated. * * @param rotatable true if the log should be rotated */ public void setRotatable(boolean rotatable) { this.rotatable = rotatable; } /** * Should we defer inclusion of the date stamp in the file name until rotate time. * * @return <code>true</code> if the logs file names are time stamped only when they are rotated */ public boolean isRenameOnRotate() { return renameOnRotate; } /** * Set the value if we should defer inclusion of the date stamp in the file name until rotate time * * @param renameOnRotate true if defer inclusion of date stamp */ public void setRenameOnRotate(boolean renameOnRotate) { this.renameOnRotate = renameOnRotate; } /** * Is the logging buffered. Usually buffering can increase performance. * * @return <code>true</code> if the logging uses a buffer */ public boolean isBuffered() { return buffered; } /** * Set the value if the logging should be buffered * * @param buffered <code>true</code> if buffered. */ public void setBuffered(boolean buffered) { this.buffered = buffered; } /** * @return the log file suffix. */ public String getSuffix() { return suffix; } /** * Set the log file suffix. * * @param suffix The new log file suffix */ public void setSuffix(String suffix) { this.suffix = suffix; } /** * @return the date format date based log rotation. */ public String getFileDateFormat() { return fileDateFormat; } /** * Set the date format date based log rotation. * * @param fileDateFormat The format for the file timestamp */ public void setFileDateFormat(String fileDateFormat) { String newFormat; newFormat = Objects.requireNonNullElse(fileDateFormat, ""); this.fileDateFormat = newFormat; synchronized (this) { fileDateFormatter = new SimpleDateFormat(newFormat, Locale.US); fileDateFormatter.setTimeZone(TimeZone.getDefault()); } } /** * Return the character set name that is used to write the log file. * * @return Character set name, or <code>null</code> if the default character set is used. */ public String getEncoding() { return encoding; } /** * Set the character set that is used to write the log file. * * @param encoding The name of the character set. */ public void setEncoding(String encoding) { if (encoding != null && !encoding.isEmpty()) { this.encoding = encoding; } else { this.encoding = null; } } // --------------------------------------------------------- Public Methods /** * Provides support for access log rotation. */ @Override public synchronized void backgroundProcess() { if (getState().isAvailable() && getEnabled() && writer != null && buffered) { writer.flush(); } int maxDays = this.maxDays; String prefix = this.prefix; String suffix = this.suffix; if (rotatable && checkForOldLogs && maxDays > 0) { long deleteIfLastModifiedBefore = System.currentTimeMillis() - (maxDays * 24L * 60 * 60 * 1000); File dir = getDirectoryFile(); if (dir.isDirectory()) { String[] oldAccessLogs = dir.list(); if (oldAccessLogs != null) { for (String oldAccessLog : oldAccessLogs) { boolean match = false; if (prefix != null && !prefix.isEmpty()) { if (!oldAccessLog.startsWith(prefix)) { continue; } match = true; } if (suffix != null && !suffix.isEmpty()) { if (!oldAccessLog.endsWith(suffix)) { continue; } match = true; } if (match) { File file = new File(dir, oldAccessLog); if (file.isFile() && file.lastModified() < deleteIfLastModifiedBefore) { if (!file.delete()) { log.warn(sm.getString("accessLogValve.deleteFail", file.getAbsolutePath())); } } } } } } checkForOldLogs = false; } } /** * Rotate the log file if necessary. */ public void rotate() { if (rotatable) { // Only do a logfile switch check once a second, max. long systime = System.currentTimeMillis(); if ((systime - rotationLastChecked) > 1000) { synchronized (this) { if ((systime - rotationLastChecked) > 1000) { rotationLastChecked = systime; String tsDate; // Check for a change of date tsDate = fileDateFormatter.format(new Date(systime)); // If the date has changed, switch log files if (!dateStamp.equals(tsDate)) { close(true); dateStamp = tsDate; open(); } } } } } } /** * Rename the existing log file to something else. Then open the old log file name up once again. Intended to be * called by a JMX agent. * * @param newFileName The file name to move the log file entry to * * @return true if a file was rotated with no error */ public synchronized boolean rotate(String newFileName) { if (currentLogFile != null) { File holder = currentLogFile; close(false); try { holder.renameTo(new File(newFileName)); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error(sm.getString("accessLogValve.rotateFail"), t); } /* Make sure date is correct */ dateStamp = fileDateFormatter.format(new Date(System.currentTimeMillis())); open(); return true; } else { return false; } } // -------------------------------------------------------- Private Methods private File getDirectoryFile() { File dir = new File(directory); if (!dir.isAbsolute()) { dir = new File(getContainer().getCatalinaBase(), directory); } return dir; } /** * Create a File object based on the current log file name. Directories are created as needed but the underlying * file is not created or opened. * * @param useDateStamp include the timestamp in the file name. * * @return the log file object */ private File getLogFile(boolean useDateStamp) { // Create the directory if necessary File dir = getDirectoryFile(); if (!dir.mkdirs() && !dir.isDirectory()) { log.error(sm.getString("accessLogValve.openDirFail", dir)); } // Calculate the current log file name File pathname; if (useDateStamp) { pathname = new File(dir.getAbsoluteFile(), prefix + dateStamp + suffix); } else { pathname = new File(dir.getAbsoluteFile(), prefix + suffix); } File parent = pathname.getParentFile(); if (!parent.mkdirs() && !parent.isDirectory()) { log.error(sm.getString("accessLogValve.openDirFail", parent)); } return pathname; } /** * Move a current but rotated log file back to the unrotated one. Needed if date stamp inclusion is deferred to * rotation time. */ private void restore() { File newLogFile = getLogFile(false); File rotatedLogFile = getLogFile(true); if (rotatedLogFile.exists() && !newLogFile.exists() && !rotatedLogFile.equals(newLogFile)) { try { if (!rotatedLogFile.renameTo(newLogFile)) { log.error(sm.getString("accessLogValve.renameFail", rotatedLogFile, newLogFile)); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error(sm.getString("accessLogValve.renameFail", rotatedLogFile, newLogFile), t); } } } /** * Close the currently open log file (if any) * * @param rename Rename file to final name after closing */ private synchronized void close(boolean rename) { if (writer == null) { return; } writer.flush(); writer.close(); if (rename && renameOnRotate) { File newLogFile = getLogFile(true); if (!newLogFile.exists()) { try { if (!currentLogFile.renameTo(newLogFile)) { log.error(sm.getString("accessLogValve.renameFail", currentLogFile, newLogFile)); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.error(sm.getString("accessLogValve.renameFail", currentLogFile, newLogFile), t); } } else { log.error(sm.getString("accessLogValve.alreadyExists", currentLogFile, newLogFile)); } } writer = null; dateStamp = ""; currentLogFile = null; } @Override public void log(CharArrayWriter message) { rotate(); /* In case something external rotated the file instead */ if (checkExists) { synchronized (this) { if (currentLogFile != null && !currentLogFile.exists()) { try { close(false); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.info(sm.getString("accessLogValve.closeFail"), t); } /* Make sure date is correct */ dateStamp = fileDateFormatter.format(new Date(System.currentTimeMillis())); open(); } } } // Log this message try { message.write(System.lineSeparator()); synchronized (this) { if (writer != null) { message.writeTo(writer); if (!buffered) { writer.flush(); } } } } catch (IOException ioe) { log.warn(sm.getString("accessLogValve.writeFail", message.toString()), ioe); } } /** * Open the new log file for the date specified by <code>dateStamp</code>. */ protected synchronized void open() { // Open the current log file // If no rotate - no need for dateStamp in fileName File pathname = getLogFile(rotatable && !renameOnRotate); Charset charset = null; if (encoding != null) { try { charset = B2CConverter.getCharset(encoding); } catch (UnsupportedEncodingException ex) { log.error(sm.getString("accessLogValve.unsupportedEncoding", encoding), ex); } } if (charset == null) { charset = StandardCharsets.UTF_8; } try { writer = new PrintWriter( new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pathname, true), charset), 128000), false); currentLogFile = pathname; } catch (IOException ioe) { writer = null; currentLogFile = null; log.error(sm.getString("accessLogValve.openFail", pathname, System.getProperty("user.name")), ioe); } // Rotating a log file will always trigger a new file to be opened so // when a new file is opened, check to see if any old files need to be // removed. checkForOldLogs = true; } /** * Start this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error that prevents this component from being * used */ @Override protected void startInternal() throws LifecycleException { // Initialize the Date formatters String format = getFileDateFormat(); fileDateFormatter = new SimpleDateFormat(format, Locale.US); fileDateFormatter.setTimeZone(TimeZone.getDefault()); dateStamp = fileDateFormatter.format(new Date(System.currentTimeMillis())); if (rotatable && renameOnRotate) { restore(); } open(); super.startInternal(); } /** * Stop this component and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error that prevents this component from being * used */ @Override protected void stopInternal() throws LifecycleException { super.stopInternal(); close(false); } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
6.1
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9