| | |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Portions Copyright 2006-2007 Sun Microsystems, Inc. |
| | | * Portions Copyright 2007 Sun Microsystems, Inc. |
| | | */ |
| | | |
| | | package org.opends.server.loggers.debug; |
| | | |
| | | import org.opends.server.api.ProtocolElement; |
| | | import org.opends.server.loggers.Logger; |
| | | import org.opends.server.loggers.LogLevel; |
| | | |
| | | import java.util.Map; |
| | | import java.util.HashMap; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.CopyOnWriteArraySet; |
| | | import java.nio.ByteBuffer; |
| | | import java.lang.reflect.Method; |
| | | import java.lang.reflect.InvocationTargetException; |
| | | |
| | | import com.sleepycat.je.OperationStatus; |
| | | import com.sleepycat.je.Database; |
| | | import com.sleepycat.je.Transaction; |
| | | import com.sleepycat.je.DatabaseEntry; |
| | | import org.opends.server.api.ProtocolElement; |
| | | import org.opends.server.api.DebugLogPublisher; |
| | | import org.opends.server.loggers.*; |
| | | import org.opends.server.types.*; |
| | | import org.opends.server.util.DynamicConstants; |
| | | import org.opends.server.util.StaticUtils; |
| | | import org.opends.server.admin.std.server.DebugLogPublisherCfg; |
| | | import org.opends.server.admin.std.meta.DebugLogPublisherCfgDefn; |
| | | import org.opends.server.admin.server.ConfigurationAddListener; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.admin.server.ConfigurationDeleteListener; |
| | | import org.opends.server.admin.ClassPropertyDefinition; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | |
| | | import static org.opends.server.messages.MessageHandler.getMessage; |
| | | import static org.opends.server.messages.ConfigMessages.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import com.sleepycat.je.*; |
| | | |
| | | /** |
| | | * A logger for debug and trace logging. DebugLogger provides a debugging |
| | |
| | | * |
| | | * DebugLogger is self-initializing. |
| | | */ |
| | | public class DebugLogger extends Logger |
| | | public class DebugLogger implements |
| | | ConfigurationAddListener<DebugLogPublisherCfg>, |
| | | ConfigurationDeleteListener<DebugLogPublisherCfg>, |
| | | ConfigurationChangeListener<DebugLogPublisherCfg> |
| | | { |
| | | private static DebugLogger logger = null; |
| | | static boolean staticEnabled = false; |
| | | //The default level to log constructor exectuions. |
| | | static final LogLevel DEFAULT_CONSTRUCTOR_LEVEL = |
| | | DebugLogLevel.VERBOSE; |
| | | //The default level to log method entry and exit pointcuts. |
| | | static final LogLevel DEFAULT_ENTRY_EXIT_LEVEL = |
| | | DebugLogLevel.VERBOSE; |
| | | //The default level to log method entry and exit pointcuts. |
| | | static final LogLevel DEFAULT_THROWN_LEVEL = |
| | | DebugLogLevel.ERROR; |
| | | |
| | | private Map<String, Tracer> classTracers; |
| | | // The set of all DebugTracer aspect instances. |
| | | static CopyOnWriteArraySet<DebugTracer> classTracers = |
| | | new CopyOnWriteArraySet<DebugTracer>(); |
| | | |
| | | private DebugConfiguration configuration; |
| | | // The set of debug loggers that have been registered with the server. It |
| | | // will initially be empty. |
| | | static ConcurrentHashMap<DN, |
| | | DebugLogPublisher> debugPublishers = |
| | | new ConcurrentHashMap<DN, |
| | | DebugLogPublisher>(); |
| | | |
| | | private DebugLogger(DebugConfiguration config) |
| | | // Trace methods will use this static boolean to determine if debug is |
| | | // enabled so to not incur the cost of calling debugPublishers.isEmtpty(). |
| | | static boolean enabled = false; |
| | | |
| | | // The singleton instance of this class for configuration purposes. |
| | | static final DebugLogger instance = new DebugLogger(); |
| | | |
| | | static |
| | | { |
| | | super(config); |
| | | configuration = config; |
| | | classTracers = new HashMap<String, Tracer>(); |
| | | staticEnabled = enabled; |
| | | // Install the startup publishers if necessary until the config kicks in and |
| | | // adds the real publishers. |
| | | |
| | | if(DynamicConstants.WEAVE_ENABLED) |
| | | { |
| | | try |
| | | { |
| | | TextDebugLogPublisher startupDebugPublisher = |
| | | TextDebugLogPublisher.getStartupTextDebugPublisher( |
| | | new TextWriter.STDOUT()); |
| | | |
| | | debugPublishers.put(DN.NULL_DN, |
| | | startupDebugPublisher); |
| | | enabled = true; |
| | | |
| | | // Update all existing aspect instances |
| | | addTracerSettings(startupDebugPublisher); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | | System.err.println("Error installing the startup debug logger: " + |
| | | StaticUtils.stackTraceToSingleLineString(e)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Add an debug log publisher to the debug logger. |
| | | * |
| | | * @param dn The DN of the configuration entry for the publisher. |
| | | * @param publisher The error log publisher to add. |
| | | */ |
| | | public synchronized static void addDebugLogPublisher(DN dn, |
| | | DebugLogPublisher publisher) |
| | | { |
| | | debugPublishers.put(dn, publisher); |
| | | enabled = DynamicConstants.WEAVE_ENABLED; |
| | | } |
| | | |
| | | /** |
| | | * Obtain the trace logger singleton. |
| | | * @return the trace logger singleton. |
| | | * Remove an debug log publisher from the debug logger. |
| | | * |
| | | * @param dn The DN of the publisher to remove. |
| | | * @return The publisher that was removed or null if it was not found. |
| | | */ |
| | | public static synchronized DebugLogger getLogger() |
| | | public synchronized static DebugLogPublisher removeDebugLogPublisher(DN dn) |
| | | { |
| | | if (logger == null) { |
| | | /** |
| | | * The debug logger is being intialized for the first time. |
| | | * Bootstrap the debug logger when the server first starts up so |
| | | * all debug messages are log from the first initialization of a |
| | | * server class. |
| | | */ |
| | | logger= new DebugLogger(DebugConfiguration.getStartupConfiguration()); |
| | | DebugLogPublisher removed = debugPublishers.remove(dn); |
| | | |
| | | if(removed != null) |
| | | { |
| | | removed.close(); |
| | | } |
| | | |
| | | return logger; |
| | | if(debugPublishers.isEmpty()) |
| | | { |
| | | enabled = false; |
| | | } |
| | | |
| | | return removed; |
| | | } |
| | | |
| | | /** |
| | | * Obtain the status of this logger singleton. |
| | | * Removes all existing debug log publishers from the logger. |
| | | */ |
| | | public synchronized static void removeAllDebugLogPublishers() |
| | | { |
| | | for(DebugLogPublisher publisher : debugPublishers.values()) |
| | | { |
| | | publisher.close(); |
| | | } |
| | | |
| | | debugPublishers.clear(); |
| | | |
| | | enabled = false; |
| | | } |
| | | |
| | | /** |
| | | * Initializes all the debug log publishers. |
| | | * |
| | | * @return the status of this logger. |
| | | * @param configs The debug log publisher configurations. |
| | | * @throws ConfigException |
| | | * If an unrecoverable problem arises in the process of |
| | | * performing the initialization as a result of the server |
| | | * configuration. |
| | | * @throws InitializationException |
| | | * If a problem occurs during initialization that is not |
| | | * related to the server configuration. |
| | | */ |
| | | public void initializeDebugLogger(List<DebugLogPublisherCfg> configs) |
| | | throws ConfigException, InitializationException |
| | | { |
| | | for(DebugLogPublisherCfg config : configs) |
| | | { |
| | | config.addDebugChangeListener(this); |
| | | |
| | | if(config.isEnabled()) |
| | | { |
| | | DebugLogPublisher debugLogPublisher = getDebugPublisher(config); |
| | | |
| | | addDebugLogPublisher(config.dn(), debugLogPublisher); |
| | | |
| | | // Update all existing aspect instances |
| | | addTracerSettings(debugLogPublisher); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isConfigurationAddAcceptable(DebugLogPublisherCfg config, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | return !config.isEnabled() || |
| | | isJavaClassAcceptable(config, unacceptableReasons); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isConfigurationChangeAcceptable(DebugLogPublisherCfg config, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | return !config.isEnabled() || |
| | | isJavaClassAcceptable(config, unacceptableReasons); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConfigChangeResult applyConfigurationAdd(DebugLogPublisherCfg config) |
| | | { |
| | | // Default result code. |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | config.addDebugChangeListener(this); |
| | | |
| | | if(config.isEnabled()) |
| | | { |
| | | try |
| | | { |
| | | DebugLogPublisher debugLogPublisher = |
| | | getDebugPublisher(config); |
| | | |
| | | addDebugLogPublisher(config.dn(), debugLogPublisher); |
| | | |
| | | addTracerSettings(debugLogPublisher); |
| | | } |
| | | catch(ConfigException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | messages.add(e.getMessage()); |
| | | resultCode = DirectoryServer.getServerErrorResultCode(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | int msgID = MSGID_CONFIG_LOGGER_CANNOT_CREATE_LOGGER; |
| | | messages.add(getMessage(msgID, String.valueOf(config.dn().toString()), |
| | | stackTraceToSingleLineString(e))); |
| | | resultCode = DirectoryServer.getServerErrorResultCode(); |
| | | } |
| | | } |
| | | return new ConfigChangeResult(resultCode, adminActionRequired, messages); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConfigChangeResult applyConfigurationChange( |
| | | DebugLogPublisherCfg config) |
| | | { |
| | | // Default result code. |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | boolean adminActionRequired = false; |
| | | ArrayList<String> messages = new ArrayList<String>(); |
| | | |
| | | DN dn = config.dn(); |
| | | DebugLogPublisher debugLogPublisher = debugPublishers.get(dn); |
| | | |
| | | if(debugLogPublisher == null) |
| | | { |
| | | if(config.isEnabled()) |
| | | { |
| | | // Needs to be added and enabled. |
| | | return applyConfigurationAdd(config); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if(config.isEnabled()) |
| | | { |
| | | // The publisher is currently active, so we don't need to do anything. |
| | | // Changes to the class name cannot be |
| | | // applied dynamically, so if the class name did change then |
| | | // indicate that administrative action is required for that |
| | | // change to take effect. |
| | | String className = config.getJavaImplementationClass(); |
| | | if(!className.equals(debugLogPublisher.getClass().getName())) |
| | | { |
| | | adminActionRequired = true; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // The publisher is being disabled so shut down and remove. |
| | | removeTracerSettings(debugLogPublisher); |
| | | removeDebugLogPublisher(config.dn()); |
| | | } |
| | | } |
| | | |
| | | return new ConfigChangeResult(resultCode, adminActionRequired, messages); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean isConfigurationDeleteAcceptable(DebugLogPublisherCfg config, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | DN dn = config.dn(); |
| | | DebugLogPublisher debugLogPublisher = debugPublishers.get(dn); |
| | | return debugLogPublisher != null; |
| | | |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public ConfigChangeResult |
| | | applyConfigurationDelete(DebugLogPublisherCfg config) |
| | | { |
| | | // Default result code. |
| | | ResultCode resultCode = ResultCode.SUCCESS; |
| | | boolean adminActionRequired = false; |
| | | |
| | | DebugLogPublisher publisher = removeDebugLogPublisher(config.dn()); |
| | | if(publisher != null) |
| | | { |
| | | removeTracerSettings(publisher); |
| | | } |
| | | |
| | | return new ConfigChangeResult(resultCode, adminActionRequired); |
| | | } |
| | | |
| | | private boolean isJavaClassAcceptable(DebugLogPublisherCfg config, |
| | | List<String> unacceptableReasons) |
| | | { |
| | | String className = config.getJavaImplementationClass(); |
| | | DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance(); |
| | | ClassPropertyDefinition pd = |
| | | d.getJavaImplementationClassPropertyDefinition(); |
| | | // Load the class and cast it to a DebugLogPublisher. |
| | | Class<? extends DebugLogPublisher> theClass; |
| | | try { |
| | | theClass = pd.loadClass(className, DebugLogPublisher.class); |
| | | theClass.newInstance(); |
| | | } catch (Exception e) { |
| | | int msgID = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS; |
| | | String message = getMessage(msgID, className, |
| | | config.dn().toString(), |
| | | String.valueOf(e)); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | // Check that the implementation class implements the correct interface. |
| | | try { |
| | | // Determine the initialization method to use: it must take a |
| | | // single parameter which is the exact type of the configuration |
| | | // object. |
| | | theClass.getMethod("initializeDebugLogPublisher", config.definition() |
| | | .getServerConfigurationClass()); |
| | | } catch (Exception e) { |
| | | int msgID = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS; |
| | | String message = getMessage(msgID, className, |
| | | config.dn().toString(), |
| | | String.valueOf(e)); |
| | | unacceptableReasons.add(message); |
| | | return false; |
| | | } |
| | | // The class is valid as far as we can tell. |
| | | return true; |
| | | } |
| | | |
| | | private DebugLogPublisher getDebugPublisher(DebugLogPublisherCfg config) |
| | | throws ConfigException { |
| | | String className = config.getJavaImplementationClass(); |
| | | DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance(); |
| | | ClassPropertyDefinition pd = |
| | | d.getJavaImplementationClassPropertyDefinition(); |
| | | // Load the class and cast it to a DebugLogPublisher. |
| | | Class<? extends DebugLogPublisher> theClass; |
| | | DebugLogPublisher debugLogPublisher; |
| | | try { |
| | | theClass = pd.loadClass(className, DebugLogPublisher.class); |
| | | debugLogPublisher = theClass.newInstance(); |
| | | |
| | | // Determine the initialization method to use: it must take a |
| | | // single parameter which is the exact type of the configuration |
| | | // object. |
| | | Method method = theClass.getMethod("initializeDebugLogPublisher", |
| | | config.definition().getServerConfigurationClass()); |
| | | method.invoke(debugLogPublisher, config); |
| | | } |
| | | catch (InvocationTargetException ite) |
| | | { |
| | | // Rethrow the exceptions thrown be the invoked method. |
| | | Throwable e = ite.getTargetException(); |
| | | int msgID = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS; |
| | | String message = getMessage(msgID, className, |
| | | config.dn().toString(), |
| | | stackTraceToSingleLineString(e)); |
| | | throw new ConfigException(msgID, message, e); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | int msgID = MSGID_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS; |
| | | String message = getMessage(msgID, className, |
| | | config.dn().toString(), |
| | | String.valueOf(e)); |
| | | throw new ConfigException(msgID, message, e); |
| | | } |
| | | |
| | | // The debug publisher has been successfully initialized. |
| | | return debugLogPublisher; |
| | | } |
| | | |
| | | /** |
| | | * Adds the settings for the provided publisher in all existing tracers. |
| | | * If existing settings exist for the given publisher, it will be updated |
| | | * with the new settings. |
| | | * |
| | | * @param publisher The debug log publisher with the new settings. |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static void addTracerSettings(DebugLogPublisher publisher) |
| | | { |
| | | // Make sure this publisher is still registered with us. If not, don't |
| | | // use its settings. |
| | | if(debugPublishers.contains(publisher)) |
| | | { |
| | | for(DebugTracer tracer : classTracers) |
| | | { |
| | | tracer.classSettings.put(publisher, |
| | | publisher.getClassSettings(tracer.className)); |
| | | |
| | | // For some reason, the compiler doesn't see that |
| | | // debugLogPublihser.getMethodSettings returns a parameterized Map. |
| | | // This problem goes away if a parameterized verson of |
| | | // DebugLogPublisher is used. However, we can't not use reflection to |
| | | // instantiate a generic |
| | | // DebugLogPublisher<? extends DebugLogPublisherCfg> type. The only |
| | | // thing we can do is to just suppress the compiler warnings. |
| | | Map<String, TraceSettings> methodSettings = |
| | | publisher.getMethodSettings(tracer.className); |
| | | if(methodSettings != null) |
| | | { |
| | | tracer.methodSettings.put(publisher, methodSettings); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Removes the settings for the provided publisher in all existing tracers. |
| | | * |
| | | * @param publisher The debug log publisher to remove. |
| | | */ |
| | | public static void removeTracerSettings(DebugLogPublisher publisher) |
| | | { |
| | | for(DebugTracer tracer : classTracers) |
| | | { |
| | | tracer.classSettings.remove(publisher); |
| | | tracer.methodSettings.remove(publisher); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Indicates if debug logging is enabled. |
| | | * |
| | | * @return True if debug logging is enabled. False otherwise. |
| | | */ |
| | | public static boolean debugEnabled() |
| | | { |
| | | return staticEnabled; |
| | | return enabled; |
| | | } |
| | | |
| | | /** |
| | | * Register a trace logger for the specified class. |
| | | * @param className - the class for which to register the tracer under. |
| | | * @param tracer - the tracer object to register. |
| | | */ |
| | | public synchronized void registerTracer(String className, |
| | | Tracer tracer) |
| | | { |
| | | Tracer traceLogger = classTracers.get(className); |
| | | if (traceLogger == null) { |
| | | classTracers.put(className, tracer); |
| | | tracer.updateSettings(this.configuration); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Update the tracing configuration of the debug logger with the specified |
| | | * trace configuration. |
| | | * Retrieve the singleton instance of this class. |
| | | * |
| | | * @param config the new configuration to apply. |
| | | * @return The singleton instance of this logger. |
| | | */ |
| | | public synchronized void updateConfiguration(DebugConfiguration config) |
| | | public static DebugLogger getInstance() |
| | | { |
| | | super.updateConfiguration(config); |
| | | staticEnabled = enabled; |
| | | |
| | | for(Tracer tracer : classTracers.values()) |
| | | { |
| | | tracer.updateSettings(config); |
| | | } |
| | | |
| | | this.configuration = config; |
| | | return instance; |
| | | } |
| | | |
| | | /** |
| | |
| | | ProtocolElement element) {} |
| | | |
| | | |
| | | /** |
| | | * Classes and methods annotated with @NoDebugTracing will not be weaved with |
| | | * debug logging statements by AspectJ. |
| | | */ |
| | | public @interface NoDebugTracing {} |
| | | |
| | | /** |
| | | * Methods annotated with @NoEntryDebugTracing will not be weaved with |
| | | * entry debug logging statements by AspectJ. |
| | | */ |
| | | public @interface NoEntryDebugTracing {} |
| | | |
| | | /** |
| | | * Methods annotated with @NoExitDebugTracing will not be weaved with |
| | | * exit debug logging statements by AspectJ. |
| | | */ |
| | | public @interface NoExitDebugTracing {} |
| | | |
| | | /** |
| | | * Methods annotated with @TraceThrown will be weaved by AspectJ with |
| | | * debug logging statements when an exception is thrown from the method. |
| | | */ |
| | | public @interface TraceThrown {} |
| | | |
| | | } |