Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2008-2024 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright 2008-2024 Ping Identity Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2008-2024 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.util;
import java.io.File;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.EnumSet;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.unboundid.asn1.ASN1Buffer;
import com.unboundid.asn1.ASN1Element;
import com.unboundid.ldap.protocol.LDAPResponse;
import com.unboundid.ldap.sdk.AbstractConnectionPool;
import com.unboundid.ldap.sdk.DisconnectType;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.InternalSDKHelper;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPRequest;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.ldif.LDIFRecord;
import com.unboundid.util.json.JSONBuffer;
import static com.unboundid.util.UtilityMessages.*;
/**
* This class provides a means of enabling and configuring debugging in the LDAP
* SDK.
*
* Access to debug information can be enabled through applications that use the
* SDK by calling the {@link Debug#setEnabled} methods, or it can also be
* enabled without any code changes through the use of system properties. In
* particular, the {@link Debug#PROPERTY_DEBUG_ENABLED},
* {@link Debug#PROPERTY_DEBUG_LEVEL}, and {@link Debug#PROPERTY_DEBUG_TYPE}
* properties may be used to control debugging without the need to alter any
* code within the application that uses the SDK.
*
* The LDAP SDK debugging subsystem uses the Java logging framework available
* through the {@code java.util.logging} package with a logger name of
* "{@code com.unboundid.ldap.sdk}". The {@link Debug#getLogger} method may
* be used to access the logger instance used by the LDAP SDK.
*
*
Example
* The following example demonstrates the process that may be used to enable
* debugging within the LDAP SDK and write information about all messages with
* a {@code WARNING} level or higher to a specified file:
*
*/
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class Debug
implements Serializable
{
/**
* The name of the system property that will be used to enable debugging in
* the UnboundID LDAP SDK for Java. The fully-qualified name for this
* property is "{@code com.unboundid.ldap.sdk.debug.enabled}". If it is set,
* then it should have a value of either "true" or "false".
*/
@NotNull public static final String PROPERTY_DEBUG_ENABLED =
"com.unboundid.ldap.sdk.debug.enabled";
/**
* The name of the system property that may be used to indicate whether stack
* trace information for the thread calling the debug method should be
* included in debug log messages. The fully-qualified name for this property
* is "{@code com.unboundid.ldap.sdk.debug.includeStackTrace}". If it is set,
* then it should have a value of either "true" or "false".
*/
@NotNull public static final String PROPERTY_INCLUDE_STACK_TRACE =
"com.unboundid.ldap.sdk.debug.includeStackTrace";
/**
* The name of the system property that will be used to set the initial level
* for the debug logger. The fully-qualified name for this property is
* "{@code com.unboundid.ldap.sdk.debug.level}". If it is set, then it should
* be one of the strings "{@code SEVERE}", "{@code WARNING}", "{@code INFO}",
* "{@code CONFIG}", "{@code FINE}", "{@code FINER}", or "{@code FINEST}".
*/
@NotNull public static final String PROPERTY_DEBUG_LEVEL =
"com.unboundid.ldap.sdk.debug.level";
/**
* The name of the system property that will be used to indicate that
* debugging should be enabled for specific types of messages. The
* fully-qualified name for this property is
* "{@code com.unboundid.ldap.sdk.debug.type}". If it is set, then it should
* be a comma-delimited list of the names of the desired debug types. See the
* {@link DebugType} enum for the available debug types.
*/
@NotNull public static final String PROPERTY_DEBUG_TYPE =
"com.unboundid.ldap.sdk.debug.type";
/**
* The name of the system property that will be used to indicate whether the
* LDAP SDK should default to including information about the exception's
* cause in an exception message obtained from the
* {@link StaticUtils#getExceptionMessage(Throwable)} method. By default,
* the cause will not be included in most messages.
*/
@NotNull public static final String
PROPERTY_INCLUDE_CAUSE_IN_EXCEPTION_MESSAGES =
"com.unboundid.ldap.sdk.debug.includeCauseInExceptionMessages";
/**
* The name of the system property that will be used to indicate whether the
* LDAP SDK should default to including a full stack trace (albeit in
* condensed form) in an exception message obtained from the
* {@link StaticUtils#getExceptionMessage(Throwable)} method. By default,
* stack traces will not be included in most messages.
*/
@NotNull public static final String
PROPERTY_INCLUDE_STACK_TRACE_IN_EXCEPTION_MESSAGES =
"com.unboundid.ldap.sdk.debug.includeStackTraceInExceptionMessages";
/**
* The name of the system property that will be used ot indicate whether
* debug messages (which will be formatted as JSON objects) should be
* generated using a multi-line string representation. By default, debug
* message will use a single-line string representation.
*/
@NotNull public static final String PROPERTY_USE_MULTI_LINE_DEBUG_MESSAGES =
"com.unboundid.ldap.sdk.debug.useMultiLineDebugMessages";
/**
* The name of the system property that will be used to indicate that debug
* log messages should be written to the specified file. The fully-qualified
* name for this property is "{@code com.unboundid.ldap.sdk.debug.file}". If
* it is set, then its value should be a pattern that describes the path to
* the log file to create as described in the Javadoc documentation for the
* {@code java.util.logging.FileHandler} class.
*/
@NotNull public static final String PROPERTY_DEBUG_FILE =
"com.unboundid.ldap.sdk.debug.file";
/**
* The name that will be used for the Java logger that will actually handle
* the debug messages if debugging is enabled.
*/
@NotNull public static final String LOGGER_NAME = "com.unboundid.ldap.sdk";
/**
* The logger that will be used to handle the debug messages if debugging is
* enabled.
*/
@NotNull private static final Logger logger = Logger.getLogger(LOGGER_NAME);
/**
* A set of thread-local formatters that may be used to generate timestamps.
*/
@NotNull private static final ThreadLocal
TIMESTAMP_FORMATTERS = new ThreadLocal<>();
/**
* The serial version UID for this serializable class.
*/
private static final long serialVersionUID = -6079754380415146030L;
// Indicates whether any debugging is currently enabled for the SDK.
private static boolean debugEnabled;
// Indicates whether to capture a thread stack trace whenever a debug message
// is logged.
private static boolean includeStackTrace;
// Indiates whether debug messages should be formatted using multiple lines.
private static boolean useMultiLineDebugMessages;
// The set of debug types for which debugging is enabled.
@NotNull private static EnumSet debugTypes=
EnumSet.allOf(DebugType.class);
static
{
initialize(StaticUtils.getSystemProperties(PROPERTY_DEBUG_ENABLED,
PROPERTY_DEBUG_LEVEL, PROPERTY_DEBUG_TYPE,
PROPERTY_INCLUDE_STACK_TRACE));
final String logFilePropertyValue =
StaticUtils.getSystemProperty(PROPERTY_DEBUG_FILE);
if (logFilePropertyValue != null)
{
try
{
logger.setUseParentHandlers(false);
final FileHandler fileHandler =
new FileHandler(logFilePropertyValue, true);
fileHandler.setFormatter(
new MinimalLogFormatter(null, false, false, true));
logger.addHandler(fileHandler);
}
catch (final Exception e)
{
throw new RuntimeException(e);
}
}
}
/**
* Prevent this class from being instantiated.
*/
private Debug()
{
// No implementation is required.
}
/**
* Initializes this debugger with the default settings. Debugging will be
* disabled, the set of debug types will include all types, and the debug
* level will be "ALL".
*/
public static void initialize()
{
includeStackTrace = false;
useMultiLineDebugMessages = false;
debugEnabled = false;
debugTypes = EnumSet.allOf(DebugType.class);
StaticUtils.setLoggerLevel(logger, Level.ALL);
}
/**
* Initializes this debugger with settings from the provided set of
* properties. Any debug setting that isn't configured in the provided
* properties will be initialized with its default value.
*
* @param properties The set of properties to use to initialize this
* debugger.
*/
public static void initialize(@Nullable final Properties properties)
{
// First, apply the default values for the properties.
initialize();
if ((properties == null) || properties.isEmpty())
{
// No properties were provided, so we don't need to do anything.
return;
}
final String enabledProp = properties.getProperty(PROPERTY_DEBUG_ENABLED);
if ((enabledProp != null) && (! enabledProp.isEmpty()))
{
if (enabledProp.equalsIgnoreCase("true"))
{
debugEnabled = true;
}
else if (enabledProp.equalsIgnoreCase("false"))
{
debugEnabled = false;
}
else
{
throw new IllegalArgumentException("Invalid value '" + enabledProp +
"' for property " + PROPERTY_DEBUG_ENABLED +
". The value must be either 'true' or 'false'.");
}
}
final String stackProp =
properties.getProperty(PROPERTY_INCLUDE_STACK_TRACE);
if ((stackProp != null) && (! stackProp.isEmpty()))
{
if (stackProp.equalsIgnoreCase("true"))
{
includeStackTrace = true;
}
else if (stackProp.equalsIgnoreCase("false"))
{
includeStackTrace = false;
}
else
{
throw new IllegalArgumentException("Invalid value '" + stackProp +
"' for property " + PROPERTY_INCLUDE_STACK_TRACE +
". The value must be either 'true' or 'false'.");
}
}
final String multiLineProp =
properties.getProperty(PROPERTY_USE_MULTI_LINE_DEBUG_MESSAGES);
if ((multiLineProp != null) && (! multiLineProp.isEmpty()))
{
if (multiLineProp.equalsIgnoreCase("true"))
{
useMultiLineDebugMessages = true;
}
else if (multiLineProp.equalsIgnoreCase("false"))
{
useMultiLineDebugMessages = false;
}
else
{
throw new IllegalArgumentException("Invalid value '" + multiLineProp +
"' for property " + PROPERTY_USE_MULTI_LINE_DEBUG_MESSAGES +
". The value must be either 'true' or 'false'.");
}
}
final String typesProp = properties.getProperty(PROPERTY_DEBUG_TYPE);
if ((typesProp != null) && (! typesProp.isEmpty()))
{
debugTypes = EnumSet.noneOf(DebugType.class);
final StringTokenizer t = new StringTokenizer(typesProp, ", ");
while (t.hasMoreTokens())
{
final String debugTypeName = t.nextToken();
final DebugType debugType = DebugType.forName(debugTypeName);
if (debugType == null)
{
// Throw a runtime exception to indicate that the debug type is
// invalid.
throw new IllegalArgumentException("Invalid value '" + debugTypeName +
"' for property " + PROPERTY_DEBUG_TYPE +
". Allowed values include: " + DebugType.getTypeNameList() +
'.');
}
else
{
debugTypes.add(debugType);
}
}
}
final String levelProp = properties.getProperty(PROPERTY_DEBUG_LEVEL);
if ((levelProp != null) && (! levelProp.isEmpty()))
{
try
{
StaticUtils.setLoggerLevel(logger, parseDebugLogLevel(levelProp));
}
catch (final IllegalArgumentException e)
{
throw new IllegalArgumentException(
"Invalid value '" + levelProp + "' for property " +
PROPERTY_DEBUG_LEVEL + '.',
e);
}
}
}
/**
* Retrieves the logger that will be used to write the debug messages.
*
* @return The logger that will be used to write the debug messages.
*/
@NotNull()
public static Logger getLogger()
{
return logger;
}
/**
* Indicates whether any form of debugging is enabled.
*
* @return {@code true} if debugging is enabled, or {@code false} if not.
*/
public static boolean debugEnabled()
{
return debugEnabled;
}
/**
* Indicates whether debugging is enabled for messages of the specified debug
* type.
*
* @param debugType The debug type for which to make the determination.
*
* @return {@code true} if debugging is enabled for messages of the specified
* debug type, or {@code false} if not.
*/
public static boolean debugEnabled(@NotNull final DebugType debugType)
{
return (debugEnabled && debugTypes.contains(debugType));
}
/**
* Specifies whether debugging should be enabled. If it should be, then it
* will be enabled for all debug types.
*
* @param enabled Specifies whether debugging should be enabled.
*/
public static void setEnabled(final boolean enabled)
{
debugTypes = EnumSet.allOf(DebugType.class);
debugEnabled = enabled;
}
/**
* Specifies whether debugging should be enabled. If it should be, then it
* will be enabled for all debug types in the provided set.
*
* @param enabled Specifies whether debugging should be enabled.
* @param types The set of debug types that should be enabled. It may be
* {@code null} or empty to indicate that it should be for
* all debug types.
*/
public static void setEnabled(final boolean enabled,
@Nullable final Set types)
{
if ((types == null) || types.isEmpty())
{
debugTypes = EnumSet.allOf(DebugType.class);
}
else
{
debugTypes = EnumSet.copyOf(types);
}
debugEnabled = enabled;
}
/**
* Indicates whether log messages should include a stack trace of the thread
* that invoked the debug method.
*
* @return {@code true} if log messages should include a stack trace of the
* thread that invoked the debug method, or {@code false} if not.
*/
public static boolean includeStackTrace()
{
return includeStackTrace;
}
/**
* Specifies whether log messages should include a stack trace of the thread
* that invoked the debug method.
*
* @param includeStackTrace Indicates whether log messages should include a
* stack trace of the thread that invoked the debug
* method.
*/
public static void setIncludeStackTrace(final boolean includeStackTrace)
{
Debug.includeStackTrace = includeStackTrace;
}
/**
* Indicates whether debug messages (which will be formatted as JSON objects)
* should use a multi-line or single-line string representation.
*
* @return {@code true} if debug messages should be formatted as multi-line
* strings, or {@code false} if debug messages should be formatted as
* single-line strings.
*/
public static boolean useMultiLineDebugMessages()
{
return useMultiLineDebugMessages;
}
/**
* Specifies whether debug messages (which will be formatted as JSON objects)
* should use a multi-line or single-line string representation.
*
* @param useMultiLineDebugMessages Indicates whether debug messages
* should be formatted as multi-line
* strings (if {@code true}) or single-line
* strings (if {@code false}).
*/
public static void setUseMultiLineDebugMessages(
final boolean useMultiLineDebugMessages)
{
Debug.useMultiLineDebugMessages = useMultiLineDebugMessages;
}
/**
* Retrieves the set of debug types that will be used if debugging is enabled.
*
* @return The set of debug types that will be used if debugging is enabled.
*/
@NotNull()
public static EnumSet getDebugTypes()
{
return debugTypes;
}
/**
* Writes debug information about the provided exception, if appropriate. If
* it is to be logged, then it will be sent to the underlying logger using the
* {@code WARNING} level.
*
* @param t The exception for which debug information should be written.
*/
public static void debugException(@NotNull final Throwable t)
{
if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION))
{
debugException(Level.WARNING, t);
}
}
/**
* Writes debug information about the provided exception, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param t The exception for which debug information should be written.
*/
public static void debugException(@NotNull final Level l,
@NotNull final Throwable t)
{
if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.EXCEPTION);
addCaughtException(buffer, "caught-exception", t);
addCommonFooter(buffer);
log(l, buffer, t);
}
}
/**
* Writes debug information to indicate that a connection has been
* established, if appropriate. If it is to be logged, then it will be sent
* to the underlying logger using the {@code INFO} level.
*
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
*/
public static void debugConnect(@NotNull final String h, final int p)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
debugConnect(Level.INFO, h, p, null);
}
}
/**
* Writes debug information to indicate that a connection has been
* established, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
*/
public static void debugConnect(@NotNull final Level l,
@NotNull final String h, final int p)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
debugConnect(l, h, p, null);
}
}
/**
* Writes debug information to indicate that a connection has been
* established, if appropriate. If it is to be logged, then it will be sent
* to the underlying logger using the {@code INFO} level.
*
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
* @param c The connection object for the connection that has been
* established. It may be {@code null} for historic reasons, but
* should be non-{@code null} in new uses.
*/
public static void debugConnect(@NotNull final String h, final int p,
@Nullable final LDAPConnection c)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
debugConnect(Level.INFO, h, p, c);
}
}
/**
* Writes debug information to indicate that a connection has been
* established, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
* @param c The connection object for the connection that has been
* established. It may be {@code null} for historic reasons, but
* should be non-{@code null} in new uses.
*/
public static void debugConnect(@NotNull final Level l,
@NotNull final String h, final int p,
@Nullable final LDAPConnection c)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.CONNECT);
buffer.appendString("connected-to-address", h);
buffer.appendNumber("connected-to-port", p);
if (c != null)
{
buffer.appendNumber("connection-id", c.getConnectionID());
final String connectionName = c.getConnectionName();
if (connectionName != null)
{
buffer.appendString("connection-name", connectionName);
}
final String connectionPoolName = c.getConnectionPoolName();
if (connectionPoolName != null)
{
buffer.appendString("connection-pool-name", connectionPoolName);
}
}
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information to indicate that a connection has been
* terminated, if appropriate. If it is to be logged, then it will be sent
* to the underlying logger using the {@code INFO} level.
*
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
* @param t The disconnect type.
* @param m The disconnect message, if available.
* @param e The disconnect cause, if available.
*/
public static void debugDisconnect(@NotNull final String h,
final int p,
@NotNull final DisconnectType t,
@Nullable final String m,
@Nullable final Throwable e)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
debugDisconnect(Level.INFO, h, p, null, t, m, e);
}
}
/**
* Writes debug information to indicate that a connection has been
* terminated, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
* @param t The disconnect type.
* @param m The disconnect message, if available.
* @param e The disconnect cause, if available.
*/
public static void debugDisconnect(@NotNull final Level l,
@NotNull final String h, final int p,
@NotNull final DisconnectType t,
@Nullable final String m,
@Nullable final Throwable e)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
debugDisconnect(l, h, p, null, t, m, e);
}
}
/**
* Writes debug information to indicate that a connection has been
* terminated, if appropriate. If it is to be logged, then it will be sent
* to the underlying logger using the {@code INFO} level.
*
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
* @param c The connection object for the connection that has been closed.
* It may be {@code null} for historic reasons, but should be
* non-{@code null} in new uses.
* @param t The disconnect type.
* @param m The disconnect message, if available.
* @param e The disconnect cause, if available.
*/
public static void debugDisconnect(@NotNull final String h, final int p,
@Nullable final LDAPConnection c,
@NotNull final DisconnectType t,
@Nullable final String m,
@Nullable final Throwable e)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
debugDisconnect(Level.INFO, h, p, c, t, m, e);
}
}
/**
* Writes debug information to indicate that a connection has been
* terminated, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param h The address of the server to which the connection was
* established.
* @param p The port of the server to which the connection was established.
* @param c The connection object for the connection that has been closed.
* It may be {@code null} for historic reasons, but should be
* non-{@code null} in new uses.
* @param t The disconnect type.
* @param m The disconnect message, if available.
* @param e The disconnect cause, if available.
*/
public static void debugDisconnect(@NotNull final Level l,
@NotNull final String h, final int p,
@Nullable final LDAPConnection c,
@NotNull final DisconnectType t,
@Nullable final String m,
@Nullable final Throwable e)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.CONNECT);
if (c != null)
{
buffer.appendNumber("connection-id", c.getConnectionID());
final String connectionName = c.getConnectionName();
if (connectionName != null)
{
buffer.appendString("connection-name", connectionName);
}
final String connectionPoolName = c.getConnectionPoolName();
if (connectionPoolName != null)
{
buffer.appendString("connection-pool-name", connectionPoolName);
}
buffer.appendString("disconnected-from-address", h);
buffer.appendNumber("disconnected-from-port", p);
buffer.appendString("disconnect-type", t.name());
if (m != null)
{
buffer.appendString("disconnect-message", m);
}
}
if (e != null)
{
addCaughtException(buffer, "disconnect-cause", e);
}
addCommonFooter(buffer);
log(l, buffer, e);
}
}
/**
* Writes debug information about the provided request, if appropriate. If
* it is to be logged, then it will be sent to the underlying logger using the
* {@code INFO} level.
*
* @param r The LDAP request for which debug information should be written.
*/
public static void debugLDAPRequest(@NotNull final LDAPRequest r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
debugLDAPRequest(Level.INFO, r, -1, null);
}
}
/**
* Writes debug information about the provided request, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param r The LDAP request for which debug information should be written.
*/
public static void debugLDAPRequest(@NotNull final Level l,
@NotNull final LDAPRequest r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
debugLDAPRequest(l, r, -1, null);
}
}
/**
* Writes debug information about the provided request, if appropriate. If
* it is to be logged, then it will be sent to the underlying logger using the
* {@code INFO} level.
*
* @param r The LDAP request for which debug information should be written.
* @param i The message ID for the request that will be sent. It may be
* negative if no message ID is available.
* @param c The connection on which the request will be sent. It may be
* {@code null} for historic reasons, but should be
* non-{@code null} in new uses.
*/
public static void debugLDAPRequest(@NotNull final LDAPRequest r, final int i,
@Nullable final LDAPConnection c)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
debugLDAPRequest(Level.INFO, r, i, c);
}
}
/**
* Writes debug information about the provided request, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param r The LDAP request for which debug information should be written.
* @param i The message ID for the request that will be sent. It may be
* negative if no message ID is available.
* @param c The connection on which the request will be sent. It may be
* {@code null} for historic reasons, but should be
* non-{@code null} in new uses.
*/
public static void debugLDAPRequest(@NotNull final Level l,
@NotNull final LDAPRequest r,
final int i,
@Nullable final LDAPConnection c)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
debugLDAPRequest(l, String.valueOf(r), i, c);
}
}
/**
* Writes debug information about the provided request, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param s A string representation of the LDAP request for which debug
* information should be written.
* @param i The message ID for the request that will be sent. It may be
* negative if no message ID is available.
* @param c The connection on which the request will be sent. It may be
* {@code null} for historic reasons, but should be
* non-{@code null} in new uses.
*/
public static void debugLDAPRequest(@NotNull final Level l,
@NotNull final String s,
final int i,
@Nullable final LDAPConnection c)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.LDAP);
if (c != null)
{
buffer.appendNumber("connection-id", c.getConnectionID());
final String connectionName = c.getConnectionName();
if (connectionName != null)
{
buffer.appendString("connection-name", connectionName);
}
final String connectionPoolName = c.getConnectionPoolName();
if (connectionPoolName != null)
{
buffer.appendString("connection-pool-name", connectionPoolName);
}
final String connectedAddress = c.getConnectedAddress();
if (connectedAddress != null)
{
buffer.appendString("connected-to-address", connectedAddress);
buffer.appendNumber("connected-to-port", c.getConnectedPort());
}
try
{
final int soTimeout = InternalSDKHelper.getSoTimeout(c);
buffer.appendNumber("socket-timeout-millis", soTimeout);
} catch (final Exception e) {}
}
if (i >= 0)
{
buffer.appendNumber("message-id", i);
}
buffer.appendString("sending-ldap-request", s);
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about the provided result, if appropriate. If
* it is to be logged, then it will be sent to the underlying logger using the
* {@code INFO} level.
*
* @param r The result for which debug information should be written.
*/
public static void debugLDAPResult(@NotNull final LDAPResponse r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
debugLDAPResult(Level.INFO, r, null);
}
}
/**
* Writes debug information about the provided result, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param r The result for which debug information should be written.
*/
public static void debugLDAPResult(@NotNull final Level l,
@NotNull final LDAPResponse r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
debugLDAPResult(l, r, null);
}
}
/**
* Writes debug information about the provided result, if appropriate. If
* it is to be logged, then it will be sent to the underlying logger using the
* {@code INFO} level.
*
* @param r The result for which debug information should be written.
* @param c The connection on which the response was received. It may be
* {@code null} for historic reasons, but should be
* non-{@code null} in new uses.
*/
public static void debugLDAPResult(@NotNull final LDAPResponse r,
@Nullable final LDAPConnection c)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
debugLDAPResult(Level.INFO, r, c);
}
}
/**
* Writes debug information about the provided result, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param r The result for which debug information should be written.
* @param c The connection on which the response was received. It may be
* {@code null} for historic reasons, but should be
* non-{@code null} in new uses.
*/
public static void debugLDAPResult(@NotNull final Level l,
@NotNull final LDAPResponse r,
@Nullable final LDAPConnection c)
{
if (debugEnabled && debugTypes.contains(DebugType.LDAP))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.LDAP);
if (c != null)
{
buffer.appendNumber("connection-id", c.getConnectionID());
final String connectionName = c.getConnectionName();
if (connectionName != null)
{
buffer.appendString("connection-name", connectionName);
}
final String connectionPoolName = c.getConnectionPoolName();
if (connectionPoolName != null)
{
buffer.appendString("connection-pool-name", connectionPoolName);
}
final String connectedAddress = c.getConnectedAddress();
if (connectedAddress != null)
{
buffer.appendString("connected-to-address", connectedAddress);
buffer.appendNumber("connected-to-port", c.getConnectedPort());
}
}
buffer.appendString("read-ldap-result", r.toString());
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about the provided ASN.1 element to be written,
* if appropriate. If it is to be logged, then it will be sent to the
* underlying logger using the {@code INFO} level.
*
* @param e The ASN.1 element for which debug information should be written.
*/
public static void debugASN1Write(@NotNull final ASN1Element e)
{
if (debugEnabled && debugTypes.contains(DebugType.ASN1))
{
debugASN1Write(Level.INFO, e);
}
}
/**
* Writes debug information about the provided ASN.1 element to be written,
* if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param e The ASN.1 element for which debug information should be written.
*/
public static void debugASN1Write(@NotNull final Level l,
@NotNull final ASN1Element e)
{
if (debugEnabled && debugTypes.contains(DebugType.ASN1))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.ASN1);
buffer.appendString("writing-asn1-element", e.toString());
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about the provided ASN.1 element to be written,
* if appropriate. If it is to be logged, then it will be sent to the
* underlying logger using the {@code INFO} level.
*
* @param b The ASN.1 buffer with the information to be written.
*/
public static void debugASN1Write(@NotNull final ASN1Buffer b)
{
if (debugEnabled && debugTypes.contains(DebugType.ASN1))
{
debugASN1Write(Level.INFO, b);
}
}
/**
* Writes debug information about the provided ASN.1 element to be written,
* if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param b The ASN1Buffer with the information to be written.
*/
public static void debugASN1Write(@NotNull final Level l,
@NotNull final ASN1Buffer b)
{
if (debugEnabled && debugTypes.contains(DebugType.ASN1))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.ASN1);
buffer.appendString("writing-asn1-element",
StaticUtils.toHex(b.toByteArray()));
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about the provided ASN.1 element that was read, if
* appropriate. If it is to be logged, then it will be sent to the underlying
* logger using the {@code INFO} level.
*
* @param e The ASN.1 element for which debug information should be written.
*/
public static void debugASN1Read(@NotNull final ASN1Element e)
{
if (debugEnabled && debugTypes.contains(DebugType.ASN1))
{
debugASN1Read(Level.INFO, e);
}
}
/**
* Writes debug information about the provided ASN.1 element that was read, if
* appropriate.
*
* @param l The log level that should be used for the debug information.
* @param e The ASN.1 element for which debug information should be written.
*/
public static void debugASN1Read(@NotNull final Level l,
@NotNull final ASN1Element e)
{
if (debugEnabled && debugTypes.contains(DebugType.ASN1))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.ASN1);
buffer.appendString("read-asn1-element", e.toString());
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about the provided ASN.1 element that was read, if
* appropriate.
*
* @param l The log level that should be used for the debug
* information.
* @param dataType A string representation of the data type for the data
* that was read.
* @param berType The BER type for the element that was read.
* @param length The number of bytes in the value of the element that was
* read.
* @param value A representation of the value that was read. The debug
* message will include the string representation of this
* value, unless the value is a byte array in which it will
* be a hex representation of the bytes that it contains.
* It may be {@code null} for an ASN.1 null element.
*/
public static void debugASN1Read(@NotNull final Level l,
@NotNull final String dataType,
final int berType, final int length,
@Nullable final Object value)
{
if (debugEnabled && debugTypes.contains(DebugType.ASN1))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.ASN1);
buffer.beginObject("read-asn1-element");
buffer.appendString("data-type", dataType);
buffer.appendString("ber-type",
StaticUtils.toHex((byte) (berType & 0xFF)));
buffer.appendNumber("value-length", length);
if (value != null)
{
if (value instanceof byte[])
{
buffer.appendString("value-bytes",
StaticUtils.toHex((byte[]) value));
}
else
{
buffer.appendString("value-string", value.toString());
}
}
buffer.endObject();
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about interaction with a connection pool.
*
* @param l The log level that should be used for the debug information.
* @param p The associated connection pool.
* @param c The associated LDAP connection, if appropriate.
* @param m A message with information about the pool interaction.
* @param e An exception to include with the log message, if appropriate.
*/
public static void debugConnectionPool(@NotNull final Level l,
@NotNull final AbstractConnectionPool p,
@Nullable final LDAPConnection c,
@Nullable final String m, @Nullable final Throwable e)
{
if (debugEnabled && debugTypes.contains(DebugType.CONNECTION_POOL))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.CONNECTION_POOL);
final String poolName = p.getConnectionPoolName();
if (poolName == null)
{
buffer.appendNull("connection-pool-name");
}
else
{
buffer.appendString("connection-pool-name", poolName);
}
if (c != null)
{
buffer.appendNumber("connection-id", c.getConnectionID());
final String connectedAddress = c.getConnectedAddress();
if (connectedAddress != null)
{
buffer.appendString("connected-to-address", connectedAddress);
buffer.appendNumber("connected-to-port", c.getConnectedPort());
}
}
final long currentAvailable = p.getCurrentAvailableConnections();
if (currentAvailable >= 0)
{
buffer.appendNumber("current-available-connections", currentAvailable);
}
final long maxAvailable = p.getMaximumAvailableConnections();
if (maxAvailable >= 0)
{
buffer.appendNumber("maximum-available-connections", maxAvailable);
}
if (m != null)
{
buffer.appendString("message", m);
}
if (e != null)
{
addCaughtException(buffer, "caught-exception", e);
}
addCommonFooter(buffer);
log(l, buffer, e);
}
}
/**
* Writes debug information about the provided LDIF record to be written, if
* if appropriate. If it is to be logged, then it will be sent to the
* underlying logger using the {@code INFO} level.
*
* @param r The LDIF record for which debug information should be written.
*/
public static void debugLDIFWrite(@NotNull final LDIFRecord r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDIF))
{
debugLDIFWrite(Level.INFO, r);
}
}
/**
* Writes debug information about the provided LDIF record to be written, if
* appropriate.
*
* @param l The log level that should be used for the debug information.
* @param r The LDIF record for which debug information should be written.
*/
public static void debugLDIFWrite(@NotNull final Level l,
@NotNull final LDIFRecord r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDIF))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.LDIF);
buffer.appendString("writing-ldif-record", r.toString());
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about the provided record read from LDIF, if
* appropriate. If it is to be logged, then it will be sent to the underlying
* logger using the {@code INFO} level.
*
* @param r The LDIF record for which debug information should be written.
*/
public static void debugLDIFRead(@NotNull final LDIFRecord r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDIF))
{
debugLDIFRead(Level.INFO, r);
}
}
/**
* Writes debug information about the provided record read from LDIF, if
* appropriate.
*
* @param l The log level that should be used for the debug information.
* @param r The LDIF record for which debug information should be written.
*/
public static void debugLDIFRead(@NotNull final Level l,
@NotNull final LDIFRecord r)
{
if (debugEnabled && debugTypes.contains(DebugType.LDIF))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.LDIF);
buffer.appendString("read-ldif-record", r.toString());
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about monitor entry parsing. If it is to be
* logged, then it will be sent to the underlying logger using the
* {@code FINE} level.
*
* @param e The entry containing the monitor information being parsed.
* @param m The message to be written to the debug logger.
*/
public static void debugMonitor(@Nullable final Entry e,
@Nullable final String m)
{
if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
{
debugMonitor(Level.FINE, e, m);
}
}
/**
* Writes debug information about monitor entry parsing, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param e The entry containing the monitor information being parsed.
* @param m The message to be written to the debug logger.
*/
public static void debugMonitor(@NotNull final Level l,
@Nullable final Entry e,
@Nullable final String m)
{
if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, DebugType.MONITOR);
if (e != null)
{
buffer.appendString("monitor-entry-dn", e.getDN());
}
if (m != null)
{
buffer.appendString("message", m);
}
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes debug information about a coding error detected in the use of the
* LDAP SDK. If it is to be logged, then it will be sent to the underlying
* logger using the {@code SEVERE} level.
*
* @param t The {@code Throwable} object that was created and will be thrown
* as a result of the coding error.
*/
public static void debugCodingError(@NotNull final Throwable t)
{
if (debugEnabled && debugTypes.contains(DebugType.CODING_ERROR))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, Level.SEVERE, DebugType.CODING_ERROR);
addCaughtException(buffer, "coding-error", t);
addCommonFooter(buffer);
log(Level.SEVERE, buffer, t);
}
}
/**
* Writes a generic debug message, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param t The debug type to use to determine whether to write the message.
* @param m The message to be written.
*/
public static void debug(@NotNull final Level l,
@NotNull final DebugType t, @Nullable final String m)
{
if (debugEnabled && debugTypes.contains(t))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, t);
if (m != null)
{
buffer.appendString("message", m);
}
addCommonFooter(buffer);
log(l, buffer);
}
}
/**
* Writes a generic debug message, if appropriate.
*
* @param l The log level that should be used for the debug information.
* @param t The debug type to use to determine whether to write the message.
* @param m The message to be written.
* @param e An exception to include with the log message.
*/
public static void debug(@NotNull final Level l, @NotNull final DebugType t,
@Nullable final String m,
@Nullable final Throwable e)
{
if (debugEnabled && debugTypes.contains(t))
{
final JSONBuffer buffer = new JSONBuffer(
null, JSONBuffer.DEFAULT_MAX_BUFFER_SIZE, useMultiLineDebugMessages);
addCommonHeader(buffer, l, t);
if (m != null)
{
buffer.appendString("message", m);
}
if (e != null)
{
addCaughtException(buffer, "caught-exception", e);
}
addCommonFooter(buffer);
log(l, buffer, e);
}
}
/**
* Adds common header information to the provided JSON buffer. It will begin
* a JSON object for the log message, then add a timestamp, debug type, log
* level, thread ID, and thread name.
*
* @param buffer The JSON buffer to which the content should be added.
* @param level The log level for the message that will be written.
* @param type The debug type for the message that will be written.
*/
private static void addCommonHeader(@NotNull final JSONBuffer buffer,
@NotNull final Level level,
@NotNull final DebugType type)
{
buffer.beginObject();
buffer.appendString("timestamp", getTimestamp());
buffer.appendString("debug-type", type.getName());
buffer.appendString("level", level.getName());
final Thread t = Thread.currentThread();
buffer.appendNumber("thread-id", t.getId());
buffer.appendString("thread-name", t.getName());
}
/**
* Retrieves a timestamp that represents the current time.
*
* @return A timestamp that represents the current time.
*/
@NotNull()
private static String getTimestamp()
{
SimpleDateFormat timestampFormatter = TIMESTAMP_FORMATTERS.get();
if (timestampFormatter == null)
{
timestampFormatter =
new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS'Z'");
timestampFormatter.setTimeZone(StaticUtils.getUTCTimeZone());
TIMESTAMP_FORMATTERS.set(timestampFormatter);
}
return timestampFormatter.format(new Date());
}
/**
* Creates a formatted string representation of the provided stack trace
* frame.
*
* @param e The stack trace element to be formatted.
*
* @return The formatted string representation of the provided stack trace
* frame.
*/
@NotNull()
private static String formatStackTraceFrame(
@NotNull final StackTraceElement e)
{
final StringBuilder buffer = new StringBuilder();
buffer.append(e.getMethodName());
buffer.append('(');
buffer.append(e.getFileName());
final int lineNumber = e.getLineNumber();
if (lineNumber > 0)
{
buffer.append(':');
buffer.append(lineNumber);
}
else if (e.isNativeMethod())
{
buffer.append(":native");
}
buffer.append(')');
return buffer.toString();
}
/**
* Adds information about a caught exception to the provided JSON buffer.
*
* @param buffer The JSON buffer to which the information should be
* appended.
* @param fieldName The name to use for the new field to be added with the
* exception information.
* @param t The exception to be included.
*/
private static void addCaughtException(@NotNull final JSONBuffer buffer,
@NotNull final String fieldName,
@Nullable final Throwable t)
{
if (t == null)
{
return;
}
buffer.beginObject(fieldName);
final String message = t.getMessage();
if (message != null)
{
buffer.appendString("message", message);
}
buffer.beginArray("stack-trace");
for (final StackTraceElement e : t.getStackTrace())
{
buffer.appendString(formatStackTraceFrame(e));
}
buffer.endArray();
final Throwable cause = t.getCause();
if (cause != null)
{
addCaughtException(buffer, "cause", cause);
}
buffer.endObject();
}
/**
* Adds common footer information to the provided JSON buffer. It will
* include an optional caller stack trace, along with the LDAP SDK version
* and revision. It will also end the object that encapsulates the log
* message.
*
* @param buffer The JSON buffer to which the content should be added.
*/
private static void addCommonFooter(@NotNull final JSONBuffer buffer)
{
if (includeStackTrace)
{
buffer.beginArray("caller-stack-trace");
boolean foundDebug = false;
for (final StackTraceElement e : Thread.currentThread().getStackTrace())
{
final String className = e.getClassName();
if (className.equals(Debug.class.getName()))
{
foundDebug = true;
}
else if (foundDebug)
{
buffer.appendString(formatStackTraceFrame(e));
}
}
buffer.endArray();
}
buffer.appendString("ldap-sdk-version", Version.NUMERIC_VERSION_STRING);
buffer.appendString("ldap-sdk-revision", Version.REVISION_ID);
buffer.endObject();
}
/**
* Logs a JSON-formatted debug message with the given level and fields.
*
* @param level The log level to use for the message.
* @param buffer The JSON buffer containing the message to be written.
*/
private static void log(@NotNull final Level level,
@NotNull final JSONBuffer buffer)
{
logger.log(level, buffer.toString());
}
/**
* Logs a JSON-formatted debug message with the given level and fields.
*
* @param level The log level to use for the message.
* @param buffer The JSON buffer containing the message to be written.
* @param thrown An exception to be included with the debug message.
*/
private static void log(@NotNull final Level level,
@NotNull final JSONBuffer buffer,
@Nullable final Throwable thrown)
{
logger.log(level, buffer.toString(), thrown);
}
/**
* Appends the provided debug message to the specified file. This method
* should be safe to call concurrently, even across multiple processes.
*
* @param path The path to the file to which the message should be
* appended. It must not be {@code null}.
* @param message The debug message to be appended to the file. It must not
* be {@code null}.
*/
public static void debugToFile(@NotNull final String path,
@NotNull final String message)
{
debugToFile(new File(path), true, true, message);
}
/**
* Appends the provided debug message to the specified file. This method
* should be safe to call concurrently, even across multiple processes.
*
* @param file The file to which the message should be appended. It must
* not be {@code null}.
* @param message The debug message to be appended to the file. It must not
* be {@code null}.
*/
public static void debugToFile(@NotNull final File file,
@NotNull final String message)
{
debugToFile(file, true, true, message);
}
/**
* Appends the provided debug message to the specified file. This method
* should be safe to call concurrently, even across multiple processes.
*
* @param file The file to which the message should be
* appended. It must not be {@code null}.
* @param includeTimestamp Indicates whether to include a timestamp along
* with the debug message.
* @param includeStackTrace Indicates whether to include a stack trace along
* with the debug message.
* @param message The debug message to be appended to the file.
* It must not be {@code null}.
*/
public static synchronized void debugToFile(@NotNull final File file,
final boolean includeTimestamp,
final boolean includeStackTrace,
@NotNull final String message)
{
try
{
try (FileChannel fileChannel = FileChannel.open(
file.toPath(),
StaticUtils.setOf(
StandardOpenOption.WRITE,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND,
StandardOpenOption.SYNC)))
{
try (FileLock fileLock = fileChannel.lock())
{
// We need to reference the fileLock variable inside the try block to
// avoid a compiler warning.
Validator.ensureTrue(fileLock.isValid());
final ByteStringBuffer messageBuffer = new ByteStringBuffer();
if (fileChannel.size() > 0L)
{
messageBuffer.append(StaticUtils.EOL_BYTES);
}
if (includeTimestamp)
{
messageBuffer.append(
StaticUtils.encodeRFC3339Time(System.currentTimeMillis()));
messageBuffer.append(StaticUtils.EOL_BYTES);
}
messageBuffer.append(message);
messageBuffer.append(StaticUtils.EOL_BYTES);
if (includeStackTrace)
{
messageBuffer.append(StaticUtils.getStackTrace(
Thread.currentThread().getStackTrace()));
messageBuffer.append(StaticUtils.EOL_BYTES);
}
fileChannel.write(ByteBuffer.wrap(
messageBuffer.getBackingArray(), 0, messageBuffer.length()));
}
}
}
catch (final Exception e)
{
// An error occurred while attempting to write to the file. As a
// fallback, print a message about it to standard error.
Debug.debugException(e);
System.err.println(ERR_DEBUG_CANNOT_WRITE_TO_FILE.get(
file.getAbsolutePath(), StaticUtils.getExceptionMessage(e),
message));
}
}
/**
* Attempts to parse the provided string as a debug log level.
*
* @param levelString The string representation of the level to use. It
* must not be {@code null} or empty.
*
* @return The log level that was parsed.
*
* @throws IllegalArgumentException If the provided string cannot be parsed
* as a valid debug log level.
*/
@NotNull()
public static Level parseDebugLogLevel(@NotNull final String levelString)
throws IllegalArgumentException
{
final String lowerLevelString = StaticUtils.toLowerCase(levelString);
switch (lowerLevelString)
{
case "off":
case "none":
case "disabled":
return Level.OFF;
case "severe":
case "error":
return Level.SEVERE;
case "warning":
case "warn":
return Level.WARNING;
case "info":
case "information":
case "informational":
return Level.INFO;
case "config":
case "configuration":
return Level.CONFIG;
case "fine":
return Level.FINE;
case "finer":
return Level.FINER;
case "finest":
case "verbose":
return Level.FINEST;
case "all":
return Level.ALL;
default:
try
{
return Level.parse(levelString);
}
catch (final IllegalArgumentException e)
{
throw new IllegalArgumentException(
ERR_DEBUG_CANNOT_PARSE_LEVEL.get(levelString));
}
}
}
}