![JAR search and dependency download from the Maven repository](/logo.png)
com.sun.enterprise.server.logging.GFFileHandler Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016-2021] [Payara Foundation and/or its affiliates]
package com.sun.enterprise.server.logging;
import com.sun.appserv.server.util.Version;
import com.sun.common.util.logging.BooleanLatch;
import com.sun.common.util.logging.GFLogRecord;
import com.sun.common.util.logging.LoggingOutputStream;
import com.sun.enterprise.admin.monitor.callflow.Agent;
import com.sun.enterprise.module.bootstrap.EarlyLogHandler;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.v3.logging.AgentFormatterDelegate;
import fish.payara.enterprise.server.logging.JSONLogFormatter;
import fish.payara.enterprise.server.logging.PayaraNotificationLogRotationTimer;;
import java.io.*;
import java.security.PrivilegedAction;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.ErrorManager;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;
import java.util.zip.GZIPOutputStream;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.glassfish.api.logging.Task;
import org.glassfish.config.support.TranslatedConfigView;
import org.glassfish.hk2.api.PostConstruct;
import org.glassfish.hk2.api.PreDestroy;
import org.glassfish.server.ServerEnvironmentImpl;
import org.jvnet.hk2.annotations.ContractsProvided;
import org.jvnet.hk2.annotations.Optional;
import org.jvnet.hk2.annotations.Service;
import static java.security.AccessController.doPrivileged;
import org.glassfish.internal.api.Globals;
/**
* GFFileHandler publishes formatted log Messages to a FILE.
*
* @author Jerome Dochez
* @author Carla Mott
*/
@Service
@Singleton
@ContractsProvided(
{GFFileHandler.class, java.util.logging.Handler.class,
LogEventBroadcaster.class, LoggingRuntime.class}
)
public class GFFileHandler extends StreamHandler implements
PostConstruct, PreDestroy, LogEventBroadcaster, LoggingRuntime {
private static final int DEFAULT_ROTATION_LIMIT_BYTES = 2000000;
public static final int DISABLE_LOG_FILE_ROTATION_VALUE = 0;
private final static LocalStringManagerImpl LOCAL_STRINGS =
new LocalStringManagerImpl(GFFileHandler.class);
private static final String RECORD_BEGIN_MARKER = "[#|";
private static final String RECORD_END_MARKER = "|#]";
private static final String RECORD_FIELD_SEPARATOR = "|";
private static final String RECORD_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
@Inject
protected ServerEnvironmentImpl env;
@Inject @Optional
private Agent agent;
// This is a OutputStream to keep track of number of bytes
// written out to the stream
private MeteredStream meter;
protected static final String LOGS_DIR = "logs";
private static final String LOG_FILE_NAME = "server.log";
private static final String GZIP_EXTENSION = ".gz";
private String absoluteServerLogName = null;
private File absoluteFile = null;
private int flushFrequency = 1;
private int maxHistoryFiles = 10;
private boolean logToFile = true;
private boolean rotationOnDateChange;
private String excludeFields;
private Integer rotationLimitAttrValue;
private Long rotationTimeLimitValue;
private boolean compressionOnRotation;
private boolean multiLineMode;
private String fileHandlerFormatter = "";
private String currentFileHandlerFormatter = "";
private boolean logStandardStreams;
private PrintStream oStdOutBackup = System.out;
private PrintStream oStdErrBackup = System.err;
private LoggingOutputStream stdoutOutputStream=null;
private LoggingOutputStream stderrOutputStream=null;
/** Initially the LogRotation will be off until the domain.xml value is read. */
private int limitForFileRotation = 0;
private BlockingQueue pendingRecords = new ArrayBlockingQueue<>(10000);
/**Rotation can be done in 3 ways:
* - Based on the Size: Rotate when some Threshold number of bytes are
* written to server.log
* - Based on the Time: Rotate ever 'n' minutes, mostly 24 hrs
* - Rotate now
* For mechanisms 2 and 3 we will use this flag. The rotate() will always
* be fired from the publish( ) method for consistency */
private AtomicBoolean rotationRequested = new AtomicBoolean(false);
private final Object rotationLock = new Object();
private static final String LOG_ROTATE_DATE_FORMAT =
"yyyy-MM-dd'T'HH-mm-ss";
private static final String DEFAULT_LOG_FILE_FORMATTER_CLASS_NAME = UniformLogFormatter.class.getName();
public static final int MINIMUM_ROTATION_LIMIT_VALUE = 500*1000;
private BooleanLatch done = new BooleanLatch();
private boolean dayBasedFileRotation = false;
private List logEventListeners = new ArrayList<>();
private Thread pump;
protected String logFileProperty = "";
private final LogManager manager = LogManager.getLogManager();
private final String className = getClass().getName();
private static final String GF_FILE_HANDLER = GFFileHandler.class.getCanonicalName() ;
private LogRecord logRecord = new LogRecord(Level.INFO, LogFacade.GF_VERSION_INFO);
@Override
public void postConstruct() {
String filename = evaluateFileName();
File logFile = new File(filename);
absoluteServerLogName = filename;
if (!logFile.isAbsolute()) {
logFile = new File(env.getInstanceRoot(), filename);
absoluteServerLogName = env.getInstanceRoot() + File.separator + filename;
}
changeFileName(logFile);
// Reading just few lines of log file to get the log formatter used.
String strLine;
int odlFormatter = 0;
int uniformLogFormatter = 0;
int jsonLogFormatter = 0;
int otherFormatter = 0;
boolean mustRotate = false;
try (BufferedReader br = new BufferedReader(new FileReader(logFile))) {
while ((strLine = br.readLine()) != null) {
strLine = strLine.trim();
if (!strLine.equals("")) {
if (LogFormatHelper.isUniformFormatLogHeader(strLine)) { // for ufl formatter
uniformLogFormatter++;
} else if (LogFormatHelper.isODLFormatLogHeader(strLine)) {
// for ODL formatter
odlFormatter++;
} else if (LogFormatHelper.isJSONFormatLogHeader(strLine)) {
//for JSON Log format
jsonLogFormatter++;
} else {
otherFormatter++; // for other formatter
}
// Rotate on startup for custom log files
if (otherFormatter > 0) {
mustRotate = true;
}
// Read only first log record line and break out of the loop
break;
}
}
} catch (Exception e) {
ErrorManager em = getErrorManager();
if (em != null) {
em.error(e.getMessage(), e, ErrorManager.GENERIC_FAILURE);
}
}
if (odlFormatter > 0) {
currentFileHandlerFormatter = "com.sun.enterprise.server.logging.ODLLogFormatter";
} else if (uniformLogFormatter > 0) {
currentFileHandlerFormatter = "com.sun.enterprise.server.logging.UniformLogFormatter";
} else if (jsonLogFormatter > 0) {
currentFileHandlerFormatter = "fish.payara.enterprise.server.logging.JSONLogFormatter";
}
String propertyValue = manager.getProperty(className + ".logtoFile");
boolean logToFile = true;
if (propertyValue != null) {
logToFile = Boolean.parseBoolean(propertyValue);
}
setLogToFile(logToFile);
logRecord.setParameters(new Object[]{Version.getFullVersion()});
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
propertyValue = manager.getProperty(className + ".rotationOnDateChange");
timeBasedRotation(propertyValue);
propertyValue = manager.getProperty(className + ".rotationLimitInBytes");
rotationOnFileSizeLimit(propertyValue);
//setLevel(Level.ALL);
propertyValue = manager.getProperty(className + ".flushFrequency");
if (propertyValue != null) {
try {
flushFrequency = Integer.parseInt(propertyValue);
} catch (NumberFormatException e) {
logRecord = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
logRecord.setParameters(new Object[]{propertyValue, "flushFrequency"});
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
}
}
if (flushFrequency <= 0) {
flushFrequency = 1;
}
propertyValue = manager.getProperty(className + ".maxHistoryFiles");
try {
if (propertyValue != null) {
maxHistoryFiles = Integer.parseInt(propertyValue);
}
} catch (NumberFormatException e) {
logRecord = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
logRecord.setParameters(new Object[]{propertyValue, "maxHistoryFiles"});
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
}
if (maxHistoryFiles < 0) {
maxHistoryFiles = 10;
}
propertyValue = manager.getProperty(className + ".compressOnRotation");
compressionOnRotation = false;
if (propertyValue != null) {
compressionOnRotation = Boolean.parseBoolean(propertyValue);
}
propertyValue = manager.getProperty(className + ".logStandardStreams");
if (propertyValue != null) {
logStandardStreams = Boolean.parseBoolean(propertyValue);
if (logStandardStreams) {
logStandardStreams();
}
}
String formatterName = manager.getProperty(className + ".formatter");
formatterName = (formatterName == null) ? DEFAULT_LOG_FILE_FORMATTER_CLASS_NAME : formatterName;
// Below snapshot of the code is used to rotate server.log file on startup. It is used to avoid different format
// log messages logged under same server.log file.
fileHandlerFormatter = formatterName;
if (mustRotate) {
rotate();
} else if (fileHandlerFormatter != null
&& !fileHandlerFormatter.equals(currentFileHandlerFormatter)) {
rotate();
}
excludeFields = manager.getProperty(LogManagerService.EXCLUDE_FIELDS_PROPERTY);
multiLineMode = Boolean.parseBoolean(manager.getProperty(LogManagerService.MULTI_LINE_MODE_PROPERTY));
configureLogFormatter(formatterName, excludeFields, multiLineMode);
}
private void configureLogFormatter(String formatterName, String excludeFields, boolean multiLineMode) {
if (UniformLogFormatter.class.getName().equals(formatterName)) {
configureUniformLogFormatter(excludeFields, multiLineMode);
} else if (ODLLogFormatter.class.getName().equals(formatterName)) {
configureODLFormatter(excludeFields, multiLineMode);
} else if (JSONLogFormatter.class.getName().equals(formatterName)) {
configureJSONFormatter(excludeFields);
} else {
// Custom formatter is configured in logging.properties
// Check if the user specified formatter is in play else
// log an error message
Formatter currentFormatter = this.getFormatter();
if (currentFormatter == null || !currentFormatter.getClass().getName().equals(formatterName)) {
Formatter formatter = findFormatterService(formatterName);
if (formatter == null) {
logRecord = new LogRecord(Level.SEVERE, LogFacade.INVALID_FORMATTER_CLASS_NAME);
logRecord.setParameters(new Object[]{formatterName});
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
// Fall back to the GlassFish default
configureDefaultFormatter(excludeFields, multiLineMode);
} else {
setFormatter(formatter);
}
}
}
formatterName = this.getFormatter().getClass().getName();
logRecord = new LogRecord(Level.INFO, LogFacade.LOG_FORMATTER_INFO);
logRecord.setParameters(new Object[]{formatterName});
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
}
private void timeBasedRotation(String propertyValue) {
rotationOnDateChange = false;
if (propertyValue != null) {
rotationOnDateChange = Boolean.parseBoolean(propertyValue);
}
if (rotationOnDateChange) {
rotationOnDateChange();
} else {
rotationTimeLimitValue = 0L;
try {
propertyValue = manager.getProperty(className + ".rotationTimelimitInMinutes");
if (propertyValue != null) {
rotationTimeLimitValue = Long.parseLong(propertyValue);
}
} catch (NumberFormatException e) {
logRecord = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
logRecord.setParameters(new Object[]{propertyValue, "rotationTimelimitInMinutes"});
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
}
rotationOnTimeLimit();
}
}
private void rotationOnDateChange() {
dayBasedFileRotation = true;
rotationTimeLimitValue = 0L;
int millisecondsInDay = 1000 * 60 * 60 * 24;
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
long systemTime = System.currentTimeMillis();
String nextDate = dateFormat.format(date.getTime() + millisecondsInDay);
Date nextDay;
try {
nextDay = dateFormat.parse(nextDate);
} catch (ParseException e) {
nextDay = new Date();
logRecord = new LogRecord(Level.WARNING, LogFacade.DATE_PARSING_FAILED);
logRecord.setParameters(new Object[]{nextDate});
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
}
long nextDaySystemTime = nextDay.getTime();
rotationTimeLimitValue = nextDaySystemTime - systemTime;
Task rotationTask = () -> {
rotate();
return null;
};
if (className.equals(GF_FILE_HANDLER)) {
LogRotationTimer.getInstance().startTimer(
new LogRotationTimerTask(rotationTask,
rotationTimeLimitValue / 60000));
} else {
PayaraNotificationLogRotationTimer.getInstance().startTimer(
new LogRotationTimerTask(rotationTask,
rotationTimeLimitValue / 60000));
}
}
private void rotationOnTimeLimit() {
if (rotationTimeLimitValue > 0) {
Task rotationTask = () -> {
rotate();
return null;
};
if (className.equals(GF_FILE_HANDLER)) {
LogRotationTimer.getInstance().startTimer(
new LogRotationTimerTask(rotationTask,
rotationTimeLimitValue));
} else {
PayaraNotificationLogRotationTimer.getInstance().startTimer(
new LogRotationTimerTask(rotationTask,
rotationTimeLimitValue));
}
}
}
private void rotationOnFileSizeLimit(String propertyValue) {
try {
if (propertyValue != null) {
rotationLimitAttrValue = Integer.parseInt(propertyValue);
} else {
rotationLimitAttrValue = DEFAULT_ROTATION_LIMIT_BYTES;
}
} catch (NumberFormatException e) {
logRecord = new LogRecord(Level.WARNING, LogFacade.INVALID_ATTRIBUTE_VALUE);
logRecord.setParameters(new Object[]{propertyValue, "rotationLimitInBytes"});
logRecord.setResourceBundle(ResourceBundle.getBundle(LogFacade.LOGGING_RB_NAME));
logRecord.setThreadID((int) Thread.currentThread().getId());
logRecord.setLoggerName(LogFacade.LOGGING_LOGGER_NAME);
EarlyLogHandler.earlyMessages.add(logRecord);
}
// We set the LogRotation limit here. The rotation limit is the
// Threshold for the number of bytes in the log file after which
// it will be rotated.
if (rotationLimitAttrValue >= MINIMUM_ROTATION_LIMIT_VALUE || rotationLimitAttrValue == DISABLE_LOG_FILE_ROTATION_VALUE) {
setLimitForRotation(rotationLimitAttrValue);
}
}
protected String evaluateFileName() {
String cname = getClass().getName();
LogManager manager = LogManager.getLogManager();
logFileProperty = manager.getProperty(cname + ".file");
if(logFileProperty==null || logFileProperty.trim().equals("")) {
logFileProperty = env.getInstanceRoot().getAbsolutePath() + File.separator + LOGS_DIR + File.separator +
LOG_FILE_NAME;
}
return TranslatedConfigView.expandConfigValue(logFileProperty);
}
Formatter findFormatterService(String formatterName) {
List formatterServices = Globals.getDefaultHabitat().getAllServices(Formatter.class);
for (Formatter formatter : formatterServices) {
if (formatter.getClass().getName().equals(formatterName)) {
return formatter;
}
}
return null;
}
private void configureDefaultFormatter(String excludeFields,
boolean multiLineMode) {
configureUniformLogFormatter(excludeFields, multiLineMode);
}
private void configureODLFormatter(String excludeFields, boolean multiLineMode) {
// this loop is used for ODL formatter
ODLLogFormatter formatterClass;
// set the formatter
if (agent != null) {
formatterClass = new ODLLogFormatter(new AgentFormatterDelegate(agent), excludeFields);
setFormatter(formatterClass);
} else {
formatterClass = new ODLLogFormatter(excludeFields);
setFormatter(formatterClass);
}
formatterClass.setMultiLineMode(multiLineMode);
formatterClass.noAnsi();
formatterClass.setLogEventBroadcaster(this);
}
private void configureUniformLogFormatter(String excludeFields, boolean multiLineMode) {
LogManager manager = LogManager.getLogManager();
String cname = getClass().getName();
// this loop is used for UFL formatter
UniformLogFormatter formatterClass;
// set the formatter
if (agent != null) {
formatterClass = new UniformLogFormatter(new AgentFormatterDelegate(agent), excludeFields);
setFormatter(formatterClass);
} else {
formatterClass = new UniformLogFormatter(excludeFields);
setFormatter(formatterClass);
}
formatterClass.setMultiLineMode(multiLineMode);
formatterClass.setLogEventBroadcaster(this);
formatterClass.noAnsi();
String recordBeginMarker = manager.getProperty(cname + ".logFormatBeginMarker");
if (recordBeginMarker == null || ("").equals(recordBeginMarker)) {
recordBeginMarker = RECORD_BEGIN_MARKER;
}
String recordEndMarker = manager.getProperty(cname + ".logFormatEndMarker");
if (recordEndMarker == null || ("").equals(recordEndMarker)) {
recordEndMarker = RECORD_END_MARKER;
}
String recordFieldSeparator = manager.getProperty(cname + ".logFormatFieldSeparator");
if (recordFieldSeparator == null || ("").equals(recordFieldSeparator) || recordFieldSeparator.length() > 1) {
recordFieldSeparator = RECORD_FIELD_SEPARATOR;
}
String recordDateFormat = manager.getProperty(cname + ".logFormatDateFormat");
if (recordDateFormat != null && !("").equals(recordDateFormat)) {
SimpleDateFormat sdf = new SimpleDateFormat(recordDateFormat);
try {
sdf.format(new Date());
} catch (Exception e) {
recordDateFormat = RECORD_DATE_FORMAT;
}
} else {
recordDateFormat = RECORD_DATE_FORMAT;
}
formatterClass.setRecordBeginMarker(recordBeginMarker);
formatterClass.setRecordEndMarker(recordEndMarker);
formatterClass.setRecordDateFormat(recordDateFormat);
formatterClass.setRecordFieldSeparator(recordFieldSeparator);
}
void initializePump() {
pump = new Thread() { //Not using the PayaraExecutorService here as it prevents shutdown happening quickly, see PAYARA-4118
@Override
public void run() {
while (!done.isSignalled() && logToFile) {
try {
log();
} catch (Exception e) {
// GLASSFISH-19125
// Continue the loop without exiting
}
}
}
};
pump.setName("GFFileHandler log pump");
pump.setDaemon(true);
pump.start();
}
@Override
public void preDestroy() {
// stop the Queue consumer thread.
if (LogFacade.LOGGING_LOGGER.isLoggable(Level.FINE)) {
LogFacade.LOGGING_LOGGER.fine("Logger handler killed");
}
System.setOut(oStdOutBackup);
System.setErr(oStdErrBackup);
try {
if (stdoutOutputStream != null) {
stdoutOutputStream.close();
}
if (stderrOutputStream != null) {
stderrOutputStream.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
done.tryReleaseShared(1);
if (pump != null) {
pump.interrupt();
}
// drain and return all
drainAllPendingRecords();
flush();
}
private void drainAllPendingRecords() {
drainPendingRecords(0);
}
/**
* Drains the amount of {@link LogRecord}s in the pending records queue.
* If passed in the amount <= 0 all of the records get drained.
* @param flushAmount number of records to drain from the queue of pending records.
*/
private void drainPendingRecords(int flushAmount) {
if (!pendingRecords.isEmpty()) {
Collection records;
if (flushAmount > 0) {
records = new ArrayList<>(flushAmount);
pendingRecords.drainTo(records, flushAmount);
} else {
records = new ArrayList<>(pendingRecords.size());
pendingRecords.drainTo(records);
}
for (LogRecord record : records) {
super.publish(record);
}
}
}
/**
* This method is invoked from LogManager.reInitializeLoggers() to
* change the location of the file.
*/
void changeFileName(File file) {
// If the file name is same as the current file name, there
// is no need to change the filename
if (file.equals(absoluteFile)) {
return;
}
synchronized (rotationLock) {
super.flush();
super.close();
try {
openFile(file);
absoluteFile = file;
} catch (IOException ix) {
new ErrorManager().error(
"FATAL ERROR: COULD NOT OPEN LOG FILE. " +
"Please Check to make sure that the directory for " +
"Logfile exists. Currently reverting back to use the " +
" default server.log", ix, ErrorManager.OPEN_FAILURE);
try {
// Reverting back to the old server.log
openFile(absoluteFile);
} catch (Exception e) {
new ErrorManager().error(
"FATAL ERROR: COULD NOT RE-OPEN SERVER LOG FILE. ", e,
ErrorManager.OPEN_FAILURE);
}
}
}
}
/**
* A simple getter to access the current log file written by
* this FileHandler.
*/
@Override
public File getCurrentLogFile() {
return absoluteFile;
}
/**
* A package private method to set the limit for File Rotation.
*/
private synchronized void setLimitForRotation(int rotationLimitInBytes) {
limitForFileRotation = rotationLimitInBytes;
}
private void configureJSONFormatter(String excludeFields) {
// this loop is used for JSON formatter
JSONLogFormatter formatterClass;
// set the formatter
if (agent != null) {
formatterClass = new JSONLogFormatter(new AgentFormatterDelegate(agent), excludeFields);
setFormatter(formatterClass);
} else {
formatterClass = new JSONLogFormatter(excludeFields);
setFormatter(formatterClass);
}
formatterClass.setLogEventBroadcaster(this);
}
/** NOTE: This private class is copied from java.util.logging.FileHandler
* A metered stream is a subclass of OutputStream that
* (a) forwards all its output to a target stream
* (b) keeps track of how many bytes have been written
*/
private static final class MeteredStream extends OutputStream {
private volatile boolean isOpen;
OutputStream out;
long written;
MeteredStream(OutputStream out, long written) {
this.out = out;
this.written = written;
isOpen = true;
}
@Override
public void write(int b) throws IOException {
out.write(b);
written++;
}
@Override
public void write(byte[] buff) throws IOException {
out.write(buff);
written += buff.length;
}
@Override
public void write(byte[] buff, int off, int len) throws IOException {
out.write(buff, off, len);
written += len;
}
@Override
public void flush() throws IOException {
out.flush();
}
@Override
public void close() throws IOException {
if (isOpen) {
isOpen = false;
flush();
out.close();
}
}
}
/**
* Creates the file and initialized MeteredStream and passes it on to
* Superclass (java.util.logging.StreamHandler).
*/
private void openFile(File file) throws IOException {
// check that the parent directory exists.
File parent = file.getParentFile();
if (!parent.exists() && !parent.mkdirs()) {
throw new IOException(LOCAL_STRINGS.getLocalString("parent.dir.create.failed",
"Failed to create the parent dir {0}", parent.getAbsolutePath()));
}
FileOutputStream fout = new FileOutputStream(file, true);
BufferedOutputStream bout = new BufferedOutputStream(fout);
meter = new MeteredStream(bout, file.length());
setOutputStream(meter);
}
/**
* Request Rotation called from Rotation Timer Task or LogMBean
*/
void requestRotation() {
rotationRequested.set(true);
}
/**
* cleanup the history log file based on attributes set under logging.properties file".
*
* If it is defined with valid number, we only keep that number of history logfiles;
* If "max_history_files" is defined without value, then default that number to be 10;
* If "max_history_files" is defined with value 0, any number of history files are kept.
*/
public void cleanUpHistoryLogFiles() {
if (maxHistoryFiles == 0)
return;
synchronized (rotationLock) {
File dir = absoluteFile.getParentFile();
String logFileName = absoluteFile.getName();
if (dir == null)
return;
File[] fset = dir.listFiles();
List candidates = new ArrayList<>();
for (int i = 0; fset != null && i < fset.length; i++) {
if (!logFileName.equals(fset[i].getName()) && fset[i].isFile()
&& fset[i].getName().startsWith(logFileName)) {
candidates.add(fset[i].getAbsolutePath());
}
}
if (candidates.size() <= maxHistoryFiles)
return;
Object[] paths = candidates.toArray();
java.util.Arrays.sort(paths);
try {
for (int i = 0; i < paths.length - maxHistoryFiles; i++) {
File logFile = new File((String) paths[i]);
boolean delFile = logFile.delete();
if (!delFile) {
throw new IOException("Could not delete log file: "
+ logFile.getAbsolutePath());
}
}
} catch (Exception e) {
new ErrorManager().error(
"FATAL ERROR: COULD NOT DELETE LOG FILE.", e,
ErrorManager.GENERIC_FAILURE);
}
}
}
/**
* A Simple rotate method to close the old file and start the new one
* when the limit is reached.
*/
public void rotate() {
final GFFileHandler thisInstance = this;
doPrivileged((PrivilegedAction
© 2015 - 2025 Weber Informatics LLC | Privacy Policy