/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.logging;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import alma.acs.logging.config.LogConfig;
import alma.acs.logging.config.LogConfigSubscriber;
import alma.acs.logging.level.AcsLogLevelDefinition;
import alma.acs.util.StopWatch;
/**
* The logging handler used by ACS for remote logging.
* All log records are immediately fed to a {@link alma.acs.logging.DispatchingLogQueue}.
*/
public class AcsLoggingHandler extends Handler implements LogConfigSubscriber
{
/**
* The queue that this handler puts all log records in.
*/
private DispatchingLogQueue logQueue;
/**
* Variable to enable messages.
* TODO: check if this flag should be integrated with LogConfig classes
*/
private static boolean DEBUG = Boolean.getBoolean("alma.acs.logging.verbose");
private final boolean PROFILE = Boolean.getBoolean("alma.acs.logging.profile.local");
private LogConfig logConfig;
private final String loggerName;
private final LogThrottle logThrottle;
private AcsLogLevelDefinition immediateDispatchLevel;
private boolean isClosed;
/**
* @param logQueue
* @param logConfig
* @param loggerName
* @param logThrottle may be null (then no throttling will happen)
*/
public AcsLoggingHandler(DispatchingLogQueue logQueue, LogConfig logConfig, String loggerName, LogThrottle logThrottle) {
this.logQueue = logQueue;
this.logConfig = logConfig;
this.loggerName = loggerName;
this.logThrottle = logThrottle;
configureLogging(logConfig);
logConfig.addSubscriber(this); // passing "this" should only be done when this object is fully constructed.
}
/**
* Called whenever the logging configuration is updated, for example when the CDB is read.
* @see alma.acs.logging.config.LogConfigSubscriber#configureLogging(alma.acs.logging.config.LogConfig)
*/
public void configureLogging(LogConfig logConfig) {
// we expect always the same singleton object, but there is no reason for this class to rely on this
// or even assert this behavior. Instead we just update our reference.
this.logConfig = logConfig;
// all remote Loggers get their levels configured so that isLoggable() returns correct results
// for both local and remote logging.
// In case the threshold for local logging is lower than for remote logging,
// this handler still needs to filter out log records whose levels are in between the thresholds.
try {
AcsLogLevelDefinition minLogLevelACS = AcsLogLevelDefinition.fromXsdLogLevel(
logConfig.getNamedLoggerConfig(loggerName).getMinLogLevel() );
setLevel(AcsLogLevel.getLowestMatchingJdkLevel(minLogLevelACS));
immediateDispatchLevel = logConfig.getImmediateDispatchLevel();
} catch (Exception ex) {
publish(new LogRecord(Level.WARNING, "Failed to configure remote log handler: " + ex.toString()));
}
}
/**
* @see java.util.logging.Handler#publish(java.util.logging.LogRecord)
* Writes a single log into an array of strings.
* @param logRecord
*/
public void publish(LogRecord logRecord)
{
if (isClosed) {
if (DEBUG) {
System.out.println("AcsLoggingHandler: ignoring record with msg='" + logRecord.getMessage() +
"' because this logging handler is already closed.");
}
return; // or throw an exc.
}
if (!isLoggable(logRecord) || logRecord.getLevel().intValue() == Level.OFF.intValue()) { // abusing Level.OFF for a log call is not caught by the JDK!
if (DEBUG) {
System.out.println("AcsLoggingHandler: ignoring record with msg='" + logRecord.getMessage() +
"' because isLoggable() was false.");
}
return;
}
StopWatch sw_local = null;
if (PROFILE) {
LogParameterUtil logParamUtil = new LogParameterUtil(logRecord);
StopWatch sw_parent = logParamUtil.getStopWatch();
if (sw_parent != null) {
sw_local = sw_parent.createStopWatchForSubtask("AcsLoggingHandler");
}
}
if (logThrottle == null || logThrottle.checkPublishLogRecordRemote()) {
// must trigger a call to LogRecord#inferCaller before the log record gets processed by a different thread
logRecord.getSourceClassName();
logQueue.log(logRecord);
if (AcsLogLevel.getNativeLevel(logRecord.getLevel()).getAcsLevel().compareTo(immediateDispatchLevel) >= 0) {
if (DEBUG) {
System.out.println("flushing log queue because of log record with level " + logRecord.getLevel().getName());
}
logQueue.flush();
}
}
else {
// System.out.println("Suppressed remote log!");
}
if (PROFILE && sw_local != null) {
sw_local.stop();
}
}
/**
* Forwards the flush request to the underlying queue.
* @see java.util.logging.Handler#flush()
*/
public void flush() {
logQueue.flush();
}
/**
* Required method. Not sure if it's useful for us.
* @see java.util.logging.Handler#close()
* Cleans handler up before exiting.
*/
public void close() {
isClosed = true;
logConfig.removeSubscriber(this);
}
/**
* Used in the test. Sets debug to true.
*/
protected static void debug()
{
DEBUG = true;
System.out.println("AcsLoggingHandler#DEBUG set to true...");
}
}