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