/* * 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;
/** * 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>
*/ publicclass AccessLogValve extends AbstractAccessLogValve {
/** * The suffix that is added to log file filenames.
*/ protectedvolatile 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.
*/ privatevolatilelong rotationLastChecked = 0L;
/** * Do we check for log file existence? Helpful if an external agent renames the log file so we can automagically * recreate it.
*/ privateboolean 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.
*/ protectedvolatile String encoding = null;
/** * The number of days to retain the access log files before they are removed.
*/ privateint maxDays = -1; privatevolatileboolean checkForOldLogs = false;
/** * @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
*/ publicvoid setDirectory(String directory) { this.directory = directory;
}
/** * Check for file existence before logging. * * @return <code>true</code> if file existence is checked first
*/ publicboolean isCheckExists() {
return checkExists;
}
/** * Set whether to check for log file existence before logging. * * @param checkExists true meaning to check for file existence.
*/ publicvoid 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
*/ publicvoid setPrefix(String prefix) { this.prefix = prefix;
}
/** * Should we rotate the access log. * * @return <code>true</code> if the access log should be rotated
*/ publicboolean isRotatable() { return rotatable;
}
/** * Configure whether the access log should be rotated. * * @param rotatable true if the log should be rotated
*/ publicvoid 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
*/ publicboolean 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
*/ publicvoid 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
*/ publicboolean isBuffered() { return buffered;
}
/** * Set the value if the logging should be buffered * * @param buffered <code>true</code> if buffered.
*/ publicvoid 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
*/ publicvoid 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
*/ publicvoid setFileDateFormat(String fileDateFormat) {
String newFormat; if (fileDateFormat == null) {
newFormat = "";
} else {
newFormat = fileDateFormat;
} this.fileDateFormat = newFormat;
/** * 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.
*/ publicvoid setEncoding(String encoding) { if (encoding != null && encoding.length() > 0) { this.encoding = encoding;
} else { this.encoding = null;
}
}
// --------------------------------------------------------- Public Methods
/** * Execute a periodic task, such as reloading, etc. This method will be invoked inside the classloading context of * this container. Unexpected throwables will be caught and logged.
*/
@Override publicsynchronizedvoid backgroundProcess() { if (getState().isAvailable() && getEnabled() && writer != null && buffered) {
writer.flush();
}
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.length() > 0) { if (!oldAccessLog.startsWith(prefix)) { continue;
}
match = true;
}
if (suffix != null && suffix.length() > 0) { 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.
*/ publicvoid 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
*/ publicsynchronizedboolean rotate(String newFileName) {
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.
*/ privatevoid 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 e) {
ExceptionUtils.handleThrowable(e);
log.error(sm.getString("accessLogValve.renameFail", rotatedLogFile, newLogFile), e);
}
}
}
/** * Close the currently open log file (if any) * * @param rename Rename file to final name after closing
*/ privatesynchronizedvoid 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 e) {
ExceptionUtils.handleThrowable(e);
log.error(sm.getString("accessLogValve.renameFail", currentLogFile, newLogFile), e);
}
} else {
log.error(sm.getString("accessLogValve.alreadyExists", currentLogFile, newLogFile));
}
}
writer = null;
dateStamp = "";
currentLogFile = null;
}
/** * Log the specified message to the log file, switching files if the date has changed since the previous log call. * * @param message Message to be logged
*/
@Override publicvoid 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 e) {
ExceptionUtils.handleThrowable(e);
log.info(sm.getString("accessLogValve.closeFail"), e);
}
/* Make sure date is correct */
dateStamp = fileDateFormatter.format(new Date(System.currentTimeMillis()));
/** * Open the new log file for the date specified by <code>dateStamp</code>.
*/ protectedsynchronizedvoid open() { // Open the current log file // If no rotate - no need for dateStamp in fileName
File pathname = getLogFile(rotatable && !renameOnRotate);
try {
writer = new PrintWriter( new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pathname, true), charset), 128000), false);
currentLogFile = pathname;
} catch (IOException e) {
writer = null;
currentLogFile = null;
log.error(sm.getString("accessLogValve.openFail", pathname, System.getProperty("user.name")), e);
} // 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 protectedsynchronizedvoid 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 protectedsynchronizedvoid stopInternal() throws LifecycleException {
super.stopInternal();
close(false);
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.17Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.