/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: Debug.java,v 1.6 2009/08/19 05:41:17 veiming Exp $ * * Portions Copyrighted 2013-2016 ForgeRock AS. * */ package com.sun.identity.shared.debug; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import org.slf4j.helpers.MessageFormatter; import com.sun.identity.shared.configuration.SystemPropertiesManager; import com.sun.identity.shared.debug.file.impl.StdDebugFile; import com.sun.identity.shared.debug.impl.DebugProviderImpl; // NOTE: Since JVM specs guarantee atomic access/updates to int variables // (actually all variables except double and long), the design consciously // avoids synchronized methods, particularly for message(). This is done to // reduce the performance overhead of synchronized message() when debugging // is disabled. This does not have serious side-effects other than an occasional // invocation of message() missing concurrent update of 'debugLevel'. /** * **************************************************************************** *
* Allows a uniform interface to file debug and exception information in a
* uniform format. Debug
supports different levels/states of
* filing debug information (in the ascending order): OFF
,
* ERROR
, WARNING
, MESSAGE
and
* ON
. A given debug level/state is enabled if the debug
* state/level is set to at least that state/level. For example, if the debug
* state is ERROR
, only errors will be filed. If the debug state
* is WARNING
, only errors and warnings will be filed. If the
* debug state is MESSAGE
, everything will be filed.
* MESSAGE
and ON
are of the same levels; the
* difference between them being MESSAGE
writes to a file,
* whereas ON
writes to System.out.
*
* Debug service uses the property file, AMConfig.properties
, to
* set the default debug level and the output directory where the debug files
* will be placed. The properties file is located (using
* {@link java.util.ResourceBundle} semantics) from one of the directories in
* the CLASSPATH.
*
* The following keys are used to configure the Debug service. Possible values * for the key 'com.iplanet.services.debug.level' are: off | error | warning | * message. The key 'com.iplanet.services.debug.directory' specifies the output * directory where the debug files will be created. Optionally, the key * 'com.sun.identity.util.debug.provider' may be used to plugin a non-default * implementation of the debug service where necessary. *
* ** ** * If there is an error reading or loading the properties, Debug service will * redirect all debug information to* com.iplanet.services.debug.level * com.iplanet.services.debug.directory * com.sun.identity.util.debug.provider ** *
System.out
*
* If these properties are changed, the server must be restarted for the changes
* to take effect.
*
*
* NOTE: Debugging is an IO intensive operation and may hurt application
* performance when abused. Particularly, note that Java evaluates the arguments
* to message()
and warning()
even when debugging
* is turned off. It is recommended that the debug state be checked before
* invoking any message()
or warning()
methods to
* avoid unnecessary argument evaluation and to maximize application
* performance.
*
ERROR
, error debugging is also disabled.
*/
public static final int ERROR = 1;
/**
* flags the state where warning debugging is enabled, but message debugging
* is disabled. When debugging is set to less than WARNING
,
* warning debugging is also disabled.
*/
public static final int WARNING = 2;
/**
* This state enables debugging of messages, warnings and errors.
*/
public static final int MESSAGE = 3;
/**
* flags the enabled debug state for warnings, errors and messages. Printing
* to a file is disabled. All printing is done on System.out.
*/
public static final int ON = 4;
/**
* flags the disabled debug state.
*/
public static final String STR_OFF = "off";
/**
* flags the state where error debugging is enabled. When debugging is set
* to less than ERROR
, error debugging is also disabled.
*/
public static final String STR_ERROR = "error";
/**
* flags the state where warning debugging is enabled, but message debugging
* is disabled. When debugging is set to less than WARNING
,
* warning debugging is also disabled.
*/
public static final String STR_WARNING = "warning";
/**
* This state enables debugging of messages, warnings and errors.
*/
public static final String STR_MESSAGE = "message";
/**
* flags the enables debug state for warnings, errors and messages. Printing
* to a file is disabled. All printing is done on System.out.
*/
public static final String STR_ON = "on";
/**
* debugMap is a container of all active Debug objects. Log file name is the
* key and Debug is the value of this map.
*/
private static Map debugMap = new HashMap();
/**
* serviceInitialized indicates if the service is already initialized.
*/
private static boolean serviceInitialized = false;
/**
* the provider instance that will be used for Debug service.
*/
private static IDebugProvider debugProvider;
/**
* Gets an existing instance of Debug for the specified debug name or a new
* one if no such instance already exists. If a Debug object has to be
* created, its level is set to the level defined in the
* AMConfig.properties
file. The level can be changed later
* by using {@link #setDebug(int)} or {@link #setDebug(String)} methods.
*
* @param debugName name of the debug instances to be created
* @return a Debug instance corresponding to the specified debug name.
*/
public static synchronized Debug getInstance(String debugName) {
IDebug iDebug = getDebugProvider().getInstance(debugName);
Debug debug = (Debug) getDebugMap().get(iDebug.getName());
if (debug == null) {
debug = new Debug(iDebug);
getDebugMap().put(iDebug.getName(), debug);
}
return debug;
}
/**
* Returns a collection of all Debug instances that exist in the system at
* the current instance. This is a live collection that will be updated as
* and when new Debug instances are created. Note that if an iterator is
* used, it could potentially cause a
* ConcurrentModificationException
if during the process of
* iteration, the collection is modified by the system.
*
* @return a collection of all Debug instances in the system.
*/
public static Collection getInstances() {
return getDebugMap().values();
}
/**
* Gets the Map
of all Debug instances being used in the
* system currently.
*
* @return the Map
of all Debug instances
*/
private static Map getDebugMap() {
return debugMap;
}
/**
* Sets the provider instance to be used by Debug service.
*
* @param provider the IDebugProvider
instance that is used by the
* Debug service.
*/
private static void setDebugProvider(IDebugProvider provider) {
debugProvider = provider;
}
/**
* Gets the configured debug provider being used by the Debug service.
*
* @return the configured debug provider.
*/
static IDebugProvider getDebugProvider() {
return debugProvider;
}
/**
* Initializes the Debug Service by locating the SPI implementations and
* instantiating the appropriate classes.
*/
private static synchronized void initialize() {
if (!serviceInitialized) {
String providerName = SystemPropertiesManager.get(DebugConstants.CONFIG_DEBUG_PROVIDER);
IDebugProvider provider = null;
boolean providerLoadFailed = false;
Exception exceptionCatched = null;
if (providerName != null && providerName.trim().length() > 0) {
try {
provider = (IDebugProvider) Class.forName(providerName).newInstance();
} catch (ClassNotFoundException cnex) {
providerLoadFailed = true;
exceptionCatched = cnex;
} catch (InstantiationException iex) {
providerLoadFailed = true;
exceptionCatched = iex;
} catch (IllegalAccessException iaex) {
providerLoadFailed = true;
exceptionCatched = iaex;
} catch (ClassCastException ccex) {
providerLoadFailed = true;
exceptionCatched = ccex;
}
}
if (provider == null) {
if (providerLoadFailed) {
ResourceBundle bundle = com.sun.identity.shared.locale.Locale.getInstallResourceBundle
("amUtilMsgs");
StdDebugFile.printError(Debug.class.getSimpleName(), bundle.getString("com.iplanet" + ".services" +
".debug.invalidprovider"), exceptionCatched);
}
provider = new DebugProviderImpl();
}
setDebugProvider(provider);
serviceInitialized = true;
}
}
/* Instance fields and methods */
/**
* The instance of the actual debug service class as obtained from the
* configured provider.
*/
private IDebug debugServiceInstance;
/**
* Convinience method to query the name being used for this Debug instance.
* The return value of this method is a string exactly equal to the name
* that was used while creating this instance.
*
* @return the name of this Debug instance
*/
public String getName() {
return getDebugServiceInstance().getName();
}
/**
* Checks if message debugging is enabled.
*
*
* NOTE: Debugging is an IO intensive operation and may hurt
* application performance when abused. Particularly, note that Java
* evaluates arguments to message()
even when debugging is
* turned off. It is recommended that messageEnabled()
be
* called to check the debug state before invoking any
* message()
methods to avoid unnecessary argument evaluation
* and maximize application performance.
*
true
if message debugging is enabled
* false
if message debugging is disabled
*/
public boolean messageEnabled() {
return getDebugServiceInstance().messageEnabled();
}
/**
* Checks if warning debugging is enabled.
*
*
* NOTE: Debugging is an IO intensive operation and may hurt
* application performance when abused. Particularly, note that Java
* evaluates arguments to warning()
even when warning
* debugging is turned off. It is recommended that
* warningEnabled()
be called to check the debug state before
* invoking any warning()
methods to avoid unnecessary
* argument evaluation and maximize application performance.
*
true
if warning debugging is enabled
* false
if warning debugging is disabled
*/
public boolean warningEnabled() {
return getDebugServiceInstance().warningEnabled();
}
/**
* Checks if error debugging is enabled.
*
*
* NOTE: Debugging is an IO intensive operation and may hurt
* application performance when abused. Particularly, note that Java
* evaluates arguments to error()
even when error debugging
* is turned off. It is recommended that errorEnabled()
be
* called to check the debug state before invoking any error()
* methods to avoid unnecessary argument evaluation and maximize application
* performance.
*
true
if error debugging is enabled
* false
if error debugging is disabled
*/
public boolean errorEnabled() {
return getDebugServiceInstance().errorEnabled();
}
/**
* Returns one of the five possible values:
*
* NOTE: Debugging is an IO intensive operation and may hurt
* application performance when abused. Particularly, note that Java
* evaluates arguments to message()
even when debugging is
* turned off. So when the argument to this method involves the String
* concatenation operator '+' or any other method invocation,
* messageEnabled
MUST be used. It is recommended
* that the debug state be checked by invoking messageEnabled()
* before invoking any message()
methods to avoid unnecessary
* argument evaluation and maximize application performance.
*
* Prints debug and exception messages only when the debug state is either
* Debug.MESSAGE or Debug.ON. If the debug file is not accessible and
* debugging is enabled, the message along with a time stamp and thread info
* will be printed on System.out
.
*
* This method creates the debug file if does not exist; otherwise it starts * appending to the existing debug file. When invoked for the first time on * this object, the method writes a line delimiter of '*'s. *
* *
* Note that the debug file will remain open until destroy()
* is invoked. To conserve file resources, you should invoke
* destroy()
explicitly rather than wait for the garbage
* collector to clean up.
*
* NOTE: Debugging is an IO intensive operation and may hurt
* application performance when abused. Particularly, note that Java
* evaluates arguments to message()
even when debugging is
* turned off. It is recommended that the debug state be checked by invoking
* messageEnabled()
before invoking any
* message()
methods to avoid unnecessary argument evaluation
* and to maximize application performance.
*
System.out
or
* to the debug file. If msg
is null, it is
* ignored.
* @param t Throwable
, on which
* printStackTrace
will be invoked to print the
* stack trace. If t
is null, it is ignored.
* @see Debug#error(String, Throwable)
*/
public void message(String msg, Throwable t) {
getDebugServiceInstance().message(msg, t);
}
/**
* A convenience method for message debug statements. The message will only be formatted
* if the debug level is greater than {@link #WARNING}, and then it will be formatted using
* the {@link org.slf4j.helpers.MessageFormatter} class. The relevant {@code message} method is then
* called depending on whether the last parameter is an instance of {@code Throwable}.
* * This method is convenient way of issuing warning level debug statements without having * to guard the call with a check to {@link #messageEnabled()}, as that check is done * before evaluating the method parameters. *
* For this optimisation to work properly, this method should not be called using string * concatenation. If concatenation is required, it can be achieved using format patterns. *
* In this way, the only cost to execution is the assembly of the varargs parameter. * @param msg The debug message format, using {@link MessageFormatter} style format patterns. * @param params The parameters to the message, optionally with a {@code Throwable} as * the last parameter. */ public void message(String msg, Object... params) { IDebug debug = getDebugServiceInstance(); if (debug.messageEnabled()) { String message = MessageFormatter.arrayFormat(msg, params).getMessage(); if (params.length > 0 && params[params.length - 1] instanceof Throwable) { debug.message(message, (Throwable) params[params.length - 1]); } else { debug.message(message, null); } } } /** * Prints warning messages only when debug level is greater than * Debug.ERROR. *
*
* NOTE: Debugging is an IO intensive operation and may hurt
* application performance when abused. Particularly, note that Java
* evaluates arguments to warning()
even when debugging is
* turned off. So when the argument to this method involves the String
* concatenation operator '+' or any other method invocation,
* warningEnabled
MUST be used. It is recommended
* that the debug state be checked by invoking warningEnabled()
* before invoking any warning()
methods to avoid unnecessary
* argument evaluation and to maximize application performance.
*
System.out
or
* to the debug file. If msg
is null, it is
* ignored.
* @see Debug#warning(String, Throwable)
*/
public void warning(String msg) {
getDebugServiceInstance().warning(msg, null);
}
/**
* Prints warning messages only when debug level is greater than
* Debug.ERROR.
*
*
* NOTE: Debugging is an IO intensive operation and may hurt
* application performance when abused. Particularly, note that Java
* evaluates arguments to warning()
even when debugging is
* turned off. It is recommended that the debug state be checked by invoking
* warningEnabled()
before invoking any
* warning()
methods to avoid unnecessary argument evaluation
* and to maximize application performance.
*
* If the debug file is not accessible and debugging is enabled, the message
* along with a time stamp and thread info will be printed on
* System.out
.
*
* This method creates the debug file if does not exist; otherwise it starts * appending to the existing debug file. When invoked for the first time on * this object, the method writes a line delimiter of '*'s. *
* *
* Note that the debug file will remain open until destroy()
* is invoked. To conserve file resources, you should invoke
* destroy()
explicitly rather than wait for the garbage
* collector to clean up.
*
System.out
or
* to the debug file. If msg
is null, it is
* ignored.
* @param t Throwable
, on which
* printStackTrace()
will be invoked to print the
* stack trace. If t
is null, it is ignored.
*/
public void warning(String msg, Throwable t) {
getDebugServiceInstance().warning(msg, t);
}
/**
* A convenience method for warning debug statements. The message will only be formatted
* if the debug level is greater than {@link #ERROR}, and then it will be formatted using
* the {@link org.slf4j.helpers.MessageFormatter} class. The relevant {@code warning} method is then
* called depending on whether the last parameter is an instance of {@code Throwable}.
* * This method is convenient way of issuing warning level debug statements without having * to guard the call with a check to {@link #warningEnabled()}, as that check is done * before evaluating the method parameters. *
* For this optimisation to work properly, this method should not be called using string * concatenation. If concatenation is required, it can be achieved using format patterns. *
* In this way, the only cost to execution is the assembly of the varargs parameter.
* @param msg The debug message format, using {@link MessageFormatter} style format patterns.
* @param params The parameters to the message, optionally with a {@code Throwable} as
* the last parameter.
*/
public void warning(String msg, Object... params) {
IDebug debug = getDebugServiceInstance();
if (debug.warningEnabled()) {
String message = MessageFormatter.arrayFormat(msg, params).getMessage();
if (params.length > 0 && params[params.length - 1] instanceof Throwable) {
debug.warning(message, (Throwable) params[params.length - 1]);
} else {
debug.warning(message, null);
}
}
}
/**
* Prints error messages only when debug level is greater than DEBUG.OFF.
*
* @param msg message to be printed. A newline will be appended to the
* message before printing either to System.out
or
* to the debug file. If msg
is null, it is
* ignored.
* @see Debug#error(String, Throwable)
*/
public void error(String msg) {
getDebugServiceInstance().error(msg, null);
}
/**
* Prints error messages only if debug state is greater than Debug.OFF. If
* the debug file is not accessible and debugging is enabled, the message
* along with a time stamp and thread info will be printed on
* System.out
.
*
* This method creates the debug file if does not exist; otherwise it starts * appending to the existing debug file. When invoked for the first time on * this object, the method writes a line delimiter of '*'s. *
* *
* Note that the debug file will remain open until destroy()
* is invoked. To conserve file resources, you should invoke
* destroy()
explicitly rather than wait for the garbage
* collector to clean up.
*
System.out
or
* to the debug file. If msg
is null, it is
* ignored.
* @param t Throwable
, on which
* printStackTrace()
will be invoked to print the
* stack trace. If t
is null, it is ignored.
*/
public void error(String msg, Throwable t) {
getDebugServiceInstance().error(msg, t);
}
/**
* A convenience method for error debug statements. The message will only be formatted
* if the debug level is greater than {@link #OFF}, and then it will be formatted using
* the {@link org.slf4j.helpers.MessageFormatter} class. The relevant {@code error} method is then
* called depending on whether the last parameter is an instance of {@code Throwable}.
* * This method is convenient way of issuing warning level debug statements without having * to guard the call with a check to {@link #errorEnabled()}, as that check is done * before evaluating the method parameters. *
* For this optimisation to work properly, this method should not be called using string * concatenation. If concatenation is required, it can be achieved using format patterns. *
* In this way, the only cost to execution is the assembly of the varargs parameter.
* @param msg The debug message format, using {@link MessageFormatter} style format patterns.
* @param params The parameters to the message, optionally with a {@code Throwable} as
* the last parameter.
*/
public void error(String msg, Object... params) {
IDebug debug = getDebugServiceInstance();
if (debug.errorEnabled()) {
String message = MessageFormatter.arrayFormat(msg, params).getMessage();
if (params.length > 0 && params[params.length - 1] instanceof Throwable) {
debug.error(message, (Throwable) params[params.length - 1]);
} else {
debug.error(message, null);
}
}
}
/**
* Sets the debug capabilities based on the values of the
* debugType
argument.
*
* @param debugType is any one of five possible values:
*
Debug.OFF
* Debug.ERROR
* Debug.WARNING
* Debug.MESSAGE
* Debug.ON
* debugType
argument.
*
* @param debugType is any one of the following possible values:
* Debug.STR_OFF
* Debug.STR_ERROR
* Debug.STR_WARNING
* Debug.STR_MESSAGE
* Debug.STR_ON
* destroy()
is invoked. To conserve file resources, you
* should invoke destroy()
explicitly rather than wait for
* the garbage collector to clean up.
*
*
* If this object is accessed after destroy()
has been
* invoked, the results are undefined.
*