001    /*
002     * Copyright 2008-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2015 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.util;
022    
023    
024    
025    import java.io.Serializable;
026    import java.util.EnumSet;
027    import java.util.Properties;
028    import java.util.Set;
029    import java.util.StringTokenizer;
030    import java.util.logging.Level;
031    import java.util.logging.Logger;
032    
033    import com.unboundid.asn1.ASN1Buffer;
034    import com.unboundid.asn1.ASN1Element;
035    import com.unboundid.ldap.protocol.LDAPResponse;
036    import com.unboundid.ldap.sdk.DisconnectType;
037    import com.unboundid.ldap.sdk.Entry;
038    import com.unboundid.ldap.sdk.LDAPConnection;
039    import com.unboundid.ldap.sdk.LDAPRequest;
040    import com.unboundid.ldap.sdk.Version;
041    import com.unboundid.ldif.LDIFRecord;
042    
043    import static com.unboundid.util.StaticUtils.*;
044    
045    
046    
047    /**
048     * This class provides a means of enabling and configuring debugging in the LDAP
049     * SDK.
050     * <BR><BR>
051     * Access to debug information can be enabled through applications that use the
052     * SDK by calling the {@link Debug#setEnabled} methods, or it can also be
053     * enabled without any code changes through the use of system properties.  In
054     * particular, the {@link Debug#PROPERTY_DEBUG_ENABLED},
055     * {@link Debug#PROPERTY_DEBUG_LEVEL}, and {@link Debug#PROPERTY_DEBUG_TYPE}
056     * properties may be used to control debugging without the need to alter any
057     * code within the application that uses the SDK.
058     * <BR><BR>
059     * The LDAP SDK debugging subsystem uses the Java logging framework available
060     * through the {@code java.util.logging} package with a logger name of
061     * "{@code com.unboundid.ldap.sdk}".  The {@link Debug#getLogger} method may
062     * be used to access the logger instance used by the LDAP SDK.
063     * <BR><BR>
064     * <H2>Example</H2>
065     * The following example demonstrates the process that may be used to enable
066     * debugging within the LDAP SDK and write information about all messages with
067     * a {@code WARNING} level or higher to a specified file:
068     * <PRE>
069     * Debug.setEnabled(true);
070     * Logger logger = Debug.getLogger();
071     *
072     * FileHandler fileHandler = new FileHandler(logFilePath);
073     * fileHandler.setLevel(Level.WARNING);
074     * logger.addHandler(fileHandler);
075     * </PRE>
076     */
077    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
078    public final class Debug
079           implements Serializable
080    {
081      /**
082       * The name of the system property that will be used to enable debugging in
083       * the UnboundID LDAP SDK for Java.  The fully-qualified name for this
084       * property is "{@code com.unboundid.ldap.sdk.debug.enabled}".  If it is set,
085       * then it should have a value of either "true" or "false".
086       */
087      public static final String PROPERTY_DEBUG_ENABLED =
088           "com.unboundid.ldap.sdk.debug.enabled";
089    
090    
091    
092      /**
093       * The name of the system property that may be used to indicate whether stack
094       * trace information for the thread calling the debug method should be
095       * included in debug log messages.  The fully-qualified name for this property
096       * is "{@code com.unboundid.ldap.sdk.debug.includeStackTrace}".  If it is set,
097       * then it should have a value of either "true" or "false".
098       */
099      public static final String PROPERTY_INCLUDE_STACK_TRACE =
100           "com.unboundid.ldap.sdk.debug.includeStackTrace";
101    
102    
103    
104      /**
105       * The name of the system property that will be used to set the initial level
106       * for the debug logger.  The fully-qualified name for this property is
107       * "{@code com.unboundid.ldap.sdk.debug.level}".  If it is set, then it should
108       * be one of the strings "{@code SEVERE}", "{@code WARNING}", "{@code INFO}",
109       * "{@code CONFIG}", "{@code FINE}", "{@code FINER}", or "{@code FINEST}".
110       */
111      public static final String PROPERTY_DEBUG_LEVEL =
112           "com.unboundid.ldap.sdk.debug.level";
113    
114    
115    
116      /**
117       * The name of the system property that will be used to indicate that
118       * debugging should be enabled for specific types of messages.  The
119       * fully-qualified name for this property is
120       * "{@code com.unboundid.ldap.sdk.debug.type}". If it is set, then it should
121       * be a comma-delimited list of the names of the desired debug types.  See the
122       * {@link DebugType} enum for the available debug types.
123       */
124      public static final String PROPERTY_DEBUG_TYPE =
125           "com.unboundid.ldap.sdk.debug.type";
126    
127    
128    
129      /**
130       * The name that will be used for the Java logger that will actually handle
131       * the debug messages if debugging is enabled.
132       */
133      public static final String LOGGER_NAME = "com.unboundid.ldap.sdk";
134    
135    
136    
137      /**
138       * The logger that will be used to handle the debug messages if debugging is
139       * enabled.
140       */
141      private static final Logger logger = Logger.getLogger(LOGGER_NAME);
142    
143    
144    
145      /**
146       * The serial version UID for this serializable class.
147       */
148      private static final long serialVersionUID = -6079754380415146030L;
149    
150    
151    
152      // Indicates whether any debugging is currently enabled for the SDK.
153      private static boolean debugEnabled;
154    
155      // Indicates whether to capture a thread stack trace whenever a debug message
156      // is logged.
157      private static boolean includeStackTrace;
158    
159      // The set of debug types for which debugging is enabled.
160      private static EnumSet<DebugType> debugTypes;
161    
162    
163    
164      static
165      {
166        initialize(System.getProperties());
167      }
168    
169    
170    
171      /**
172       * Prevent this class from being instantiated.
173       */
174      private Debug()
175      {
176        // No implementation is required.
177      }
178    
179    
180    
181      /**
182       * Initializes this debugger with the default settings.  Debugging will be
183       * disabled, the set of debug types will include all types, and the debug
184       * level will be "ALL".
185       */
186      public static void initialize()
187      {
188        includeStackTrace = false;
189        debugEnabled      = false;
190        debugTypes        = EnumSet.allOf(DebugType.class);
191    
192        logger.setLevel(Level.ALL);
193      }
194    
195    
196    
197      /**
198       * Initializes this debugger with settings from the provided set of
199       * properties.  Any debug setting that isn't configured in the provided
200       * properties will be initialized with its default value.
201       *
202       * @param  properties  The set of properties to use to initialize this
203       *                     debugger.
204       */
205      public static void initialize(final Properties properties)
206      {
207        // First, apply the default values for the properties.
208        initialize();
209        if ((properties == null) || properties.isEmpty())
210        {
211          // No properties were provided, so we don't need to do anything.
212          return;
213        }
214    
215        final String enabledProp = properties.getProperty(PROPERTY_DEBUG_ENABLED);
216        if ((enabledProp != null) && (enabledProp.length() > 0))
217        {
218          if (enabledProp.equalsIgnoreCase("true"))
219          {
220            debugEnabled = true;
221          }
222          else if (enabledProp.equalsIgnoreCase("false"))
223          {
224            debugEnabled = false;
225          }
226          else
227          {
228            throw new IllegalArgumentException("Invalid value '" + enabledProp +
229                                               "' for property " +
230                                               PROPERTY_DEBUG_ENABLED +
231                                               ".  The value must be either " +
232                                               "'true' or 'false'.");
233          }
234        }
235    
236        final String stackProp =
237             properties.getProperty(PROPERTY_INCLUDE_STACK_TRACE);
238        if ((stackProp != null) && (stackProp.length() > 0))
239        {
240          if (stackProp.equalsIgnoreCase("true"))
241          {
242            includeStackTrace = true;
243          }
244          else if (stackProp.equalsIgnoreCase("false"))
245          {
246            includeStackTrace = false;
247          }
248          else
249          {
250            throw new IllegalArgumentException("Invalid value '" + stackProp +
251                                               "' for property " +
252                                               PROPERTY_INCLUDE_STACK_TRACE +
253                                               ".  The value must be either " +
254                                               "'true' or 'false'.");
255          }
256        }
257    
258        final String typesProp = properties.getProperty(PROPERTY_DEBUG_TYPE);
259        if ((typesProp != null) && (typesProp.length() > 0))
260        {
261          debugTypes = EnumSet.noneOf(DebugType.class);
262          final StringTokenizer t = new StringTokenizer(typesProp, ", ");
263          while (t.hasMoreTokens())
264          {
265            final String debugTypeName = t.nextToken();
266            final DebugType debugType = DebugType.forName(debugTypeName);
267            if (debugType == null)
268            {
269              // Throw a runtime exception to indicate that the debug type is
270              // invalid.
271              throw new IllegalArgumentException("Invalid value '" + debugTypeName +
272                          "' for property " + PROPERTY_DEBUG_TYPE +
273                          ".  Allowed values include:  " +
274                          DebugType.getTypeNameList() + '.');
275            }
276            else
277            {
278              debugTypes.add(debugType);
279            }
280          }
281        }
282    
283        final String levelProp = properties.getProperty(PROPERTY_DEBUG_LEVEL);
284        if ((levelProp != null) && (levelProp.length() > 0))
285        {
286          logger.setLevel(Level.parse(levelProp));
287        }
288      }
289    
290    
291    
292      /**
293       * Retrieves the logger that will be used to write the debug messages.
294       *
295       * @return  The logger that will be used to write the debug messages.
296       */
297      public static Logger getLogger()
298      {
299        return logger;
300      }
301    
302    
303    
304      /**
305       * Indicates whether any form of debugging is enabled.
306       *
307       * @return  {@code true} if debugging is enabled, or {@code false} if not.
308       */
309      public static boolean debugEnabled()
310      {
311        return debugEnabled;
312      }
313    
314    
315    
316      /**
317       * Indicates whether debugging is enabled for messages of the specified debug
318       * type.
319       *
320       * @param  debugType  The debug type for which to make the determination.
321       *
322       * @return  {@code true} if debugging is enabled for messages of the specified
323       *          debug type, or {@code false} if not.
324       */
325      public static boolean debugEnabled(final DebugType debugType)
326      {
327        return (debugEnabled && debugTypes.contains(debugType));
328      }
329    
330    
331    
332      /**
333       * Specifies whether debugging should be enabled.  If it should be, then it
334       * will be enabled for all debug types.
335       *
336       * @param  enabled  Specifies whether debugging should be enabled.
337       */
338      public static void setEnabled(final boolean enabled)
339      {
340        debugTypes   = EnumSet.allOf(DebugType.class);
341        debugEnabled = enabled;
342      }
343    
344    
345    
346      /**
347       * Specifies whether debugging should be enabled.  If it should be, then it
348       * will be enabled for all debug types in the provided set.
349       *
350       * @param  enabled  Specifies whether debugging should be enabled.
351       * @param  types    The set of debug types that should be enabled.  It may be
352       *                  {@code null} or empty to indicate that it should be for
353       *                  all debug types.
354       */
355      public static void setEnabled(final boolean enabled,
356                                    final Set<DebugType> types)
357      {
358        if ((types == null) || types.isEmpty())
359        {
360          debugTypes = EnumSet.allOf(DebugType.class);
361        }
362        else
363        {
364          debugTypes = EnumSet.copyOf(types);
365        }
366    
367        debugEnabled = enabled;
368      }
369    
370    
371    
372      /**
373       * Indicates whether log messages should include a stack trace of the thread
374       * that invoked the debug method.
375       *
376       * @return  {@code true} if log messages should include a stack trace of the
377       *          thread that invoked the debug method, or {@code false} if not.
378       */
379      public static boolean includeStackTrace()
380      {
381        return includeStackTrace;
382      }
383    
384    
385    
386      /**
387       * Specifies whether log messages should include a stack trace of the thread
388       * that invoked the debug method.
389       *
390       * @param  includeStackTrace  Indicates whether log messages should include a
391       *                            stack trace of the thread that invoked the debug
392       *                            method.
393       */
394      public static void setIncludeStackTrace(final boolean includeStackTrace)
395      {
396        Debug.includeStackTrace = includeStackTrace;
397      }
398    
399    
400    
401      /**
402       * Retrieves the set of debug types that will be used if debugging is enabled.
403       *
404       * @return  The set of debug types that will be used if debugging is enabled.
405       */
406      public static EnumSet<DebugType> getDebugTypes()
407      {
408        return debugTypes;
409      }
410    
411    
412    
413      /**
414       * Writes debug information about the provided exception, if appropriate.  If
415       * it is to be logged, then it will be sent to the underlying logger using the
416       * {@code WARNING} level.
417       *
418       * @param  t  The exception for which debug information should be written.
419       */
420      public static void debugException(final Throwable t)
421      {
422        if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION))
423        {
424          debugException(Level.WARNING, t);
425        }
426      }
427    
428    
429    
430      /**
431       * Writes debug information about the provided exception, if appropriate.
432       *
433       * @param  l  The log level that should be used for the debug information.
434       * @param  t  The exception for which debug information should be written.
435       */
436      public static void debugException(final Level l, final Throwable t)
437      {
438        if (debugEnabled && debugTypes.contains(DebugType.EXCEPTION))
439        {
440          final StringBuilder buffer = new StringBuilder();
441          addCommonHeader(buffer, l);
442          buffer.append("caughtException=\"");
443          getStackTrace(t, buffer);
444          buffer.append('"');
445    
446          logger.log(l, buffer.toString(), t);
447        }
448      }
449    
450    
451    
452      /**
453       * Writes debug information to indicate that a connection has been
454       * established, if appropriate.  If it is to be logged, then it will be sent
455       * to the underlying logger using the {@code INFO} level.
456       *
457       * @param  h  The address of the server to which the connection was
458       *            established.
459       * @param  p  The port of the server to which the connection was established.
460       */
461      public static void debugConnect(final String h, final int p)
462      {
463        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
464        {
465          debugConnect(Level.INFO, h, p, null);
466        }
467      }
468    
469    
470    
471      /**
472       * Writes debug information to indicate that a connection has been
473       * established, if appropriate.
474       *
475       * @param  l  The log level that should be used for the debug information.
476       * @param  h  The address of the server to which the connection was
477       *            established.
478       * @param  p  The port of the server to which the connection was established.
479       */
480      public static void debugConnect(final Level l, final String h, final int p)
481      {
482        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
483        {
484          debugConnect(l, h, p, null);
485        }
486      }
487    
488    
489    
490      /**
491       * Writes debug information to indicate that a connection has been
492       * established, if appropriate.  If it is to be logged, then it will be sent
493       * to the underlying logger using the {@code INFO} level.
494       *
495       * @param  h  The address of the server to which the connection was
496       *            established.
497       * @param  p  The port of the server to which the connection was established.
498       * @param  c  The connection object for the connection that has been
499       *            established.  It may be {@code null} for historic reasons, but
500       *            should be non-{@code null} in new uses.
501       */
502      public static void debugConnect(final String h, final int p,
503                                      final LDAPConnection c)
504      {
505        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
506        {
507          debugConnect(Level.INFO, h, p, c);
508        }
509      }
510    
511    
512    
513      /**
514       * Writes debug information to indicate that a connection has been
515       * established, if appropriate.
516       *
517       * @param  l  The log level that should be used for the debug information.
518       * @param  h  The address of the server to which the connection was
519       *            established.
520       * @param  p  The port of the server to which the connection was established.
521       * @param  c  The connection object for the connection that has been
522       *            established.  It may be {@code null} for historic reasons, but
523       *            should be non-{@code null} in new uses.
524       */
525      public static void debugConnect(final Level l, final String h, final int p,
526                                      final LDAPConnection c)
527      {
528        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
529        {
530          final StringBuilder buffer = new StringBuilder();
531          addCommonHeader(buffer, l);
532          buffer.append("connectedTo=\"");
533          buffer.append(h);
534          buffer.append(':');
535          buffer.append(p);
536          buffer.append('"');
537    
538          if (c != null)
539          {
540            buffer.append(" connectionID=");
541            buffer.append(c.getConnectionID());
542    
543            final String connectionName = c.getConnectionName();
544            if (connectionName != null)
545            {
546              buffer.append(" connectionName=\"");
547              buffer.append(connectionName);
548              buffer.append('"');
549            }
550    
551            final String connectionPoolName = c.getConnectionPoolName();
552            if (connectionPoolName != null)
553            {
554              buffer.append(" connectionPoolName=\"");
555              buffer.append(connectionPoolName);
556              buffer.append('"');
557            }
558          }
559    
560          logger.log(l, buffer.toString());
561        }
562      }
563    
564    
565    
566      /**
567       * Writes debug information to indicate that a connection has been
568       * terminated, if appropriate.  If it is to be logged, then it will be sent
569       * to the underlying logger using the {@code INFO} level.
570       *
571       * @param  h  The address of the server to which the connection was
572       *            established.
573       * @param  p  The port of the server to which the connection was established.
574       * @param  t  The disconnect type.
575       * @param  m  The disconnect message, if available.
576       * @param  e  The disconnect cause, if available.
577       */
578      public static void debugDisconnect(final String h, final int p,
579                                         final DisconnectType t, final String m,
580                                         final Throwable e)
581      {
582        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
583        {
584          debugDisconnect(Level.INFO, h, p, null, t, m, e);
585        }
586      }
587    
588    
589    
590      /**
591       * Writes debug information to indicate that a connection has been
592       * terminated, if appropriate.
593       *
594       * @param  l  The log level that should be used for the debug information.
595       * @param  h  The address of the server to which the connection was
596       *            established.
597       * @param  p  The port of the server to which the connection was established.
598       * @param  t  The disconnect type.
599       * @param  m  The disconnect message, if available.
600       * @param  e  The disconnect cause, if available.
601       */
602      public static void debugDisconnect(final Level l, final String h, final int p,
603                                         final DisconnectType t, final String m,
604                                         final Throwable e)
605      {
606        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
607        {
608          debugDisconnect(l, h, p, null, t, m, e);
609        }
610      }
611    
612    
613    
614      /**
615       * Writes debug information to indicate that a connection has been
616       * terminated, if appropriate.  If it is to be logged, then it will be sent
617       * to the underlying logger using the {@code INFO} level.
618       *
619       * @param  h  The address of the server to which the connection was
620       *            established.
621       * @param  p  The port of the server to which the connection was established.
622       * @param  c  The connection object for the connection that has been closed.
623       *            It may be {@code null} for historic reasons, but should be
624       *            non-{@code null} in new uses.
625       * @param  t  The disconnect type.
626       * @param  m  The disconnect message, if available.
627       * @param  e  The disconnect cause, if available.
628       */
629      public static void debugDisconnect(final String h, final int p,
630                                         final LDAPConnection c,
631                                         final DisconnectType t, final String m,
632                                         final Throwable e)
633      {
634        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
635        {
636          debugDisconnect(Level.INFO, h, p, c, t, m, e);
637        }
638      }
639    
640    
641    
642      /**
643       * Writes debug information to indicate that a connection has been
644       * terminated, if appropriate.
645       *
646       * @param  l  The log level that should be used for the debug information.
647       * @param  h  The address of the server to which the connection was
648       *            established.
649       * @param  p  The port of the server to which the connection was established.
650       * @param  c  The connection object for the connection that has been closed.
651       *            It may be {@code null} for historic reasons, but should be
652       *            non-{@code null} in new uses.
653       * @param  t  The disconnect type.
654       * @param  m  The disconnect message, if available.
655       * @param  e  The disconnect cause, if available.
656       */
657      public static void debugDisconnect(final Level l, final String h, final int p,
658                                         final LDAPConnection c,
659                                         final DisconnectType t, final String m,
660                                         final Throwable e)
661      {
662        if (debugEnabled && debugTypes.contains(DebugType.CONNECT))
663        {
664          final StringBuilder buffer = new StringBuilder();
665          addCommonHeader(buffer, l);
666    
667          if (c != null)
668          {
669            buffer.append("connectionID=");
670            buffer.append(c.getConnectionID());
671    
672            final String connectionName = c.getConnectionName();
673            if (connectionName != null)
674            {
675              buffer.append(" connectionName=\"");
676              buffer.append(connectionName);
677              buffer.append('"');
678            }
679    
680            final String connectionPoolName = c.getConnectionPoolName();
681            if (connectionPoolName != null)
682            {
683              buffer.append(" connectionPoolName=\"");
684              buffer.append(connectionPoolName);
685              buffer.append('"');
686            }
687    
688            buffer.append(' ');
689          }
690    
691          buffer.append("disconnectedFrom=\"");
692          buffer.append(h);
693          buffer.append(':');
694          buffer.append(p);
695          buffer.append("\" disconnectType=\"");
696          buffer.append(t.name());
697          buffer.append('"');
698    
699          if (m != null)
700          {
701            buffer.append("\" disconnectMessage=\"");
702            buffer.append(m);
703            buffer.append('"');
704          }
705    
706          if (e != null)
707          {
708            buffer.append("\" disconnectCause=\"");
709            getStackTrace(e, buffer);
710            buffer.append('"');
711          }
712    
713          logger.log(l, buffer.toString(), c);
714        }
715      }
716    
717    
718    
719      /**
720       * Writes debug information about the provided request, if appropriate.  If
721       * it is to be logged, then it will be sent to the underlying logger using the
722       * {@code INFO} level.
723       *
724       * @param  r  The LDAP request for which debug information should be written.
725       */
726      public static void debugLDAPRequest(final LDAPRequest r)
727      {
728        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
729        {
730          debugLDAPRequest(Level.INFO, r, -1, null);
731        }
732      }
733    
734    
735    
736      /**
737       * Writes debug information about the provided request, if appropriate.
738       *
739       * @param  l  The log level that should be used for the debug information.
740       * @param  r  The LDAP request for which debug information should be written.
741       */
742      public static void debugLDAPRequest(final Level l, final LDAPRequest r)
743      {
744        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
745        {
746          debugLDAPRequest(l, r, -1, null);
747        }
748      }
749    
750    
751    
752      /**
753       * Writes debug information about the provided request, if appropriate.  If
754       * it is to be logged, then it will be sent to the underlying logger using the
755       * {@code INFO} level.
756       *
757       * @param  r  The LDAP request for which debug information should be written.
758       * @param  i  The message ID for the request that will be sent.  It may be
759       *            negative if no message ID is available.
760       * @param  c  The connection on which the request will be sent.  It may be
761       *            {@code null} for historic reasons, but should be
762       *            non-{@code null} in new uses.
763       */
764      public static void debugLDAPRequest(final LDAPRequest r, final int i,
765                                          final LDAPConnection c)
766      {
767        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
768        {
769          debugLDAPRequest(Level.INFO, r, i, c);
770        }
771      }
772    
773    
774    
775      /**
776       * Writes debug information about the provided request, if appropriate.
777       *
778       * @param  l  The log level that should be used for the debug information.
779       * @param  r  The LDAP request for which debug information should be written.
780       * @param  i  The message ID for the request that will be sent.  It may be
781       *            negative if no message ID is available.
782       * @param  c  The connection on which the request will be sent.  It may be
783       *            {@code null} for historic reasons, but should be
784       *            non-{@code null} in new uses.
785       */
786      public static void debugLDAPRequest(final Level l, final LDAPRequest r,
787                                          final int i, final LDAPConnection c)
788      {
789        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
790        {
791          final StringBuilder buffer = new StringBuilder();
792          addCommonHeader(buffer, l);
793    
794          if (c != null)
795          {
796            buffer.append("connectionID=");
797            buffer.append(c.getConnectionID());
798    
799            final String connectionName = c.getConnectionName();
800            if (connectionName != null)
801            {
802              buffer.append(" connectionName=\"");
803              buffer.append(connectionName);
804              buffer.append('"');
805            }
806    
807            final String connectionPoolName = c.getConnectionPoolName();
808            if (connectionPoolName != null)
809            {
810              buffer.append(" connectionPoolName=\"");
811              buffer.append(connectionPoolName);
812              buffer.append('"');
813            }
814    
815            buffer.append(" connectedTo=\"");
816            buffer.append(c.getConnectedAddress());
817            buffer.append(':');
818            buffer.append(c.getConnectedPort());
819            buffer.append("\" ");
820          }
821    
822          if (i >= 0)
823          {
824            buffer.append(" messageID=");
825            buffer.append(i);
826            buffer.append(' ');
827          }
828    
829          buffer.append("sendingLDAPRequest=\"");
830          r.toString(buffer);
831          buffer.append('"');
832    
833          logger.log(l,  buffer.toString());
834        }
835      }
836    
837    
838    
839      /**
840       * Writes debug information about the provided result, if appropriate.  If
841       * it is to be logged, then it will be sent to the underlying logger using the
842       * {@code INFO} level.
843       *
844       * @param  r  The result for which debug information should be written.
845       */
846      public static void debugLDAPResult(final LDAPResponse r)
847      {
848        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
849        {
850          debugLDAPResult(Level.INFO, r, null);
851        }
852      }
853    
854    
855    
856      /**
857       * Writes debug information about the provided result, if appropriate.
858       *
859       * @param  l  The log level that should be used for the debug information.
860       * @param  r  The result for which debug information should be written.
861       */
862      public static void debugLDAPResult(final Level l, final LDAPResponse r)
863      {
864        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
865        {
866          debugLDAPResult(l, r, null);
867        }
868      }
869    
870    
871    
872      /**
873       * Writes debug information about the provided result, if appropriate.  If
874       * it is to be logged, then it will be sent to the underlying logger using the
875       * {@code INFO} level.
876       *
877       * @param  r  The result for which debug information should be written.
878       * @param  c  The connection on which the response was received.  It may be
879       *            {@code null} for historic reasons, but should be
880       *            non-{@code null} in new uses.
881       */
882      public static void debugLDAPResult(final LDAPResponse r,
883                                         final LDAPConnection c)
884      {
885        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
886        {
887          debugLDAPResult(Level.INFO, r, c);
888        }
889      }
890    
891    
892    
893      /**
894       * Writes debug information about the provided result, if appropriate.
895       *
896       * @param  l  The log level that should be used for the debug information.
897       * @param  r  The result for which debug information should be written.
898       * @param  c  The connection on which the response was received.  It may be
899       *            {@code null} for historic reasons, but should be
900       *            non-{@code null} in new uses.
901       */
902      public static void debugLDAPResult(final Level l, final LDAPResponse r,
903                                         final LDAPConnection c)
904      {
905        if (debugEnabled && debugTypes.contains(DebugType.LDAP))
906        {
907          final StringBuilder buffer = new StringBuilder();
908          addCommonHeader(buffer, l);
909    
910          if (c != null)
911          {
912            buffer.append("connectionID=");
913            buffer.append(c.getConnectionID());
914    
915            final String connectionName = c.getConnectionName();
916            if (connectionName != null)
917            {
918              buffer.append(" connectionName=\"");
919              buffer.append(connectionName);
920              buffer.append('"');
921            }
922    
923            final String connectionPoolName = c.getConnectionPoolName();
924            if (connectionPoolName != null)
925            {
926              buffer.append(" connectionPoolName=\"");
927              buffer.append(connectionPoolName);
928              buffer.append('"');
929            }
930    
931            buffer.append(" connectedTo=\"");
932            buffer.append(c.getConnectedAddress());
933            buffer.append(':');
934            buffer.append(c.getConnectedPort());
935            buffer.append("\" ");
936          }
937    
938          buffer.append("readLDAPResult=\"");
939          r.toString(buffer);
940          buffer.append('"');
941    
942          logger.log(l,  buffer.toString());
943        }
944      }
945    
946    
947    
948      /**
949       * Writes debug information about the provided ASN.1 element to be written,
950       * if appropriate.  If it is to be logged, then it will be sent to the
951       * underlying logger using the {@code INFO} level.
952       *
953       * @param  e  The ASN.1 element for which debug information should be written.
954       */
955      public static void debugASN1Write(final ASN1Element e)
956      {
957        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
958        {
959          debugASN1Write(Level.INFO, e);
960        }
961      }
962    
963    
964    
965      /**
966       * Writes debug information about the provided ASN.1 element to be written,
967       * if appropriate.
968       *
969       * @param  l  The log level that should be used for the debug information.
970       * @param  e  The ASN.1 element for which debug information should be written.
971       */
972      public static void debugASN1Write(final Level l, final ASN1Element e)
973      {
974        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
975        {
976          final StringBuilder buffer = new StringBuilder();
977          addCommonHeader(buffer, l);
978          buffer.append("writingASN1Element=\"");
979          e.toString(buffer);
980          buffer.append('"');
981    
982          logger.log(l, buffer.toString());
983        }
984      }
985    
986    
987    
988      /**
989       * Writes debug information about the provided ASN.1 element to be written,
990       * if appropriate.  If it is to be logged, then it will be sent to the
991       * underlying logger using the {@code INFO} level.
992       *
993       * @param  b  The ASN.1 buffer with the information to be written.
994       */
995      public static void debugASN1Write(final ASN1Buffer b)
996      {
997        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
998        {
999          debugASN1Write(Level.INFO, b);
1000        }
1001      }
1002    
1003    
1004    
1005      /**
1006       * Writes debug information about the provided ASN.1 element to be written,
1007       * if appropriate.
1008       *
1009       * @param  l  The log level that should be used for the debug information.
1010       * @param  b  The ASN1Buffer with the information to be written.
1011       */
1012      public static void debugASN1Write(final Level l, final ASN1Buffer b)
1013      {
1014        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
1015        {
1016          final StringBuilder buffer = new StringBuilder();
1017          addCommonHeader(buffer, l);
1018          buffer.append("writingASN1Element=\"");
1019          toHex(b.toByteArray(), buffer);
1020          buffer.append('"');
1021    
1022          logger.log(l, buffer.toString());
1023        }
1024      }
1025    
1026    
1027    
1028      /**
1029       * Writes debug information about the provided ASN.1 element that was read, if
1030       * appropriate.  If it is to be logged, then it will be sent to the underlying
1031       * logger using the {@code INFO} level.
1032       *
1033       * @param  e  The ASN.1 element for which debug information should be written.
1034       */
1035      public static void debugASN1Read(final ASN1Element e)
1036      {
1037        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
1038        {
1039          debugASN1Read(Level.INFO, e);
1040        }
1041      }
1042    
1043    
1044    
1045      /**
1046       * Writes debug information about the provided ASN.1 element that was read, if
1047       * appropriate.
1048       *
1049       * @param  l  The log level that should be used for the debug information.
1050       * @param  e  The ASN.1 element for which debug information should be written.
1051       */
1052      public static void debugASN1Read(final Level l, final ASN1Element e)
1053      {
1054        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
1055        {
1056          final StringBuilder buffer = new StringBuilder();
1057          addCommonHeader(buffer, l);
1058          buffer.append("readASN1Element=\"");
1059          e.toString(buffer);
1060          buffer.append('"');
1061    
1062          logger.log(l, buffer.toString());
1063        }
1064      }
1065    
1066    
1067    
1068      /**
1069       * Writes debug information about the provided ASN.1 element that was read, if
1070       * appropriate.
1071       *
1072       * @param  l         The log level that should be used for the debug
1073       *                   information.
1074       * @param  dataType  A string representation of the data type for the data
1075       *                   that was read.
1076       * @param  berType   The BER type for the element that was read.
1077       * @param  length    The number of bytes in the value of the element that was
1078       *                   read.
1079       * @param  value     A representation of the value that was read.  The debug
1080       *                   message will include the string representation of this
1081       *                   value, unless the value is a byte array in which it will
1082       *                   be a hex representation of the bytes that it contains.
1083       *                   It may be {@code null} for an ASN.1 null element.
1084       */
1085      public static void debugASN1Read(final Level l, final String dataType,
1086                                       final int berType, final int length,
1087                                       final Object value)
1088      {
1089        if (debugEnabled && debugTypes.contains(DebugType.ASN1))
1090        {
1091          final StringBuilder buffer = new StringBuilder();
1092          addCommonHeader(buffer, l);
1093          buffer.append("readASN1Element=\"dataType='");
1094          buffer.append(dataType);
1095          buffer.append("' berType='");
1096          buffer.append(toHex((byte) (berType & 0xFF)));
1097          buffer.append('\'');
1098          buffer.append("' valueLength=");
1099          buffer.append(length);
1100    
1101          if (value != null)
1102          {
1103            buffer.append(" value='");
1104            if (value instanceof byte[])
1105            {
1106              toHex((byte[]) value, buffer);
1107            }
1108            else
1109            {
1110              buffer.append(value);
1111            }
1112            buffer.append('\'');
1113          }
1114          buffer.append('"');
1115    
1116          logger.log(l, buffer.toString());
1117        }
1118      }
1119    
1120    
1121    
1122      /**
1123       * Writes debug information about the provided LDIF record to be written, if
1124       * if appropriate.  If it is to be logged, then it will be sent to the
1125       * underlying logger using the {@code INFO} level.
1126       *
1127       * @param  r  The LDIF record for which debug information should be written.
1128       */
1129      public static void debugLDIFWrite(final LDIFRecord r)
1130      {
1131        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1132        {
1133          debugLDIFWrite(Level.INFO, r);
1134        }
1135      }
1136    
1137    
1138    
1139      /**
1140       * Writes debug information about the provided LDIF record to be written, if
1141       * appropriate.
1142       *
1143       * @param  l  The log level that should be used for the debug information.
1144       * @param  r  The LDIF record for which debug information should be written.
1145       */
1146      public static void debugLDIFWrite(final Level l, final LDIFRecord r)
1147      {
1148        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1149        {
1150          final StringBuilder buffer = new StringBuilder();
1151          addCommonHeader(buffer, l);
1152          buffer.append("writingLDIFRecord=\"");
1153          r.toString(buffer);
1154          buffer.append('"');
1155    
1156          logger.log(l, buffer.toString());
1157        }
1158      }
1159    
1160    
1161    
1162      /**
1163       * Writes debug information about the provided record read from LDIF, if
1164       * appropriate.  If it is to be logged, then it will be sent to the underlying
1165       * logger using the {@code INFO} level.
1166       *
1167       * @param  r  The LDIF record for which debug information should be written.
1168       */
1169      public static void debugLDIFRead(final LDIFRecord r)
1170      {
1171        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1172        {
1173          debugLDIFRead(Level.INFO, r);
1174        }
1175      }
1176    
1177    
1178    
1179      /**
1180       * Writes debug information about the provided record read from LDIF, if
1181       * appropriate.
1182       *
1183       * @param  l  The log level that should be used for the debug information.
1184       * @param  r  The LDIF record for which debug information should be written.
1185       */
1186      public static void debugLDIFRead(final Level l, final LDIFRecord r)
1187      {
1188        if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1189        {
1190          final StringBuilder buffer = new StringBuilder();
1191          addCommonHeader(buffer, l);
1192          buffer.append("readLDIFRecord=\"");
1193          r.toString(buffer);
1194          buffer.append('"');
1195    
1196          logger.log(l, buffer.toString());
1197        }
1198      }
1199    
1200    
1201    
1202      /**
1203       * Writes debug information about monitor entry parsing.  If it is to be
1204       * logged, then it will be sent to the underlying logger using the
1205       * {@code FINE} level.
1206       *
1207       * @param  e  The entry containing the monitor information being parsed.
1208       * @param  m  The message to be written to the debug logger.
1209       */
1210      public static void debugMonitor(final Entry e, final String m)
1211      {
1212        if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
1213        {
1214          debugMonitor(Level.FINE, e, m);
1215        }
1216      }
1217    
1218    
1219    
1220      /**
1221       * Writes debug information about monitor entry parsing, if appropriate.
1222       *
1223       * @param  l  The log level that should be used for the debug information.
1224       * @param  e  The entry containing the monitor information being parsed.
1225       * @param  m  The message to be written to the debug logger.
1226       */
1227      public static void debugMonitor(final Level l, final Entry e, final String m)
1228      {
1229        if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
1230        {
1231          final StringBuilder buffer = new StringBuilder();
1232          addCommonHeader(buffer, l);
1233          buffer.append("monitorEntryDN=\"");
1234          buffer.append(e.getDN());
1235          buffer.append("\" message=\"");
1236          buffer.append(m);
1237          buffer.append('"');
1238    
1239          logger.log(l, buffer.toString());
1240        }
1241      }
1242    
1243    
1244    
1245      /**
1246       * Writes debug information about a coding error detected in the use of the
1247       * LDAP SDK.  If it is to be logged, then it will be sent to the underlying
1248       * logger using the {@code SEVERE} level.
1249       *
1250       * @param  t  The {@code Throwable} object that was created and will be thrown
1251       *            as a result of the coding error.
1252       */
1253      public static void debugCodingError(final Throwable t)
1254      {
1255        if (debugEnabled && debugTypes.contains(DebugType.CODING_ERROR))
1256        {
1257          final StringBuilder buffer = new StringBuilder();
1258          addCommonHeader(buffer, Level.SEVERE);
1259          buffer.append("codingError=\"");
1260          getStackTrace(t, buffer);
1261          buffer.append('"');
1262    
1263          logger.log(Level.SEVERE, buffer.toString());
1264        }
1265      }
1266    
1267    
1268    
1269      /**
1270       * Writes a generic debug message, if appropriate.
1271       *
1272       * @param  l  The log level that should be used for the debug information.
1273       * @param  t  The debug type to use to determine whether to write the message.
1274       * @param  m  The message to be written.
1275       */
1276      public static void debug(final Level l, final DebugType t, final String m)
1277      {
1278        if (debugEnabled && debugTypes.contains(t))
1279        {
1280          final StringBuilder buffer = new StringBuilder();
1281          addCommonHeader(buffer, l);
1282          buffer.append("message=\"");
1283          buffer.append(m);
1284          buffer.append('"');
1285    
1286          logger.log(l, buffer.toString());
1287        }
1288      }
1289    
1290    
1291    
1292      /**
1293       * Writes a generic debug message, if appropriate.
1294       *
1295       * @param  l  The log level that should be used for the debug information.
1296       * @param  t  The debug type to use to determine whether to write the message.
1297       * @param  m  The message to be written.
1298       * @param  e  An exception to include with the log message.
1299       */
1300      public static void debug(final Level l, final DebugType t, final String m,
1301                               final Throwable e)
1302      {
1303        if (debugEnabled && debugTypes.contains(t))
1304        {
1305          final StringBuilder buffer = new StringBuilder();
1306          addCommonHeader(buffer, l);
1307          buffer.append("message=\"");
1308          buffer.append(m);
1309          buffer.append('"');
1310          buffer.append(" exception=\"");
1311          getStackTrace(e, buffer);
1312          buffer.append('"');
1313    
1314          logger.log(l, buffer.toString(), e);
1315        }
1316      }
1317    
1318    
1319    
1320      /**
1321       * Writes common header information to the provided buffer.  It will include
1322       * the thread ID, name, and caller stack trace (optional), and it will be
1323       * followed by a trailing space.
1324       *
1325       * @param  buffer  The buffer to which the information should be appended.
1326       * @param  level   The log level for the message that will be written.
1327       */
1328      private static void addCommonHeader(final StringBuilder buffer,
1329                                          final Level level)
1330      {
1331        buffer.append("level=\"");
1332        buffer.append(level.getName());
1333        buffer.append("\" threadID=");
1334        buffer.append(Thread.currentThread().getId());
1335        buffer.append(" threadName=\"");
1336        buffer.append(Thread.currentThread().getName());
1337    
1338        if (includeStackTrace)
1339        {
1340          buffer.append("\" calledFrom=\"");
1341    
1342          boolean appended   = false;
1343          boolean foundDebug = false;
1344          for (final StackTraceElement e : Thread.currentThread().getStackTrace())
1345          {
1346            final String className = e.getClassName();
1347            if (className.equals(Debug.class.getName()))
1348            {
1349              foundDebug = true;
1350            }
1351            else if (foundDebug)
1352            {
1353              if (appended)
1354              {
1355                buffer.append(" / ");
1356              }
1357              appended = true;
1358    
1359              buffer.append(e.getMethodName());
1360              buffer.append('(');
1361              buffer.append(e.getFileName());
1362    
1363              final int lineNumber = e.getLineNumber();
1364              if (lineNumber > 0)
1365              {
1366                buffer.append(':');
1367                buffer.append(lineNumber);
1368              }
1369              else if (e.isNativeMethod())
1370              {
1371                buffer.append(":native");
1372              }
1373    
1374              buffer.append(')');
1375            }
1376          }
1377        }
1378    
1379        buffer.append("\" revision=");
1380        buffer.append(Version.REVISION_NUMBER);
1381        buffer.append(' ');
1382      }
1383    }