001/*
002 * Copyright 2008-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2014 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 */
021package com.unboundid.util;
022
023
024
025import java.io.Serializable;
026import java.util.EnumSet;
027import java.util.Properties;
028import java.util.Set;
029import java.util.StringTokenizer;
030import java.util.logging.Level;
031import java.util.logging.Logger;
032
033import com.unboundid.asn1.ASN1Buffer;
034import com.unboundid.asn1.ASN1Element;
035import com.unboundid.ldap.protocol.LDAPResponse;
036import com.unboundid.ldap.sdk.DisconnectType;
037import com.unboundid.ldap.sdk.Entry;
038import com.unboundid.ldap.sdk.LDAPConnection;
039import com.unboundid.ldap.sdk.LDAPRequest;
040import com.unboundid.ldap.sdk.Version;
041import com.unboundid.ldif.LDIFRecord;
042
043import 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)
078public 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 LDIF record to be written, if
1070   * if appropriate.  If it is to be logged, then it will be sent to the
1071   * underlying logger using the {@code INFO} level.
1072   *
1073   * @param  r  The LDIF record for which debug information should be written.
1074   */
1075  public static void debugLDIFWrite(final LDIFRecord r)
1076  {
1077    if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1078    {
1079      debugLDIFWrite(Level.INFO, r);
1080    }
1081  }
1082
1083
1084
1085  /**
1086   * Writes debug information about the provided LDIF record to be written, if
1087   * appropriate.
1088   *
1089   * @param  l  The log level that should be used for the debug information.
1090   * @param  r  The LDIF record for which debug information should be written.
1091   */
1092  public static void debugLDIFWrite(final Level l, final LDIFRecord r)
1093  {
1094    if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1095    {
1096      final StringBuilder buffer = new StringBuilder();
1097      addCommonHeader(buffer, l);
1098      buffer.append("writingLDIFRecord=\"");
1099      r.toString(buffer);
1100      buffer.append('"');
1101
1102      logger.log(l, buffer.toString());
1103    }
1104  }
1105
1106
1107
1108  /**
1109   * Writes debug information about the provided record read from LDIF, if
1110   * appropriate.  If it is to be logged, then it will be sent to the underlying
1111   * logger using the {@code INFO} level.
1112   *
1113   * @param  r  The LDIF record for which debug information should be written.
1114   */
1115  public static void debugLDIFRead(final LDIFRecord r)
1116  {
1117    if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1118    {
1119      debugLDIFRead(Level.INFO, r);
1120    }
1121  }
1122
1123
1124
1125  /**
1126   * Writes debug information about the provided record read from LDIF, if
1127   * appropriate.
1128   *
1129   * @param  l  The log level that should be used for the debug information.
1130   * @param  r  The LDIF record for which debug information should be written.
1131   */
1132  public static void debugLDIFRead(final Level l, final LDIFRecord r)
1133  {
1134    if (debugEnabled && debugTypes.contains(DebugType.LDIF))
1135    {
1136      final StringBuilder buffer = new StringBuilder();
1137      addCommonHeader(buffer, l);
1138      buffer.append("readLDIFRecord=\"");
1139      r.toString(buffer);
1140      buffer.append('"');
1141
1142      logger.log(l, buffer.toString());
1143    }
1144  }
1145
1146
1147
1148  /**
1149   * Writes debug information about monitor entry parsing.  If it is to be
1150   * logged, then it will be sent to the underlying logger using the
1151   * {@code FINE} level.
1152   *
1153   * @param  e  The entry containing the monitor information being parsed.
1154   * @param  m  The message to be written to the debug logger.
1155   */
1156  public static void debugMonitor(final Entry e, final String m)
1157  {
1158    if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
1159    {
1160      debugMonitor(Level.FINE, e, m);
1161    }
1162  }
1163
1164
1165
1166  /**
1167   * Writes debug information about monitor entry parsing, if appropriate.
1168   *
1169   * @param  l  The log level that should be used for the debug information.
1170   * @param  e  The entry containing the monitor information being parsed.
1171   * @param  m  The message to be written to the debug logger.
1172   */
1173  public static void debugMonitor(final Level l, final Entry e, final String m)
1174  {
1175    if (debugEnabled && debugTypes.contains(DebugType.MONITOR))
1176    {
1177      final StringBuilder buffer = new StringBuilder();
1178      addCommonHeader(buffer, l);
1179      buffer.append("monitorEntryDN=\"");
1180      buffer.append(e.getDN());
1181      buffer.append("\" message=\"");
1182      buffer.append(m);
1183      buffer.append('"');
1184
1185      logger.log(l, buffer.toString());
1186    }
1187  }
1188
1189
1190
1191  /**
1192   * Writes debug information about a coding error detected in the use of the
1193   * LDAP SDK.  If it is to be logged, then it will be sent to the underlying
1194   * logger using the {@code SEVERE} level.
1195   *
1196   * @param  t  The {@code Throwable} object that was created and will be thrown
1197   *            as a result of the coding error.
1198   */
1199  public static void debugCodingError(final Throwable t)
1200  {
1201    if (debugEnabled && debugTypes.contains(DebugType.CODING_ERROR))
1202    {
1203      final StringBuilder buffer = new StringBuilder();
1204      addCommonHeader(buffer, Level.SEVERE);
1205      buffer.append("codingError=\"");
1206      getStackTrace(t, buffer);
1207      buffer.append('"');
1208
1209      logger.log(Level.SEVERE, buffer.toString());
1210    }
1211  }
1212
1213
1214
1215  /**
1216   * Writes a generic debug message, if appropriate.
1217   *
1218   * @param  l  The log level that should be used for the debug information.
1219   * @param  t  The debug type to use to determine whether to write the message.
1220   * @param  m  The message to be written.
1221   */
1222  public static void debug(final Level l, final DebugType t, final String m)
1223  {
1224    if (debugEnabled && debugTypes.contains(t))
1225    {
1226      final StringBuilder buffer = new StringBuilder();
1227      addCommonHeader(buffer, l);
1228      buffer.append("message=\"");
1229      buffer.append(m);
1230      buffer.append('"');
1231
1232      logger.log(l, buffer.toString());
1233    }
1234  }
1235
1236
1237
1238  /**
1239   * Writes a generic debug message, if appropriate.
1240   *
1241   * @param  l  The log level that should be used for the debug information.
1242   * @param  t  The debug type to use to determine whether to write the message.
1243   * @param  m  The message to be written.
1244   * @param  e  An exception to include with the log message.
1245   */
1246  public static void debug(final Level l, final DebugType t, final String m,
1247                           final Throwable e)
1248  {
1249    if (debugEnabled && debugTypes.contains(t))
1250    {
1251      final StringBuilder buffer = new StringBuilder();
1252      addCommonHeader(buffer, l);
1253      buffer.append("message=\"");
1254      buffer.append(m);
1255      buffer.append('"');
1256      buffer.append(" exception=\"");
1257      getStackTrace(e, buffer);
1258      buffer.append('"');
1259
1260      logger.log(l, buffer.toString(), e);
1261    }
1262  }
1263
1264
1265
1266  /**
1267   * Writes common header information to the provided buffer.  It will include
1268   * the thread ID, name, and caller stack trace (optional), and it will be
1269   * followed by a trailing space.
1270   *
1271   * @param  buffer  The buffer to which the information should be appended.
1272   * @param  level   The log level for the message that will be written.
1273   */
1274  private static void addCommonHeader(final StringBuilder buffer,
1275                                      final Level level)
1276  {
1277    buffer.append("level=\"");
1278    buffer.append(level.getName());
1279    buffer.append("\" threadID=");
1280    buffer.append(Thread.currentThread().getId());
1281    buffer.append(" threadName=\"");
1282    buffer.append(Thread.currentThread().getName());
1283
1284    if (includeStackTrace)
1285    {
1286      buffer.append("\" calledFrom=\"");
1287
1288      boolean appended   = false;
1289      boolean foundDebug = false;
1290      for (final StackTraceElement e : Thread.currentThread().getStackTrace())
1291      {
1292        final String className = e.getClassName();
1293        if (className.equals(Debug.class.getName()))
1294        {
1295          foundDebug = true;
1296        }
1297        else if (foundDebug)
1298        {
1299          if (appended)
1300          {
1301            buffer.append(" / ");
1302          }
1303          appended = true;
1304
1305          buffer.append(e.getMethodName());
1306          buffer.append('(');
1307          buffer.append(e.getFileName());
1308
1309          final int lineNumber = e.getLineNumber();
1310          if (lineNumber > 0)
1311          {
1312            buffer.append(':');
1313            buffer.append(lineNumber);
1314          }
1315          else if (e.isNativeMethod())
1316          {
1317            buffer.append(":native");
1318          }
1319
1320          buffer.append(')');
1321        }
1322      }
1323    }
1324
1325    buffer.append("\" revision=");
1326    buffer.append(Version.REVISION_NUMBER);
1327    buffer.append(' ');
1328  }
1329}