From 2ba1652a6ce1a77eb0fcc86f678f4caab376720b Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 14 Oct 2011 12:27:25 +0000
Subject: [PATCH] OPENDJ-308: Implement access log filtering and configurable message format

---
 opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java |  524 +++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 354 insertions(+), 170 deletions(-)

diff --git a/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java b/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
index 6b039ce..31913cc 100644
--- a/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
+++ b/opends/src/server/org/opends/server/loggers/TextAccessLogPublisher.java
@@ -35,6 +35,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
@@ -43,6 +44,7 @@
 import org.opends.messages.Message;
 import org.opends.messages.MessageBuilder;
 import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.meta.FileBasedAccessLogPublisherCfgDefn.*;
 import org.opends.server.admin.std.server.FileBasedAccessLogPublisherCfg;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.ExtendedOperationHandler;
@@ -100,6 +102,9 @@
 
   private TextWriter writer = null;
   private FileBasedAccessLogPublisherCfg cfg = null;
+  private boolean isCombinedMode = false;
+  private boolean includeControlOIDs = false;
+  private String timeStampFormat = "dd/MMM/yyyy:HH:mm:ss Z";
 
 
 
@@ -207,7 +212,15 @@
           adminActionRequired = true;
         }
 
+        if (!config.getLogRecordTimeFormat().equals(timeStampFormat))
+        {
+          TimeThread.removeUserDefinedFormatter(timeStampFormat);
+          timeStampFormat = config.getLogRecordTimeFormat();
+        }
+
         cfg = config;
+        isCombinedMode = cfg.getLogFormat() == LogFormat.COMBINED;
+        includeControlOIDs = cfg.isLogControlOids();
       }
     }
     catch (final Exception e)
@@ -228,21 +241,6 @@
    * {@inheritDoc}
    */
   @Override
-  protected void close0()
-  {
-    writer.shutdown();
-    if (cfg != null)
-    {
-      cfg.removeFileBasedAccessChangeListener(this);
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
   public void initializeAccessLogPublisher(
       final FileBasedAccessLogPublisherCfg cfg) throws ConfigException,
       InitializationException
@@ -311,7 +309,12 @@
     }
 
     initializeFilters(cfg);
+
     this.cfg = cfg;
+    isCombinedMode = cfg.getLogFormat() == LogFormat.COMBINED;
+    includeControlOIDs = cfg.isLogControlOids();
+    timeStampFormat = cfg.getLogRecordTimeFormat();
+
     cfg.addFileBasedAccessChangeListener(this);
   }
 
@@ -337,6 +340,20 @@
       final FileBasedAccessLogPublisherCfg config,
       final List<Message> unacceptableReasons)
   {
+    // Validate the time-stamp formatter.
+    final String formatString = config.getLogRecordTimeFormat();
+    try
+    {
+      new SimpleDateFormat(formatString);
+    }
+    catch (final Exception e)
+    {
+      final Message message = ERR_CONFIG_LOGGING_INVALID_TIME_FORMAT.get(String
+          .valueOf(formatString));
+      unacceptableReasons.add(message);
+      return false;
+    }
+
     // Make sure the permission is valid.
     try
     {
@@ -374,20 +391,14 @@
   @Override
   public void logAbandonRequest(final AbandonOperation abandonOperation)
   {
-    if (!isRequestLoggable(abandonOperation))
+    if (isCombinedMode || !isRequestLoggable(abandonOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(abandonOperation, "ABANDON", CATEGORY_REQUEST, buffer);
-    buffer.append(" idToAbandon=");
-    buffer.append(abandonOperation.getIDToAbandon());
-    if (abandonOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendAbandonRequest(abandonOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -411,6 +422,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(abandonOperation, "ABANDON", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendAbandonRequest(abandonOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(abandonOperation.getResultCode().getIntValue());
     final MessageBuilder msg = abandonOperation.getErrorMessage();
@@ -442,21 +457,14 @@
   @Override
   public void logAddRequest(final AddOperation addOperation)
   {
-    if (!isRequestLoggable(addOperation))
+    if (isCombinedMode || !isRequestLoggable(addOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(addOperation, "ADD", CATEGORY_REQUEST, buffer);
-    buffer.append(" dn=\"");
-    buffer.append(addOperation.getRawEntryDN().toString());
-    buffer.append("\"");
-    if (addOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendAddRequest(addOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -480,6 +488,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(addOperation, "ADD", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendAddRequest(addOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(addOperation.getResultCode().getIntValue());
 
@@ -525,44 +537,14 @@
   @Override
   public void logBindRequest(final BindOperation bindOperation)
   {
-    if (!isRequestLoggable(bindOperation))
+    if (isCombinedMode || !isRequestLoggable(bindOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(bindOperation, "BIND", CATEGORY_REQUEST, buffer);
-
-    final String protocolVersion = bindOperation.getProtocolVersion();
-    if (protocolVersion != null)
-    {
-      buffer.append(" version=");
-      buffer.append(protocolVersion);
-    }
-
-    switch (bindOperation.getAuthenticationType())
-    {
-    case SIMPLE:
-      buffer.append(" type=SIMPLE");
-      break;
-    case SASL:
-      buffer.append(" type=SASL mechanism=");
-      buffer.append(bindOperation.getSASLMechanism());
-      break;
-    default:
-      buffer.append(" type=");
-      buffer.append(bindOperation.getAuthenticationType());
-      break;
-    }
-
-    buffer.append(" dn=\"");
-    buffer.append(bindOperation.getRawBindDN().toString());
-    buffer.append("\"");
-    if (bindOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendBindRequest(bindOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -586,6 +568,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(bindOperation, "BIND", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendBindRequest(bindOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(bindOperation.getResultCode().getIntValue());
 
@@ -663,22 +649,14 @@
   @Override
   public void logCompareRequest(final CompareOperation compareOperation)
   {
-    if (!isRequestLoggable(compareOperation))
+    if (isCombinedMode || !isRequestLoggable(compareOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(compareOperation, "COMPARE", CATEGORY_REQUEST, buffer);
-    buffer.append(" dn=\"");
-    buffer.append(compareOperation.getRawEntryDN().toString());
-    buffer.append("\" attr=");
-    buffer.append(compareOperation.getAttributeType().getNameOrOID());
-    if (compareOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendCompareRequest(compareOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -702,6 +680,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(compareOperation, "COMPARE", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendCompareRequest(compareOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(compareOperation.getResultCode().getIntValue());
 
@@ -783,21 +765,14 @@
   @Override
   public void logDeleteRequest(final DeleteOperation deleteOperation)
   {
-    if (!isRequestLoggable(deleteOperation))
+    if (isCombinedMode || !isRequestLoggable(deleteOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(deleteOperation, "DELETE", CATEGORY_REQUEST, buffer);
-    buffer.append(" dn=\"");
-    buffer.append(deleteOperation.getRawEntryDN().toString());
-    buffer.append("\"");
-    if (deleteOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendDeleteRequest(deleteOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -821,6 +796,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(deleteOperation, "DELETE", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendDeleteRequest(deleteOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(deleteOperation.getResultCode().getIntValue());
 
@@ -910,35 +889,14 @@
   @Override
   public void logExtendedRequest(final ExtendedOperation extendedOperation)
   {
-    if (!isRequestLoggable(extendedOperation))
+    if (isCombinedMode || !isRequestLoggable(extendedOperation))
     {
       return;
     }
 
-    String name = null;
-    final String oid = extendedOperation.getRequestOID();
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(extendedOperation, "EXTENDED", CATEGORY_REQUEST, buffer);
-    final ExtendedOperationHandler<?> extOpHandler = DirectoryServer
-        .getExtendedOperationHandler(oid);
-    if (extOpHandler != null)
-    {
-      name = extOpHandler.getExtendedOperationName();
-      if (name != null)
-      {
-        buffer.append(" name=\"");
-        buffer.append(name);
-        buffer.append("\"");
-      }
-    }
-    buffer.append(" oid=\"");
-    buffer.append(oid);
-    buffer.append("\"");
-    if (extendedOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendExtendedRequest(extendedOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -962,6 +920,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(extendedOperation, "EXTENDED", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendExtendedRequest(extendedOperation, buffer);
+    }
 
     String name = null;
     final String oid = extendedOperation.getResponseOID();
@@ -1021,31 +983,14 @@
   @Override
   public void logModifyDNRequest(final ModifyDNOperation modifyDNOperation)
   {
-    if (!isRequestLoggable(modifyDNOperation))
+    if (isCombinedMode || !isRequestLoggable(modifyDNOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyDNOperation, "MODIFYDN", CATEGORY_REQUEST, buffer);
-    buffer.append(" dn=\"");
-    buffer.append(modifyDNOperation.getRawEntryDN().toString());
-    buffer.append("\" newRDN=\"");
-    buffer.append(modifyDNOperation.getRawNewRDN().toString());
-    buffer.append("\" deleteOldRDN=");
-    buffer.append(modifyDNOperation.deleteOldRDN());
-
-    final ByteString newSuperior = modifyDNOperation.getRawNewSuperior();
-    if (newSuperior != null)
-    {
-      buffer.append(" newSuperior=\"");
-      buffer.append(newSuperior.toString());
-    }
-    if (modifyDNOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendModifyDNRequest(modifyDNOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -1069,6 +1014,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyDNOperation, "MODIFYDN", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendModifyDNRequest(modifyDNOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(modifyDNOperation.getResultCode().getIntValue());
 
@@ -1114,21 +1063,14 @@
   @Override
   public void logModifyRequest(final ModifyOperation modifyOperation)
   {
-    if (!isRequestLoggable(modifyOperation))
+    if (isCombinedMode || !isRequestLoggable(modifyOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyOperation, "MODIFY", CATEGORY_REQUEST, buffer);
-    buffer.append(" dn=\"");
-    buffer.append(modifyOperation.getRawEntryDN().toString());
-    buffer.append("\"");
-    if (modifyOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendModifyRequest(modifyOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -1152,6 +1094,10 @@
 
     final StringBuilder buffer = new StringBuilder(100);
     appendHeader(modifyOperation, "MODIFY", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendModifyRequest(modifyOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(modifyOperation.getResultCode().getIntValue());
 
@@ -1197,44 +1143,14 @@
   @Override
   public void logSearchRequest(final SearchOperation searchOperation)
   {
-    if (!isRequestLoggable(searchOperation))
+    if (isCombinedMode || !isRequestLoggable(searchOperation))
     {
       return;
     }
 
     final StringBuilder buffer = new StringBuilder(192);
     appendHeader(searchOperation, "SEARCH", CATEGORY_REQUEST, buffer);
-    buffer.append(" base=\"");
-    buffer.append(searchOperation.getRawBaseDN().toString());
-    buffer.append("\" scope=");
-    buffer.append(searchOperation.getScope());
-    buffer.append(" filter=\"");
-    searchOperation.getRawFilter().toString(buffer);
-
-    final LinkedHashSet<String> attrs = searchOperation.getAttributes();
-    if ((attrs == null) || attrs.isEmpty())
-    {
-      buffer.append("\" attrs=\"ALL\"");
-    }
-    else
-    {
-      buffer.append("\" attrs=\"");
-
-      final Iterator<String> iterator = attrs.iterator();
-      buffer.append(iterator.next());
-      while (iterator.hasNext())
-      {
-        buffer.append(",");
-        buffer.append(iterator.next());
-      }
-
-      buffer.append("\"");
-    }
-    if (searchOperation.isSynchronizationOperation())
-    {
-      buffer.append(" type=synchronization");
-    }
-
+    appendSearchRequest(searchOperation, buffer);
     writer.writeRecord(buffer.toString());
   }
 
@@ -1258,6 +1174,10 @@
 
     final StringBuilder buffer = new StringBuilder(128);
     appendHeader(searchOperation, "SEARCH", CATEGORY_RESPONSE, buffer);
+    if (isCombinedMode)
+    {
+      appendSearchRequest(searchOperation, buffer);
+    }
     buffer.append(" result=");
     buffer.append(searchOperation.getResultCode().getIntValue());
 
@@ -1306,7 +1226,6 @@
   @Override
   public void logUnbind(final UnbindOperation unbindOperation)
   {
-    // FIXME: ensure that these are logged in combined mode.
     if (!isRequestLoggable(unbindOperation))
     {
       return;
@@ -1324,16 +1243,160 @@
 
 
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void close0()
+  {
+    writer.shutdown();
+    TimeThread.removeUserDefinedFormatter(timeStampFormat);
+    if (cfg != null)
+    {
+      cfg.removeFileBasedAccessChangeListener(this);
+    }
+  }
+
+
+
+  private void appendAbandonRequest(final AbandonOperation abandonOperation,
+      final StringBuilder buffer)
+  {
+    buffer.append(" idToAbandon=");
+    buffer.append(abandonOperation.getIDToAbandon());
+    appendRequestControls(abandonOperation, buffer);
+    if (abandonOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
+  private void appendAddRequest(final AddOperation addOperation,
+      final StringBuilder buffer)
+  {
+    buffer.append(" dn=\"");
+    buffer.append(addOperation.getRawEntryDN().toString());
+    buffer.append("\"");
+    appendRequestControls(addOperation, buffer);
+    if (addOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
+  private void appendBindRequest(final BindOperation bindOperation,
+      final StringBuilder buffer)
+  {
+    final String protocolVersion = bindOperation.getProtocolVersion();
+    if (protocolVersion != null)
+    {
+      buffer.append(" version=");
+      buffer.append(protocolVersion);
+    }
+
+    switch (bindOperation.getAuthenticationType())
+    {
+    case SIMPLE:
+      buffer.append(" type=SIMPLE");
+      break;
+    case SASL:
+      buffer.append(" type=SASL mechanism=");
+      buffer.append(bindOperation.getSASLMechanism());
+      break;
+    default:
+      buffer.append(" type=");
+      buffer.append(bindOperation.getAuthenticationType());
+      break;
+    }
+
+    buffer.append(" dn=\"");
+    buffer.append(bindOperation.getRawBindDN().toString());
+    buffer.append("\"");
+    appendRequestControls(bindOperation, buffer);
+    if (bindOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
+  private void appendCompareRequest(final CompareOperation compareOperation,
+      final StringBuilder buffer)
+  {
+    buffer.append(" dn=\"");
+    buffer.append(compareOperation.getRawEntryDN().toString());
+    buffer.append("\" attr=");
+    buffer.append(compareOperation.getAttributeType().getNameOrOID());
+    appendRequestControls(compareOperation, buffer);
+    if (compareOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
+  private void appendDeleteRequest(final DeleteOperation deleteOperation,
+      final StringBuilder buffer)
+  {
+    buffer.append(" dn=\"");
+    buffer.append(deleteOperation.getRawEntryDN().toString());
+    buffer.append("\"");
+    appendRequestControls(deleteOperation, buffer);
+    if (deleteOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
+  private void appendExtendedRequest(final ExtendedOperation extendedOperation,
+      final StringBuilder buffer)
+  {
+    final String oid = extendedOperation.getRequestOID();
+    final ExtendedOperationHandler<?> extOpHandler = DirectoryServer
+        .getExtendedOperationHandler(oid);
+    if (extOpHandler != null)
+    {
+      final String name = extOpHandler.getExtendedOperationName();
+      if (name != null)
+      {
+        buffer.append(" name=\"");
+        buffer.append(name);
+        buffer.append("\"");
+      }
+    }
+    buffer.append(" oid=\"");
+    buffer.append(oid);
+    buffer.append("\"");
+    appendRequestControls(extendedOperation, buffer);
+    if (extendedOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
   // Appends the common log header information to the provided buffer.
   private void appendHeader(final Operation operation, final String opType,
       final String category, final StringBuilder buffer)
   {
     buffer.append('[');
-    buffer.append(TimeThread.getLocalTime());
+    buffer.append(TimeThread.getUserDefinedTime(timeStampFormat));
     buffer.append("] ");
     buffer.append(opType);
-    buffer.append(' ');
-    buffer.append(category);
+    if (!isCombinedMode)
+    {
+      buffer.append(' ');
+      buffer.append(category);
+    }
     buffer.append(" conn=");
     buffer.append(operation.getConnectionID());
     buffer.append(" op=");
@@ -1344,10 +1407,131 @@
 
 
 
+  private void appendModifyDNRequest(final ModifyDNOperation modifyDNOperation,
+      final StringBuilder buffer)
+  {
+    buffer.append(" dn=\"");
+    buffer.append(modifyDNOperation.getRawEntryDN().toString());
+    buffer.append("\" newRDN=\"");
+    buffer.append(modifyDNOperation.getRawNewRDN().toString());
+    buffer.append("\" deleteOldRDN=");
+    buffer.append(modifyDNOperation.deleteOldRDN());
+
+    final ByteString newSuperior = modifyDNOperation.getRawNewSuperior();
+    if (newSuperior != null)
+    {
+      buffer.append(" newSuperior=\"");
+      buffer.append(newSuperior.toString());
+    }
+    appendRequestControls(modifyDNOperation, buffer);
+    if (modifyDNOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
+  private void appendModifyRequest(final ModifyOperation modifyOperation,
+      final StringBuilder buffer)
+  {
+    buffer.append(" dn=\"");
+    buffer.append(modifyOperation.getRawEntryDN().toString());
+    buffer.append("\"");
+    appendRequestControls(modifyOperation, buffer);
+    if (modifyOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
+  private void appendRequestControls(final Operation operation,
+      final StringBuilder buffer)
+  {
+    if (includeControlOIDs && !operation.getRequestControls().isEmpty())
+    {
+      buffer.append(" requestControls=");
+      boolean isFirst = true;
+      for (final Control control : operation.getRequestControls())
+      {
+        if (!isFirst)
+        {
+          buffer.append(",");
+        }
+        buffer.append(control.getOID());
+        isFirst = false;
+      }
+    }
+  }
+
+
+
+  private void appendResponseControls(final Operation operation,
+      final StringBuilder buffer)
+  {
+    if (includeControlOIDs && !operation.getResponseControls().isEmpty())
+    {
+      buffer.append(" responseControls=");
+      boolean isFirst = true;
+      for (final Control control : operation.getResponseControls())
+      {
+        if (!isFirst)
+        {
+          buffer.append(",");
+        }
+        buffer.append(control.getOID());
+        isFirst = false;
+      }
+    }
+  }
+
+
+
+  private void appendSearchRequest(final SearchOperation searchOperation,
+      final StringBuilder buffer)
+  {
+    buffer.append(" base=\"");
+    buffer.append(searchOperation.getRawBaseDN().toString());
+    buffer.append("\" scope=");
+    buffer.append(searchOperation.getScope());
+    buffer.append(" filter=\"");
+    searchOperation.getRawFilter().toString(buffer);
+
+    final LinkedHashSet<String> attrs = searchOperation.getAttributes();
+    if ((attrs == null) || attrs.isEmpty())
+    {
+      buffer.append("\" attrs=\"ALL\"");
+    }
+    else
+    {
+      buffer.append("\" attrs=\"");
+
+      final Iterator<String> iterator = attrs.iterator();
+      buffer.append(iterator.next());
+      while (iterator.hasNext())
+      {
+        buffer.append(",");
+        buffer.append(iterator.next());
+      }
+
+      buffer.append("\"");
+    }
+    appendRequestControls(searchOperation, buffer);
+    if (searchOperation.isSynchronizationOperation())
+    {
+      buffer.append(" type=synchronization");
+    }
+  }
+
+
+
   // Appends additional log items to the provided builder.
   private void logAdditionalLogItems(final Operation operation,
       final StringBuilder builder)
   {
+    appendResponseControls(operation, builder);
     for (final AdditionalLogItem item : operation.getAdditionalLogItems())
     {
       builder.append(' ');

--
Gitblit v1.10.0