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