org.glassfish.admin.amx.util.AMXDebug Maven / Gradle / Ivy
Show all versions of payara-micro Show documentation
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2012 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 [2019] Payara Foundation and/or affiliates
package org.glassfish.admin.amx.util;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
Internal debug facility. For development use only.
Do not use this class; it is subject to arbitrary change.
AMX has some unique issues that make a separate debug
facility highly useful. These include:
- The Logging MBean cannot use System.out/err or logging
mechanisms to emit debugging information because it can
potentially cause infinite recursion (and thus stack overflow).
Without anothermechanism, debugging the Logging MBean would
be very difficult
-
AMX code has both client and server aspects, and the
client code runs in both environments. The client code
in particular can't necessarily assume there is any
logging infrastructure in place
- Debugging complex interactions of MBean invocations
over a remote connection which may also involve asynchronous
Notifications is complicated. This logging facility makes
it possible to selectively view specific entities in the
interactions that are occuring, on a fine-grained level,
something not possible with the server logging mechanism
-
There is reason and justification for considering debugging
code a separate issue than the log file that customers are
expected to use. Comingling the two has already led to
the log file becoming useless when logging is set to FINE[R][EST].
This issue becomes a serious problem when trying to diagnose
an issue; one is either stuck with inserting INFO or higher
logging messages, or using FINE and dealing with the vast
quantify of log messages emitted at that level. It simply
is unproductive, and discourages thorough testing.
Usage notes
AMXDebug associates a file with each identifier (typically a classname).
These files are located within the {@link #AMX_DEBUG_SUBDIR}
subdirectory within the directory specified by
System.getProperty( "user.home" ) unless the system property
{@link #AMX_DEBUG_DIR_SPROP} is specified.
All resulting AMXDebug output files
end in the suffix {@link #AMX_DEBUG_SUFFIX}.
This fine-grained approach makes it possible to "tail" just the
output from just the classes of interest,
something that is difficult or impossible otherwise.
AMXDebug is designed as a singleton. However, arbitrary identifiers
may be used to associate debugging output with a particular
output file. This allows fine-grained and selective debugging
of just the items of interest.
When debugging is off, overhead is minimal, because all debugging
calls are routed to "dev/null". The caller can also wrap
such calls such that they don't make it to AMXDebug at all.
The debug flag may be set via the system property
{@link #AMX_DEBUG_ENABLED_SPROP}. Debugging will be enabled if that
property has value "true". Otherwise, it is disabled.
Debugging may also be programmatically enabled, on a per-ID
basis.
The expected usage is per-class and the classname can generally
be used as the identifier. However, usage include other
patterns; anything that the emitting code can agree on, regardless
of whether it is in the same class, or spread across many. One
possibility would be to place the Output into the thread context.
There are other possibilities.
Output may be marked using the {@link #mark} and {@link #markAll} routines.
This aids in visually organizing the output.
For more information, see the javadoc on individual routines.
*/
public final class AMXDebug
{
private final ConcurrentMap mOutputs;
private static final AMXDebug INSTANCE = new AMXDebug();
private final File mDir;
private boolean mMadeDebugDir;
private boolean mDefaultDebug;
private final boolean mAppend;
/** the key for the system property to enable AMX debug facility */
public static final String AMX_DEBUG_ENABLED_SPROP = "AMX-DEBUG.enabled";
/** the key for the system property to append to debug files.
Otherwise they are overwritten each time
*/
public static final String AMX_DEBUG_APPEND_SPROP = "AMX-DEBUG.append";
/**
The key for the system property to specify a different AMX_DEBUG_DIR.
This value is uninterpreted--the result from
new File( System.getProperty( {@link #AMX_DEBUG_SUBDIR} ) is used directly.
If the sytem property {@link #AMX_DEBUG_SUBDIR} is not specified,
then AMXDebug looks for the system property
"com.sun.aas.instanceRoot". If that system property
is not found, then "user.home" is used. The result of this is
the "parent dir". The resulting output
directory is then /{@link #AMX_DEBUG_SUBDIR}.
*/
public static final String AMX_DEBUG_DIR_SPROP = "AMX-DEBUG.dir";
/**
The name of the default subdirectory which contains
the ".debug" files created by AMXDebug. This is the directory
used if {@link #AMX_DEBUG_DIR_SPROP} is not specified.
*/
public static final String AMX_DEBUG_SUBDIR = "AMX-DEBUG";
/**
Suffix used on all Output files.
*/
public static final String AMX_DEBUG_SUFFIX = ".debug";
// Output for AMXDebug itself
private final WrapOutput mDebug;
private final String NEWLINE;
private final Set ILLEGAL_CHARS;
private final char[] ILLEGAL_CHARS_ARRAY =
{
'\u0000',
'?', '*', '|', '\'', '|', '\\', '/', ':'
};
private AMXDebug()
{
ILLEGAL_CHARS = new HashSet();
for (final char c : ILLEGAL_CHARS_ARRAY)
{
ILLEGAL_CHARS.add(c);
}
NEWLINE = System.lineSeparator();
assert (NEWLINE != null);
String value = System.getProperty(AMX_DEBUG_ENABLED_SPROP);
if (value == null)
{
// not the right one, but a common mistake.
value = System.getProperty("AMX-DEBUG");
if (value != null && value.equals(""))
{
value = "true";
}
}
mDefaultDebug = (value != null) && Boolean.parseBoolean(value);
value = System.getProperty(AMX_DEBUG_APPEND_SPROP);
mAppend = (value != null) && Boolean.parseBoolean(value);
mOutputs = new ConcurrentHashMap();
mDir = getDir();
mMadeDebugDir = false;
if (mDefaultDebug)
{
makeDebugDir();
}
mDebug = _getOutput(this.getClass().getName());
mark(mDebug, getStdMarker("AMXDebug started "));
mDebug.println("*** System Properties ***");
dumpSystemProps();
mark(mDebug, getStdMarker("AMXDebug initialization done"));
}
private void dumpSystemProps() {
final java.util.Properties props = System.getProperties();
Set