001/* 002 * Copyright 2010-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2010-2024 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) 2010-2024 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.ldap.listener; 037 038 039 040import java.net.Socket; 041import java.text.DecimalFormat; 042import java.text.SimpleDateFormat; 043import java.util.Date; 044import java.util.Iterator; 045import java.util.List; 046import java.util.concurrent.ConcurrentHashMap; 047import java.util.concurrent.atomic.AtomicLong; 048import java.util.logging.Handler; 049import java.util.logging.Level; 050import java.util.logging.LogRecord; 051 052import com.unboundid.ldap.protocol.AbandonRequestProtocolOp; 053import com.unboundid.ldap.protocol.AddRequestProtocolOp; 054import com.unboundid.ldap.protocol.AddResponseProtocolOp; 055import com.unboundid.ldap.protocol.BindRequestProtocolOp; 056import com.unboundid.ldap.protocol.BindResponseProtocolOp; 057import com.unboundid.ldap.protocol.CompareRequestProtocolOp; 058import com.unboundid.ldap.protocol.CompareResponseProtocolOp; 059import com.unboundid.ldap.protocol.DeleteRequestProtocolOp; 060import com.unboundid.ldap.protocol.DeleteResponseProtocolOp; 061import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp; 062import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp; 063import com.unboundid.ldap.protocol.LDAPMessage; 064import com.unboundid.ldap.protocol.ModifyRequestProtocolOp; 065import com.unboundid.ldap.protocol.ModifyResponseProtocolOp; 066import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp; 067import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp; 068import com.unboundid.ldap.protocol.SearchRequestProtocolOp; 069import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp; 070import com.unboundid.ldap.protocol.SearchResultEntryProtocolOp; 071import com.unboundid.ldap.protocol.UnbindRequestProtocolOp; 072import com.unboundid.ldap.sdk.Control; 073import com.unboundid.ldap.sdk.LDAPException; 074import com.unboundid.util.NotMutable; 075import com.unboundid.util.NotNull; 076import com.unboundid.util.Nullable; 077import com.unboundid.util.ObjectPair; 078import com.unboundid.util.StaticUtils; 079import com.unboundid.util.ThreadSafety; 080import com.unboundid.util.ThreadSafetyLevel; 081import com.unboundid.util.Validator; 082 083 084 085/** 086 * This class provides a request handler that may be used to log each request 087 * and result using the Java logging framework. It will be also be associated 088 * with another request handler that will actually be used to handle the 089 * request. 090 */ 091@NotMutable() 092@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 093public final class AccessLogRequestHandler 094 extends LDAPListenerRequestHandler 095 implements SearchEntryTransformer 096{ 097 // The operation ID counter that will be used for this request handler 098 // instance. 099 @Nullable private final AtomicLong nextOperationID; 100 101 // A map used to correlate the number of search result entries returned for a 102 // particular message ID. 103 @NotNull private final ConcurrentHashMap<Integer,AtomicLong> entryCounts = 104 new ConcurrentHashMap<>(StaticUtils.computeMapCapacity(50)); 105 106 // The log handler that will be used to log the messages. 107 @NotNull private final Handler logHandler; 108 109 // The client connection with which this request handler is associated. 110 @Nullable private final LDAPListenerClientConnection clientConnection; 111 112 // The request handler that actually will be used to process any requests 113 // received. 114 @NotNull private final LDAPListenerRequestHandler requestHandler; 115 116 // The thread-local decimal formatters that will be used to format etime 117 // values. 118 @NotNull private final ThreadLocal<DecimalFormat> decimalFormatters; 119 120 // The thread-local date formatters that will be used to format timestamps. 121 @NotNull private final ThreadLocal<SimpleDateFormat> timestampFormatters; 122 123 // The thread-local string builders that will be used to build log messages. 124 @NotNull private final ThreadLocal<StringBuilder> buffers; 125 126 127 128 /** 129 * Creates a new access log request handler that will log request and result 130 * messages using the provided log handler, and will process client requests 131 * using the provided request handler. 132 * 133 * @param logHandler The log handler that will be used to log request 134 * and result messages. Note that all messages will 135 * be logged at the INFO level. It must not be 136 * {@code null}. Note that the log handler will not 137 * be automatically closed when the associated 138 * listener is shut down. 139 * @param requestHandler The request handler that will actually be used to 140 * process any requests received. It must not be 141 * {@code null}. 142 */ 143 public AccessLogRequestHandler(@NotNull final Handler logHandler, 144 @NotNull final LDAPListenerRequestHandler requestHandler) 145 { 146 Validator.ensureNotNull(logHandler, requestHandler); 147 148 this.logHandler = logHandler; 149 this.requestHandler = requestHandler; 150 151 decimalFormatters = new ThreadLocal<>(); 152 timestampFormatters = new ThreadLocal<>(); 153 buffers = new ThreadLocal<>(); 154 155 nextOperationID = null; 156 clientConnection = null; 157 } 158 159 160 161 /** 162 * Creates a new access log request handler that will log request and result 163 * messages using the provided log handler, and will process client requests 164 * using the provided request handler. 165 * 166 * @param logHandler The log handler that will be used to log request 167 * and result messages. Note that all messages will 168 * be logged at the INFO level. It must not be 169 * {@code null}. 170 * @param requestHandler The request handler that will actually be used to 171 * process any requests received. It must not be 172 * {@code null}. 173 * @param clientConnection The client connection with which this instance is 174 * associated. 175 * @param buffers The thread-local string builders that will be 176 * used to build log messages. 177 * @param timestampFormatters The thread-local date formatters that will be 178 * used to format timestamps. 179 * @param decimalFormatters The thread-local decimal formatters that 180 * will be used to format etime values. 181 */ 182 private AccessLogRequestHandler(@NotNull final Handler logHandler, 183 @NotNull final LDAPListenerRequestHandler requestHandler, 184 @NotNull final LDAPListenerClientConnection clientConnection, 185 @NotNull final ThreadLocal<StringBuilder> buffers, 186 @NotNull final ThreadLocal<SimpleDateFormat> timestampFormatters, 187 @NotNull final ThreadLocal<DecimalFormat> decimalFormatters) 188 { 189 this.logHandler = logHandler; 190 this.requestHandler = requestHandler; 191 this.clientConnection = clientConnection; 192 this.buffers = buffers; 193 this.timestampFormatters = timestampFormatters; 194 this.decimalFormatters = decimalFormatters; 195 196 nextOperationID = new AtomicLong(0L); 197 } 198 199 200 201 /** 202 * {@inheritDoc} 203 */ 204 @Override() 205 @NotNull() 206 public AccessLogRequestHandler newInstance( 207 @NotNull final LDAPListenerClientConnection connection) 208 throws LDAPException 209 { 210 final AccessLogRequestHandler h = new AccessLogRequestHandler(logHandler, 211 requestHandler.newInstance(connection), connection, buffers, 212 timestampFormatters, decimalFormatters); 213 connection.addSearchEntryTransformer(h); 214 215 final StringBuilder b = h.getConnectionHeader("CONNECT"); 216 217 final Socket s = connection.getSocket(); 218 b.append(" from=\""); 219 b.append(s.getInetAddress().getHostAddress()); 220 b.append(':'); 221 b.append(s.getPort()); 222 b.append("\" to=\""); 223 b.append(s.getLocalAddress().getHostAddress()); 224 b.append(':'); 225 b.append(s.getLocalPort()); 226 b.append('"'); 227 228 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 229 logHandler.flush(); 230 231 return h; 232 } 233 234 235 236 /** 237 * {@inheritDoc} 238 */ 239 @Override() 240 public void closeInstance() 241 { 242 final StringBuilder b = getConnectionHeader("DISCONNECT"); 243 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 244 logHandler.flush(); 245 246 requestHandler.closeInstance(); 247 } 248 249 250 251 /** 252 * {@inheritDoc} 253 */ 254 @Override() 255 public void processAbandonRequest(final int messageID, 256 @NotNull final AbandonRequestProtocolOp request, 257 @NotNull final List<Control> controls) 258 { 259 final StringBuilder b = getRequestHeader("ABANDON", 260 nextOperationID.getAndIncrement(), messageID); 261 262 b.append(" idToAbandon="); 263 b.append(request.getIDToAbandon()); 264 265 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 266 logHandler.flush(); 267 268 requestHandler.processAbandonRequest(messageID, request, controls); 269 } 270 271 272 273 /** 274 * {@inheritDoc} 275 */ 276 @Override() 277 @NotNull() 278 public LDAPMessage processAddRequest(final int messageID, 279 @NotNull final AddRequestProtocolOp request, 280 @NotNull final List<Control> controls) 281 { 282 final long opID = nextOperationID.getAndIncrement(); 283 284 final StringBuilder b = getRequestHeader("ADD", opID, messageID); 285 286 b.append(" dn=\""); 287 b.append(request.getDN()); 288 b.append('"'); 289 290 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 291 logHandler.flush(); 292 293 final long startTimeNanos = System.nanoTime(); 294 final LDAPMessage responseMessage = requestHandler.processAddRequest( 295 messageID, request, controls); 296 final long eTimeNanos = System.nanoTime() - startTimeNanos; 297 final AddResponseProtocolOp protocolOp = 298 responseMessage.getAddResponseProtocolOp(); 299 300 generateResponse(b, "ADD", opID, messageID, protocolOp.getResultCode(), 301 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 302 protocolOp.getReferralURLs(), eTimeNanos); 303 304 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 305 logHandler.flush(); 306 307 return responseMessage; 308 } 309 310 311 312 /** 313 * {@inheritDoc} 314 */ 315 @Override() 316 @NotNull() 317 public LDAPMessage processBindRequest(final int messageID, 318 @NotNull final BindRequestProtocolOp request, 319 @NotNull final List<Control> controls) 320 { 321 final long opID = nextOperationID.getAndIncrement(); 322 323 final StringBuilder b = getRequestHeader("BIND", opID, messageID); 324 325 b.append(" version="); 326 b.append(request.getVersion()); 327 b.append(" dn=\""); 328 b.append(request.getBindDN()); 329 b.append("\" authType=\""); 330 331 switch (request.getCredentialsType()) 332 { 333 case BindRequestProtocolOp.CRED_TYPE_SIMPLE: 334 b.append("SIMPLE"); 335 break; 336 337 case BindRequestProtocolOp.CRED_TYPE_SASL: 338 b.append("SASL "); 339 b.append(request.getSASLMechanism()); 340 break; 341 } 342 343 b.append('"'); 344 345 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 346 logHandler.flush(); 347 348 final long startTimeNanos = System.nanoTime(); 349 final LDAPMessage responseMessage = requestHandler.processBindRequest( 350 messageID, request, controls); 351 final long eTimeNanos = System.nanoTime() - startTimeNanos; 352 final BindResponseProtocolOp protocolOp = 353 responseMessage.getBindResponseProtocolOp(); 354 355 generateResponse(b, "BIND", opID, messageID, protocolOp.getResultCode(), 356 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 357 protocolOp.getReferralURLs(), eTimeNanos); 358 359 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 360 logHandler.flush(); 361 362 return responseMessage; 363 } 364 365 366 367 /** 368 * {@inheritDoc} 369 */ 370 @Override() 371 @NotNull() 372 public LDAPMessage processCompareRequest(final int messageID, 373 @NotNull final CompareRequestProtocolOp request, 374 @NotNull final List<Control> controls) 375 { 376 final long opID = nextOperationID.getAndIncrement(); 377 378 final StringBuilder b = getRequestHeader("COMPARE", opID, messageID); 379 380 b.append(" dn=\""); 381 b.append(request.getDN()); 382 b.append("\" attr=\""); 383 b.append(request.getAttributeName()); 384 b.append('"'); 385 386 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 387 logHandler.flush(); 388 389 final long startTimeNanos = System.nanoTime(); 390 final LDAPMessage responseMessage = requestHandler.processCompareRequest( 391 messageID, request, controls); 392 final long eTimeNanos = System.nanoTime() - startTimeNanos; 393 final CompareResponseProtocolOp protocolOp = 394 responseMessage.getCompareResponseProtocolOp(); 395 396 generateResponse(b, "COMPARE", opID, messageID, protocolOp.getResultCode(), 397 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 398 protocolOp.getReferralURLs(), eTimeNanos); 399 400 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 401 logHandler.flush(); 402 403 return responseMessage; 404 } 405 406 407 408 /** 409 * {@inheritDoc} 410 */ 411 @Override() 412 @NotNull() 413 public LDAPMessage processDeleteRequest(final int messageID, 414 @NotNull final DeleteRequestProtocolOp request, 415 @NotNull final List<Control> controls) 416 { 417 final long opID = nextOperationID.getAndIncrement(); 418 419 final StringBuilder b = getRequestHeader("DELETE", opID, messageID); 420 421 b.append(" dn=\""); 422 b.append(request.getDN()); 423 b.append('"'); 424 425 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 426 logHandler.flush(); 427 428 final long startTimeNanos = System.nanoTime(); 429 final LDAPMessage responseMessage = requestHandler.processDeleteRequest( 430 messageID, request, controls); 431 final long eTimeNanos = System.nanoTime() - startTimeNanos; 432 final DeleteResponseProtocolOp protocolOp = 433 responseMessage.getDeleteResponseProtocolOp(); 434 435 generateResponse(b, "DELETE", opID, messageID, protocolOp.getResultCode(), 436 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 437 protocolOp.getReferralURLs(), eTimeNanos); 438 439 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 440 logHandler.flush(); 441 442 return responseMessage; 443 } 444 445 446 447 /** 448 * {@inheritDoc} 449 */ 450 @Override() 451 @NotNull() 452 public LDAPMessage processExtendedRequest(final int messageID, 453 @NotNull final ExtendedRequestProtocolOp request, 454 @NotNull final List<Control> controls) 455 { 456 final long opID = nextOperationID.getAndIncrement(); 457 458 final StringBuilder b = getRequestHeader("EXTENDED", opID, messageID); 459 460 b.append(" requestOID=\""); 461 b.append(request.getOID()); 462 b.append('"'); 463 464 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 465 logHandler.flush(); 466 467 final long startTimeNanos = System.nanoTime(); 468 final LDAPMessage responseMessage = requestHandler.processExtendedRequest( 469 messageID, request, controls); 470 final long eTimeNanos = System.nanoTime() - startTimeNanos; 471 final ExtendedResponseProtocolOp protocolOp = 472 responseMessage.getExtendedResponseProtocolOp(); 473 474 generateResponse(b, "EXTENDED", opID, messageID, protocolOp.getResultCode(), 475 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 476 protocolOp.getReferralURLs(), eTimeNanos); 477 478 final String responseOID = protocolOp.getResponseOID(); 479 if (responseOID != null) 480 { 481 b.append(" responseOID=\""); 482 b.append(responseOID); 483 b.append('"'); 484 } 485 486 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 487 logHandler.flush(); 488 489 return responseMessage; 490 } 491 492 493 494 /** 495 * {@inheritDoc} 496 */ 497 @Override() 498 @NotNull() 499 public LDAPMessage processModifyRequest(final int messageID, 500 @NotNull final ModifyRequestProtocolOp request, 501 @NotNull final List<Control> controls) 502 { 503 final long opID = nextOperationID.getAndIncrement(); 504 505 final StringBuilder b = getRequestHeader("MODIFY", opID, messageID); 506 507 b.append(" dn=\""); 508 b.append(request.getDN()); 509 b.append('"'); 510 511 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 512 logHandler.flush(); 513 514 final long startTimeNanos = System.nanoTime(); 515 final LDAPMessage responseMessage = requestHandler.processModifyRequest( 516 messageID, request, controls); 517 final long eTimeNanos = System.nanoTime() - startTimeNanos; 518 final ModifyResponseProtocolOp protocolOp = 519 responseMessage.getModifyResponseProtocolOp(); 520 521 generateResponse(b, "MODIFY", opID, messageID, protocolOp.getResultCode(), 522 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 523 protocolOp.getReferralURLs(), eTimeNanos); 524 525 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 526 logHandler.flush(); 527 528 return responseMessage; 529 } 530 531 532 533 /** 534 * {@inheritDoc} 535 */ 536 @Override() 537 @NotNull() 538 public LDAPMessage processModifyDNRequest(final int messageID, 539 @NotNull final ModifyDNRequestProtocolOp request, 540 @NotNull final List<Control> controls) 541 { 542 final long opID = nextOperationID.getAndIncrement(); 543 544 final StringBuilder b = getRequestHeader("MODDN", opID, messageID); 545 546 b.append(" dn=\""); 547 b.append(request.getDN()); 548 b.append("\" newRDN=\""); 549 b.append(request.getNewRDN()); 550 b.append("\" deleteOldRDN="); 551 b.append(request.deleteOldRDN()); 552 553 final String newSuperior = request.getNewSuperiorDN(); 554 if (newSuperior != null) 555 { 556 b.append(" newSuperior=\""); 557 b.append(newSuperior); 558 b.append('"'); 559 } 560 561 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 562 logHandler.flush(); 563 564 final long startTimeNanos = System.nanoTime(); 565 final LDAPMessage responseMessage = requestHandler.processModifyDNRequest( 566 messageID, request, controls); 567 final long eTimeNanos = System.nanoTime() - startTimeNanos; 568 final ModifyDNResponseProtocolOp protocolOp = 569 responseMessage.getModifyDNResponseProtocolOp(); 570 571 generateResponse(b, "MODDN", opID, messageID, protocolOp.getResultCode(), 572 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 573 protocolOp.getReferralURLs(), eTimeNanos); 574 575 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 576 logHandler.flush(); 577 578 return responseMessage; 579 } 580 581 582 583 /** 584 * {@inheritDoc} 585 */ 586 @Override() 587 @NotNull() 588 public LDAPMessage processSearchRequest(final int messageID, 589 @NotNull final SearchRequestProtocolOp request, 590 @NotNull final List<Control> controls) 591 { 592 final long opID = nextOperationID.getAndIncrement(); 593 594 final StringBuilder b = getRequestHeader("SEARCH", opID, messageID); 595 596 b.append(" base=\""); 597 b.append(request.getBaseDN()); 598 b.append("\" scope="); 599 b.append(request.getScope().intValue()); 600 b.append(" filter=\""); 601 request.getFilter().toString(b); 602 b.append("\" attrs=\""); 603 604 final List<String> attrList = request.getAttributes(); 605 if (attrList.isEmpty()) 606 { 607 b.append("ALL"); 608 } 609 else 610 { 611 final Iterator<String> iterator = attrList.iterator(); 612 while (iterator.hasNext()) 613 { 614 b.append(iterator.next()); 615 if (iterator.hasNext()) 616 { 617 b.append(','); 618 } 619 } 620 } 621 622 b.append('"'); 623 624 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 625 logHandler.flush(); 626 627 final AtomicLong l = new AtomicLong(0L); 628 entryCounts.put(messageID, l); 629 630 try 631 { 632 final long startTimeNanos = System.nanoTime(); 633 final LDAPMessage responseMessage = requestHandler.processSearchRequest( 634 messageID, request, controls); 635 final long eTimeNanos = System.nanoTime() - startTimeNanos; 636 final SearchResultDoneProtocolOp protocolOp = 637 responseMessage.getSearchResultDoneProtocolOp(); 638 639 generateResponse(b, "SEARCH", opID, messageID, protocolOp.getResultCode(), 640 protocolOp.getDiagnosticMessage(), protocolOp.getMatchedDN(), 641 protocolOp.getReferralURLs(), eTimeNanos); 642 643 b.append(" entriesReturned="); 644 b.append(l.get()); 645 646 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 647 logHandler.flush(); 648 649 return responseMessage; 650 } 651 finally 652 { 653 entryCounts.remove(messageID); 654 } 655 } 656 657 658 659 /** 660 * {@inheritDoc} 661 */ 662 @Override() 663 public void processUnbindRequest(final int messageID, 664 @NotNull final UnbindRequestProtocolOp request, 665 @NotNull final List<Control> controls) 666 { 667 final StringBuilder b = getRequestHeader("UNBIND", 668 nextOperationID.getAndIncrement(), messageID); 669 670 logHandler.publish(new LogRecord(Level.INFO, b.toString())); 671 logHandler.flush(); 672 673 requestHandler.processUnbindRequest(messageID, request, controls); 674 } 675 676 677 678 /** 679 * Retrieves a string builder that can be used to construct a log message. 680 * 681 * @return A string builder that can be used to construct a log message. 682 */ 683 @NotNull() 684 private StringBuilder getBuffer() 685 { 686 StringBuilder b = buffers.get(); 687 if (b == null) 688 { 689 b = new StringBuilder(); 690 buffers.set(b); 691 } 692 else 693 { 694 b.setLength(0); 695 } 696 697 return b; 698 } 699 700 701 702 /** 703 * Adds a timestamp to the beginning of the provided buffer. 704 * 705 * @param buffer The buffer to which the timestamp should be added. 706 */ 707 private void addTimestamp(@NotNull final StringBuilder buffer) 708 { 709 SimpleDateFormat dateFormat = timestampFormatters.get(); 710 if (dateFormat == null) 711 { 712 dateFormat = new SimpleDateFormat("'['dd/MMM/yyyy:HH:mm:ss Z']'"); 713 timestampFormatters.set(dateFormat); 714 } 715 716 buffer.append(dateFormat.format(new Date())); 717 } 718 719 720 721 /** 722 * Retrieves a {@code StringBuilder} with header information for a request log 723 * message for the specified type of operation. 724 * 725 * @param messageType The type of operation being requested. 726 * 727 * @return A {@code StringBuilder} with header information appended for the 728 * request; 729 */ 730 @NotNull() 731 private StringBuilder getConnectionHeader(@NotNull final String messageType) 732 { 733 final StringBuilder b = getBuffer(); 734 addTimestamp(b); 735 b.append(' '); 736 b.append(messageType); 737 b.append(" conn="); 738 b.append(clientConnection.getConnectionID()); 739 740 return b; 741 } 742 743 744 745 /** 746 * Retrieves a {@code StringBuilder} with header information for a request log 747 * message for the specified type of operation. 748 * 749 * @param opType The type of operation being requested. 750 * @param opID The operation ID for the request. 751 * @param msgID The message ID for the request. 752 * 753 * @return A {@code StringBuilder} with header information appended for the 754 * request; 755 */ 756 @NotNull() 757 private StringBuilder getRequestHeader(@NotNull final String opType, 758 final long opID, final int msgID) 759 { 760 final StringBuilder b = getBuffer(); 761 addTimestamp(b); 762 b.append(' '); 763 b.append(opType); 764 b.append(" REQUEST conn="); 765 b.append(clientConnection.getConnectionID()); 766 b.append(" op="); 767 b.append(opID); 768 b.append(" msgID="); 769 b.append(msgID); 770 771 return b; 772 } 773 774 775 776 /** 777 * Writes information about the result of processing an operation to the 778 * given buffer. 779 * 780 * @param b The buffer to which the information should be 781 * written. The buffer will be cleared before 782 * adding any additional content. 783 * @param opType The type of operation that was processed. 784 * @param opID The operation ID for the response. 785 * @param msgID The message ID for the response. 786 * @param resultCode The result code for the response, if any. 787 * @param diagnosticMessage The diagnostic message for the response, if any. 788 * @param matchedDN The matched DN for the response, if any. 789 * @param referralURLs The referral URLs for the response, if any. 790 * @param eTimeNanos The length of time in nanoseconds required to 791 * process the operation. 792 */ 793 private void generateResponse(@NotNull final StringBuilder b, 794 @NotNull final String opType, 795 final long opID, final int msgID, 796 final int resultCode, 797 @Nullable final String diagnosticMessage, 798 @Nullable final String matchedDN, 799 @NotNull final List<String> referralURLs, 800 final long eTimeNanos) 801 { 802 b.setLength(0); 803 addTimestamp(b); 804 b.append(' '); 805 b.append(opType); 806 b.append(" RESULT conn="); 807 b.append(clientConnection.getConnectionID()); 808 b.append(" op="); 809 b.append(opID); 810 b.append(" msgID="); 811 b.append(msgID); 812 b.append(" resultCode="); 813 b.append(resultCode); 814 815 if (diagnosticMessage != null) 816 { 817 b.append(" diagnosticMessage=\""); 818 b.append(diagnosticMessage); 819 b.append('"'); 820 } 821 822 if (matchedDN != null) 823 { 824 b.append(" matchedDN=\""); 825 b.append(matchedDN); 826 b.append('"'); 827 } 828 829 if (! referralURLs.isEmpty()) 830 { 831 b.append(" referralURLs=\""); 832 final Iterator<String> iterator = referralURLs.iterator(); 833 while (iterator.hasNext()) 834 { 835 b.append(iterator.next()); 836 837 if (iterator.hasNext()) 838 { 839 b.append(','); 840 } 841 } 842 843 b.append('"'); 844 } 845 846 DecimalFormat f = decimalFormatters.get(); 847 if (f == null) 848 { 849 f = new DecimalFormat("0.000"); 850 decimalFormatters.set(f); 851 } 852 853 b.append(" etime="); 854 b.append(f.format(eTimeNanos / 1_000_000.0d)); 855 } 856 857 858 859 /** 860 * {@inheritDoc} 861 */ 862 @Override() 863 @NotNull() 864 public ObjectPair<SearchResultEntryProtocolOp,Control[]> transformEntry( 865 final int messageID, 866 @NotNull final SearchResultEntryProtocolOp entry, 867 @NotNull final Control[] controls) 868 { 869 final AtomicLong l = entryCounts.get(messageID); 870 if (l != null) 871 { 872 l.incrementAndGet(); 873 } 874 875 return new ObjectPair<>(entry, controls); 876 } 877}