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 StaticUtils.setLoggerLevel(logger, 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 StaticUtils.setLoggerLevel(logger, 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("' valueLength="); 1155 buffer.append(length); 1156 1157 if (value != null) 1158 { 1159 buffer.append(" value='"); 1160 if (value instanceof byte[]) 1161 { 1162 StaticUtils.toHex((byte[]) value, buffer); 1163 } 1164 else 1165 { 1166 buffer.append(value); 1167 } 1168 buffer.append('\''); 1169 } 1170 buffer.append('"'); 1171 1172 logger.log(l, buffer.toString()); 1173 } 1174 } 1175 1176 1177 1178 /** 1179 * Writes debug information about the provided LDIF record to be written, if 1180 * if appropriate. If it is to be logged, then it will be sent to the 1181 * underlying logger using the {@code INFO} level. 1182 * 1183 * @param r The LDIF record for which debug information should be written. 1184 */ 1185 public static void debugLDIFWrite(final LDIFRecord r) 1186 { 1187 if (debugEnabled && debugTypes.contains(DebugType.LDIF)) 1188 { 1189 debugLDIFWrite(Level.INFO, r); 1190 } 1191 } 1192 1193 1194 1195 /** 1196 * Writes debug information about the provided LDIF record to be written, if 1197 * appropriate. 1198 * 1199 * @param l The log level that should be used for the debug information. 1200 * @param r The LDIF record for which debug information should be written. 1201 */ 1202 public static void debugLDIFWrite(final Level l, final LDIFRecord r) 1203 { 1204 if (debugEnabled && debugTypes.contains(DebugType.LDIF)) 1205 { 1206 final StringBuilder buffer = new StringBuilder(); 1207 addCommonHeader(buffer, l); 1208 buffer.append("writingLDIFRecord=\""); 1209 r.toString(buffer); 1210 buffer.append('"'); 1211 1212 logger.log(l, buffer.toString()); 1213 } 1214 } 1215 1216 1217 1218 /** 1219 * Writes debug information about the provided record read from LDIF, if 1220 * appropriate. If it is to be logged, then it will be sent to the underlying 1221 * logger using the {@code INFO} level. 1222 * 1223 * @param r The LDIF record for which debug information should be written. 1224 */ 1225 public static void debugLDIFRead(final LDIFRecord r) 1226 { 1227 if (debugEnabled && debugTypes.contains(DebugType.LDIF)) 1228 { 1229 debugLDIFRead(Level.INFO, r); 1230 } 1231 } 1232 1233 1234 1235 /** 1236 * Writes debug information about the provided record read from LDIF, if 1237 * appropriate. 1238 * 1239 * @param l The log level that should be used for the debug information. 1240 * @param r The LDIF record for which debug information should be written. 1241 */ 1242 public static void debugLDIFRead(final Level l, final LDIFRecord r) 1243 { 1244 if (debugEnabled && debugTypes.contains(DebugType.LDIF)) 1245 { 1246 final StringBuilder buffer = new StringBuilder(); 1247 addCommonHeader(buffer, l); 1248 buffer.append("readLDIFRecord=\""); 1249 r.toString(buffer); 1250 buffer.append('"'); 1251 1252 logger.log(l, buffer.toString()); 1253 } 1254 } 1255 1256 1257 1258 /** 1259 * Writes debug information about monitor entry parsing. If it is to be 1260 * logged, then it will be sent to the underlying logger using the 1261 * {@code FINE} level. 1262 * 1263 * @param e The entry containing the monitor information being parsed. 1264 * @param m The message to be written to the debug logger. 1265 */ 1266 public static void debugMonitor(final Entry e, final String m) 1267 { 1268 if (debugEnabled && debugTypes.contains(DebugType.MONITOR)) 1269 { 1270 debugMonitor(Level.FINE, e, m); 1271 } 1272 } 1273 1274 1275 1276 /** 1277 * Writes debug information about monitor entry parsing, if appropriate. 1278 * 1279 * @param l The log level that should be used for the debug information. 1280 * @param e The entry containing the monitor information being parsed. 1281 * @param m The message to be written to the debug logger. 1282 */ 1283 public static void debugMonitor(final Level l, final Entry e, final String m) 1284 { 1285 if (debugEnabled && debugTypes.contains(DebugType.MONITOR)) 1286 { 1287 final StringBuilder buffer = new StringBuilder(); 1288 addCommonHeader(buffer, l); 1289 buffer.append("monitorEntryDN=\""); 1290 buffer.append(e.getDN()); 1291 buffer.append("\" message=\""); 1292 buffer.append(m); 1293 buffer.append('"'); 1294 1295 logger.log(l, buffer.toString()); 1296 } 1297 } 1298 1299 1300 1301 /** 1302 * Writes debug information about a coding error detected in the use of the 1303 * LDAP SDK. If it is to be logged, then it will be sent to the underlying 1304 * logger using the {@code SEVERE} level. 1305 * 1306 * @param t The {@code Throwable} object that was created and will be thrown 1307 * as a result of the coding error. 1308 */ 1309 public static void debugCodingError(final Throwable t) 1310 { 1311 if (debugEnabled && debugTypes.contains(DebugType.CODING_ERROR)) 1312 { 1313 final StringBuilder buffer = new StringBuilder(); 1314 addCommonHeader(buffer, Level.SEVERE); 1315 buffer.append("codingError=\""); 1316 StaticUtils.getStackTrace(t, buffer); 1317 buffer.append('"'); 1318 1319 logger.log(Level.SEVERE, buffer.toString()); 1320 } 1321 } 1322 1323 1324 1325 /** 1326 * Writes a generic debug message, if appropriate. 1327 * 1328 * @param l The log level that should be used for the debug information. 1329 * @param t The debug type to use to determine whether to write the message. 1330 * @param m The message to be written. 1331 */ 1332 public static void debug(final Level l, final DebugType t, final String m) 1333 { 1334 if (debugEnabled && debugTypes.contains(t)) 1335 { 1336 final StringBuilder buffer = new StringBuilder(); 1337 addCommonHeader(buffer, l); 1338 buffer.append("message=\""); 1339 buffer.append(m); 1340 buffer.append('"'); 1341 1342 logger.log(l, buffer.toString()); 1343 } 1344 } 1345 1346 1347 1348 /** 1349 * Writes a generic debug message, if appropriate. 1350 * 1351 * @param l The log level that should be used for the debug information. 1352 * @param t The debug type to use to determine whether to write the message. 1353 * @param m The message to be written. 1354 * @param e An exception to include with the log message. 1355 */ 1356 public static void debug(final Level l, final DebugType t, final String m, 1357 final Throwable e) 1358 { 1359 if (debugEnabled && debugTypes.contains(t)) 1360 { 1361 final StringBuilder buffer = new StringBuilder(); 1362 addCommonHeader(buffer, l); 1363 buffer.append("message=\""); 1364 buffer.append(m); 1365 buffer.append('"'); 1366 buffer.append(" exception=\""); 1367 StaticUtils.getStackTrace(e, buffer); 1368 buffer.append('"'); 1369 1370 logger.log(l, buffer.toString(), e); 1371 } 1372 } 1373 1374 1375 1376 /** 1377 * Writes common header information to the provided buffer. It will include 1378 * the thread ID, name, and caller stack trace (optional), and it will be 1379 * followed by a trailing space. 1380 * 1381 * @param buffer The buffer to which the information should be appended. 1382 * @param level The log level for the message that will be written. 1383 */ 1384 private static void addCommonHeader(final StringBuilder buffer, 1385 final Level level) 1386 { 1387 buffer.append("level=\""); 1388 buffer.append(level.getName()); 1389 buffer.append("\" threadID="); 1390 buffer.append(Thread.currentThread().getId()); 1391 buffer.append(" threadName=\""); 1392 buffer.append(Thread.currentThread().getName()); 1393 1394 if (includeStackTrace) 1395 { 1396 buffer.append("\" calledFrom=\""); 1397 1398 boolean appended = false; 1399 boolean foundDebug = false; 1400 for (final StackTraceElement e : Thread.currentThread().getStackTrace()) 1401 { 1402 final String className = e.getClassName(); 1403 if (className.equals(Debug.class.getName())) 1404 { 1405 foundDebug = true; 1406 } 1407 else if (foundDebug) 1408 { 1409 if (appended) 1410 { 1411 buffer.append(" / "); 1412 } 1413 appended = true; 1414 1415 buffer.append(e.getMethodName()); 1416 buffer.append('('); 1417 buffer.append(e.getFileName()); 1418 1419 final int lineNumber = e.getLineNumber(); 1420 if (lineNumber > 0) 1421 { 1422 buffer.append(':'); 1423 buffer.append(lineNumber); 1424 } 1425 else if (e.isNativeMethod()) 1426 { 1427 buffer.append(":native"); 1428 } 1429 1430 buffer.append(')'); 1431 } 1432 } 1433 } 1434 1435 buffer.append("\" ldapSDKVersion=\""); 1436 buffer.append(Version.NUMERIC_VERSION_STRING); 1437 buffer.append("\" revision=\""); 1438 buffer.append(Version.REVISION_ID); 1439 buffer.append("\" "); 1440 } 1441}