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