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