/*
* Sun Public License
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is available at http://www.sun.com/
*
* The Original Code is the SLAMD Distributed Load Generation Engine.
* The Initial Developer of the Original Code is Neil A. Wilson.
* Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
* Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): Neil A. Wilson
*/
package com.slamd.server;
import java.io.IOException;
import java.util.ArrayList;
import com.slamd.common.Constants;
import com.slamd.db.SLAMDDB;
import com.slamd.parameter.IntegerParameter;
import com.slamd.parameter.Parameter;
import com.slamd.parameter.ParameterList;
/**
* This class implements a thread that can be used to asynchronously write
* information to a specified log file. It works in conjunction with the SLAMD
* logger so that when the SLAMD logger is configured to work asynchronously it
* will write any messages to be logged into a memory-based queue. The logger
* thread will then periodically poll that queue to see if there are any
* messages to be logged. If so, then those messages will be written into the
* log file and the queue emptied. In this manner, the overhead associated with
* logging will not adversely impact the performance of the SLAMD server. Note
* that there will not be any debug logging performed in this class in order to
* prevent logging loops. However, significant events will be written to
* standard error.
*
*
* @author Neil A. Wilson
*/
public class LoggerThread
extends Thread
implements ConfigSubscriber
{
/**
* The name used to register the logger as a subscriber to the configuration
* handler.
*/
public static final String CONFIG_SUBSCRIBER_NAME = "SLAMD Logger Thread";
// Variables used to refer to the SLAMD server and its components
private SLAMDDB configDB;
private Logger logger;
private SLAMDServer slamdServer;
// A variable that controls how long the thread should sleep between polls of
// the logger's queue.
private int pollDelay;
/**
* Creates the logging thread and retrieves any pertinent configuration
* information from the configuration handler.
*
* @param slamdServer The SLAMD server with which this logging thread will
* be associated.
* @param logger The SLAMD logger whose queue this logging thread will
* watch.
*/
public LoggerThread(SLAMDServer slamdServer, Logger logger)
{
setName("Logger Thread");
// Initialize the instance variables
this.slamdServer = slamdServer;
this.logger = logger;
// Get the configuration handler and register as a subscriber
configDB = slamdServer.getConfigDB();
configDB.registerAsSubscriber(this);
// Retrieve the poll delay from the configuration
pollDelay = Constants.DEFAULT_LOG_POLL_DELAY;
String pollDelayStr =
configDB.getConfigParameter(Constants.PARAM_LOG_POLL_DELAY);
if ((pollDelayStr != null) && (pollDelayStr.length() > 0))
{
try
{
pollDelay = Integer.parseInt(pollDelayStr);
}
catch (NumberFormatException nfe)
{
System.err.println(slamdServer.getTimestamp() +
"WARNING -- " + Constants.PARAM_LOG_POLL_DELAY +
" should be numeric");
}
}
}
/**
* Performs the work of actually polling the logger's queue and writing any
* information that it finds there into the log file. Any problems
* encountered in this process will be written to standard error.
*/
@Override()
public void run()
{
// Make sure that we should actually be running. If the SLAMD server is
// logging synchronously, then there is no need for this.
if (! logger.logAsynchronously)
{
return;
}
// Once a request has been made to close the logger, then there is no more
// need to continue polling.
while (! logger.closeRequested)
{
// Define a list that we will use if there is actually something to be
// logged.
ArrayList messagesToLog = null;
// Get the current time so that we can figure out how long to sleep when
// the loop is over
long startTime = System.currentTimeMillis();
// First, check to see if there is anything to be logged. Grab the lock
// on buffer so that we know we have exclusive access to it, but only
// keep it for as little time as possible so that we don't hold up
// calls to log other messages
synchronized (logger.loggerMutex)
{
if (! logger.logBuffer.isEmpty())
{
// There are messages to be logged, so steal that buffer away for
// ourselves and replace it with a new empty one.
messagesToLog = logger.logBuffer;
logger.logBuffer = new ArrayList<String>();
}
}
// If there were messages to be logged, then write them to the log file
if (messagesToLog != null)
{
synchronized (logger.writerMutex)
{
for (int i=0; i < messagesToLog.size(); i++)
{
String message = (String) messagesToLog.get(i);
try
{
logger.logWriter.write(message);
logger.logWriter.newLine();
}
catch (IOException ioe)
{
System.err.println(slamdServer.getTimestamp() +
"ERROR writing message \"" + message +
"\" to log file: " + ioe);
}
}
try
{
logger.logWriter.flush();
} catch (IOException ioe) {}
}
}
// Determine how long we need to sleep until the next poll
long sleepTime = startTime + (pollDelay*1000) -
System.currentTimeMillis();
if (sleepTime > 0)
{
try
{
Thread.sleep(sleepTime);
} catch (InterruptedException ie) {}
}
}
// If we have gotten to this point, then a request has been made to close
// the logger. First, check to see if there are any more messages to be
// logged. If so, then write them to disk.
synchronized (logger.loggerMutex)
{
for (int i=0; i < logger.logBuffer.size(); i++)
{
String message = logger.logBuffer.get(i);
try
{
logger.logWriter.write(message);
logger.logWriter.newLine();
}
catch (IOException ioe)
{
System.err.println(slamdServer.getTimestamp() +
"ERROR writing message \"" + message +
"\" to log file: " + ioe);
}
}
}
// Finally, flush and close the log writer.
synchronized (logger.writerMutex)
{
try
{
logger.logWriter.flush();
logger.logWriter.close();
} catch (IOException ioe) {}
}
}
/**
* Retrieves the name that the logger thread uses to subscribe to the
* configuration handler in order to be notified of configuration changes.
*
* @return The name that the logger thread uses to subscribe to the
* configuration handler in order to be notified of configuration
* changes.
*/
public String getSubscriberName()
{
return CONFIG_SUBSCRIBER_NAME;
}
/**
* Retrieves the set of configuration parameters associated with this
* configuration subscriber.
*
* @return The set of configuration parameters associated with this
* configuration subscriber.
*/
public ParameterList getSubscriberParameters()
{
IntegerParameter pollDelayParameter =
new IntegerParameter(Constants.PARAM_LOG_POLL_DELAY,
"Log Poll Delay",
"The delay in seconds between checking the log " +
"queue for messages to be logged (asynchronous " +
"logging only).", true,
pollDelay, true, 1, false, 0);
Parameter[] params = new Parameter[]
{
pollDelayParameter
};
return new ParameterList(params);
}
/**
* Re-reads all configuration information used by the logger thread. In this
* case, the only option is the delay to use between polls of the log buffer.
*/
public void refreshSubscriberConfiguration()
{
pollDelay = Constants.DEFAULT_LOG_POLL_DELAY;
String pollDelayStr =
configDB.getConfigParameter(Constants.PARAM_LOG_POLL_DELAY);
if ((pollDelayStr != null) && (pollDelayStr.length() > 0))
{
try
{
pollDelay = Integer.parseInt(pollDelayStr);
}
catch (NumberFormatException nfe)
{
System.err.println(slamdServer.getTimestamp() +
"WARNING -- " + Constants.PARAM_LOG_POLL_DELAY +
" should be numeric");
}
}
else
{
pollDelay = Constants.DEFAULT_LOG_POLL_DELAY;
}
}
/**
* Re-reads all configuration information used by the logger thread. In this
* case, the only option is the delay to use between polls of the log buffer.
*
* @param parameterName The name of the parameter to be re-read from the
* configuration.
*/
public void refreshSubscriberConfiguration(String parameterName)
{
if (parameterName.equalsIgnoreCase(Constants.PARAM_LOG_POLL_DELAY))
{
pollDelay = Constants.DEFAULT_LOG_POLL_DELAY;
String pollDelayStr =
configDB.getConfigParameter(Constants.PARAM_LOG_POLL_DELAY);
if ((pollDelayStr != null) && (pollDelayStr.length() > 0))
{
try
{
pollDelay = Integer.parseInt(pollDelayStr);
}
catch (NumberFormatException nfe)
{
System.err.println(slamdServer.getTimestamp() +
"WARNING -- " +
Constants.PARAM_LOG_POLL_DELAY +
" should be numeric");
}
}
else
{
pollDelay = Constants.DEFAULT_LOG_POLL_DELAY;
}
}
}
}