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