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