/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2009 Sun Microsystems, Inc. * Portions Copyright 2011-2013 ForgeRock AS */ package org.opends.server.loggers; import static org.opends.messages.ConfigMessages.*; import static org.opends.server.util.StaticUtils.*; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; 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; import org.opends.server.config.ConfigException; import org.opends.server.core.AbandonOperation; import org.opends.server.core.AddOperation; import org.opends.server.core.BindOperation; import org.opends.server.core.CompareOperation; import org.opends.server.core.DeleteOperation; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ExtendedOperation; import org.opends.server.core.ModifyDNOperation; import org.opends.server.core.ModifyOperation; import org.opends.server.core.SearchOperation; import org.opends.server.core.UnbindOperation; import org.opends.server.types.AdditionalLogItem; import org.opends.server.types.AuthenticationInfo; import org.opends.server.types.ByteString; import org.opends.server.types.ConfigChangeResult; import org.opends.server.types.Control; import org.opends.server.types.DN; import org.opends.server.types.DirectoryException; import org.opends.server.types.DisconnectReason; import org.opends.server.types.FilePermission; import org.opends.server.types.InitializationException; import org.opends.server.types.Operation; import org.opends.server.types.ResultCode; import org.opends.server.util.TimeThread; /** * This class provides the implementation of the access logger used by the * directory server. */ public final class TextAccessLogPublisher extends AbstractTextAccessLogPublisher<FileBasedAccessLogPublisherCfg> implements ConfigurationChangeListener<FileBasedAccessLogPublisherCfg> { /** * The category to use when logging responses. */ private static final String CATEGORY_RESPONSE = "RES"; /** * The category to use when logging requests. */ private static final String CATEGORY_REQUEST = "REQ"; /** * Returns an instance of the text access log publisher that will print all * messages to the provided writer. This is used to print the messages to the * console when the server starts up. * * @param writer * The text writer where the message will be written to. * @param suppressInternal * Indicates whether to suppress internal operations. * @return The instance of the text error log publisher that will print all * messages to standard out. */ public static TextAccessLogPublisher getStartupTextAccessPublisher( final TextWriter writer, final boolean suppressInternal) { final TextAccessLogPublisher startupPublisher = new TextAccessLogPublisher(); startupPublisher.writer = writer; startupPublisher.buildFilters(suppressInternal); return startupPublisher; } 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"; /** * {@inheritDoc} */ @Override public ConfigChangeResult applyConfigurationChange( final FileBasedAccessLogPublisherCfg config) { // Default result code. ResultCode resultCode = ResultCode.SUCCESS; boolean adminActionRequired = false; final ArrayList<Message> messages = new ArrayList<Message>(); final File logFile = getFileForPath(config.getLogFile()); final FileNamingPolicy fnPolicy = new TimeStampNaming(logFile); try { final FilePermission perm = FilePermission.decodeUNIXMode(config .getLogFilePermissions()); final boolean writerAutoFlush = config.isAutoFlush() && !config.isAsynchronous(); TextWriter currentWriter; // Determine the writer we are using. If we were writing // asynchronously, we need to modify the underlying writer. if (writer instanceof AsynchronousTextWriter) { currentWriter = ((AsynchronousTextWriter) writer).getWrappedWriter(); } else if (writer instanceof ParallelTextWriter) { currentWriter = ((ParallelTextWriter) writer).getWrappedWriter(); } else { currentWriter = writer; } if (currentWriter instanceof MultifileTextWriter) { final MultifileTextWriter mfWriter = (MultifileTextWriter) currentWriter; mfWriter.setNamingPolicy(fnPolicy); mfWriter.setFilePermissions(perm); mfWriter.setAppend(config.isAppend()); mfWriter.setAutoFlush(writerAutoFlush); mfWriter.setBufferSize((int) config.getBufferSize()); mfWriter.setInterval(config.getTimeInterval()); mfWriter.removeAllRetentionPolicies(); mfWriter.removeAllRotationPolicies(); for (final DN dn : config.getRotationPolicyDNs()) { mfWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn)); } for (final DN dn : config.getRetentionPolicyDNs()) { mfWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn)); } if (writer instanceof AsynchronousTextWriter && !config.isAsynchronous()) { // The asynchronous setting is being turned off. final AsynchronousTextWriter asyncWriter = ((AsynchronousTextWriter) writer); writer = mfWriter; asyncWriter.shutdown(false); } if (writer instanceof ParallelTextWriter && !config.isAsynchronous()) { // The asynchronous setting is being turned off. final ParallelTextWriter asyncWriter = ((ParallelTextWriter) writer); writer = mfWriter; asyncWriter.shutdown(false); } if (!(writer instanceof AsynchronousTextWriter) && config.isAsynchronous()) { // The asynchronous setting is being turned on. writer = new AsynchronousTextWriter( "Asyncronous Text Writer for " + config.dn().toNormalizedString(), config.getQueueSize(), config.isAutoFlush(), mfWriter); } if (!(writer instanceof ParallelTextWriter) && config.isAsynchronous()) { // The asynchronous setting is being turned on. writer = new ParallelTextWriter( "Parallel Text Writer for " + config.dn().toNormalizedString(), config.isAutoFlush(), mfWriter); } if ((cfg.isAsynchronous() && config.isAsynchronous()) && (cfg.getQueueSize() != config.getQueueSize())) { 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) { final Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get( config.dn().toString(), stackTraceToSingleLineString(e)); resultCode = DirectoryServer.getServerErrorResultCode(); messages.add(message); } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * {@inheritDoc} */ @Override public void initializeLogPublisher(final FileBasedAccessLogPublisherCfg cfg) throws ConfigException, InitializationException { final File logFile = getFileForPath(cfg.getLogFile()); final FileNamingPolicy fnPolicy = new TimeStampNaming(logFile); try { final FilePermission perm = FilePermission.decodeUNIXMode(cfg .getLogFilePermissions()); final LogPublisherErrorHandler errorHandler = new LogPublisherErrorHandler(cfg.dn()); final boolean writerAutoFlush = cfg.isAutoFlush() && !cfg.isAsynchronous(); final MultifileTextWriter theWriter = new MultifileTextWriter( "Multifile Text Writer for " + cfg.dn().toNormalizedString(), cfg.getTimeInterval(), fnPolicy, perm, errorHandler, "UTF-8", writerAutoFlush, cfg.isAppend(), (int) cfg.getBufferSize()); // Validate retention and rotation policies. for (final DN dn : cfg.getRotationPolicyDNs()) { theWriter.addRotationPolicy(DirectoryServer.getRotationPolicy(dn)); } for (final DN dn : cfg.getRetentionPolicyDNs()) { theWriter.addRetentionPolicy(DirectoryServer.getRetentionPolicy(dn)); } if (cfg.isAsynchronous()) { if (cfg.getQueueSize() > 0) { this.writer = new AsynchronousTextWriter( "Asyncronous Text Writer for " + cfg.dn().toNormalizedString(), cfg.getQueueSize(), cfg.isAutoFlush(), theWriter); } else { this.writer = new ParallelTextWriter("Parallel Text Writer for " + cfg.dn().toNormalizedString(), cfg.isAutoFlush(), theWriter); } } else { this.writer = theWriter; } } catch (final DirectoryException e) { final Message message = ERR_CONFIG_LOGGING_CANNOT_CREATE_WRITER.get(cfg .dn().toString(), String.valueOf(e)); throw new InitializationException(message, e); } catch (final IOException e) { final Message message = ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE.get( logFile.toString(), cfg.dn().toString(), String.valueOf(e)); throw new InitializationException(message, e); } initializeFilters(cfg); this.cfg = cfg; isCombinedMode = cfg.getLogFormat() == LogFormat.COMBINED; includeControlOIDs = cfg.isLogControlOids(); timeStampFormat = cfg.getLogRecordTimeFormat(); cfg.addFileBasedAccessChangeListener(this); } /** * {@inheritDoc} */ @Override public boolean isConfigurationAcceptable( final FileBasedAccessLogPublisherCfg configuration, final List<Message> unacceptableReasons) { return isFilterConfigurationAcceptable(configuration, unacceptableReasons) && isConfigurationChangeAcceptable(configuration, unacceptableReasons); } /** * {@inheritDoc} */ @Override public boolean isConfigurationChangeAcceptable( 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 { final FilePermission filePerm = FilePermission.decodeUNIXMode(config .getLogFilePermissions()); if (!filePerm.isOwnerWritable()) { final Message message = ERR_CONFIG_LOGGING_INSANE_MODE.get(config .getLogFilePermissions()); unacceptableReasons.add(message); return false; } } catch (final DirectoryException e) { final Message message = ERR_CONFIG_LOGGING_MODE_INVALID.get( config.getLogFilePermissions(), String.valueOf(e)); unacceptableReasons.add(message); return false; } return true; } /** * Writes a message to the access logger with information about the abandon * request associated with the provided abandon operation. * * @param abandonOperation * The abandon operation containing the information to use to log the * abandon request. */ @Override public void logAbandonRequest(final AbandonOperation abandonOperation) { if (isCombinedMode || !isRequestLoggable(abandonOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(abandonOperation, "ABANDON", CATEGORY_REQUEST, buffer); appendAbandonRequest(abandonOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the result of * the provided abandon operation. * * @param abandonOperation * The abandon operation containing the information to use to log the * abandon request. */ @Override public void logAbandonResult(final AbandonOperation abandonOperation) { if (!isResponseLoggable(abandonOperation)) { return; } 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()); appendMessage(buffer, abandonOperation); logAdditionalLogItems(abandonOperation, buffer); appendEtime(buffer, abandonOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the add * request associated with the provided add operation. * * @param addOperation * The add operation containing the information to use to log the add * request. */ @Override public void logAddRequest(final AddOperation addOperation) { if (isCombinedMode || !isRequestLoggable(addOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(addOperation, "ADD", CATEGORY_REQUEST, buffer); appendAddRequest(addOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the add * response associated with the provided add operation. * * @param addOperation * The add operation containing the information to use to log the add * response. */ @Override public void logAddResponse(final AddOperation addOperation) { if (!isResponseLoggable(addOperation)) { return; } 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()); appendMessage(buffer, addOperation); logAdditionalLogItems(addOperation, buffer); appendLabelIfNotNull(buffer, "authzDN", addOperation .getProxiedAuthorizationDN()); appendEtime(buffer, addOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the bind * request associated with the provided bind operation. * * @param bindOperation * The bind operation with the information to use to log the bind * request. */ @Override public void logBindRequest(final BindOperation bindOperation) { if (isCombinedMode || !isRequestLoggable(bindOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(bindOperation, "BIND", CATEGORY_REQUEST, buffer); appendBindRequest(bindOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the bind * response associated with the provided bind operation. * * @param bindOperation * The bind operation containing the information to use to log the * bind response. */ @Override public void logBindResponse(final BindOperation bindOperation) { if (!isResponseLoggable(bindOperation)) { return; } 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()); appendMessage(buffer, bindOperation); final Message failureMessage = bindOperation.getAuthFailureReason(); if (failureMessage != null) { // this code path is mutually exclusive with the if result code is success // down below buffer.append(" authFailureID="); buffer.append(failureMessage.getDescriptor().getId()); appendLabel(buffer, "authFailureReason", failureMessage); if (bindOperation.getSASLMechanism() != null && bindOperation.getSASLAuthUserEntry() != null) { // SASL bind and we have successfully found a user entry for auth appendLabel(buffer, "authDN", bindOperation.getSASLAuthUserEntry() .getDN()); } else { // SASL bind failed to find user entry for auth or simple bind appendLabel(buffer, "authDN", bindOperation.getRawBindDN()); } } logAdditionalLogItems(bindOperation, buffer); if (bindOperation.getResultCode() == ResultCode.SUCCESS) { // this code path is mutually exclusive with the if failure message exist // just above final AuthenticationInfo authInfo = bindOperation.getAuthenticationInfo(); if (authInfo != null) { final DN authDN = authInfo.getAuthenticationDN(); if (authDN != null) { appendLabel(buffer, "authDN", authDN); final DN authzDN = authInfo.getAuthorizationDN(); if (!authDN.equals(authzDN)) { appendLabel(buffer, "authzDN", authzDN); } } else { buffer.append(" authDN=\"\""); } } } appendEtime(buffer, bindOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the compare * request associated with the provided compare operation. * * @param compareOperation * The compare operation containing the information to use to log the * compare request. */ @Override public void logCompareRequest(final CompareOperation compareOperation) { if (isCombinedMode || !isRequestLoggable(compareOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(compareOperation, "COMPARE", CATEGORY_REQUEST, buffer); appendCompareRequest(compareOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the compare * response associated with the provided compare operation. * * @param compareOperation * The compare operation containing the information to use to log the * compare response. */ @Override public void logCompareResponse(final CompareOperation compareOperation) { if (!isResponseLoggable(compareOperation)) { return; } 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()); appendMessage(buffer, compareOperation); logAdditionalLogItems(compareOperation, buffer); appendLabelIfNotNull(buffer, "authzDN", compareOperation .getProxiedAuthorizationDN()); appendEtime(buffer, compareOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about a new client * connection that has been established, regardless of whether it will be * immediately terminated. * * @param clientConnection * The client connection that has been established. */ @Override public void logConnect(final ClientConnection clientConnection) { if (!isConnectLoggable(clientConnection)) { return; } final long connectionID = clientConnection.getConnectionID(); final StringBuilder buffer = new StringBuilder(100); buffer.append("["); buffer.append(TimeThread.getLocalTime()); buffer.append("]"); buffer.append(" CONNECT conn="); buffer.append(connectionID); buffer.append(" from="); buffer.append(clientConnection.getClientHostPort()); buffer.append(" to="); buffer.append(clientConnection.getServerHostPort()); buffer.append(" protocol="); buffer.append(clientConnection.getProtocol()); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the delete * request associated with the provided delete operation. * * @param deleteOperation * The delete operation with the information to use to log the delete * request. */ @Override public void logDeleteRequest(final DeleteOperation deleteOperation) { if (isCombinedMode || !isRequestLoggable(deleteOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(deleteOperation, "DELETE", CATEGORY_REQUEST, buffer); appendDeleteRequest(deleteOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the delete * response associated with the provided delete operation. * * @param deleteOperation * The delete operation containing the information to use to log the * delete response. */ @Override public void logDeleteResponse(final DeleteOperation deleteOperation) { if (!isResponseLoggable(deleteOperation)) { return; } 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()); appendMessage(buffer, deleteOperation); logAdditionalLogItems(deleteOperation, buffer); appendLabelIfNotNull(buffer, "authzDN", deleteOperation .getProxiedAuthorizationDN()); appendEtime(buffer, deleteOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the * termination of an existing client connection. * * @param clientConnection * The client connection that has been terminated. * @param disconnectReason * A generic disconnect reason for the connection termination. * @param message * A human-readable message that can provide additional information * about the disconnect. */ @Override public void logDisconnect(final ClientConnection clientConnection, final DisconnectReason disconnectReason, final Message message) { if (!isDisconnectLoggable(clientConnection)) { return; } final long connectionID = clientConnection.getConnectionID(); final StringBuilder buffer = new StringBuilder(100); buffer.append("["); buffer.append(TimeThread.getLocalTime()); buffer.append("]"); buffer.append(" DISCONNECT conn="); buffer.append(connectionID); appendLabel(buffer, "reason", disconnectReason); appendLabelIfNotNull(buffer, "msg", message); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the extended * request associated with the provided extended operation. * * @param extendedOperation * The extended operation containing the information to use to log * the extended request. */ @Override public void logExtendedRequest(final ExtendedOperation extendedOperation) { if (isCombinedMode || !isRequestLoggable(extendedOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(extendedOperation, "EXTENDED", CATEGORY_REQUEST, buffer); appendExtendedRequest(extendedOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the extended * response associated with the provided extended operation. * * @param extendedOperation * The extended operation containing the info to use to log the * extended response. */ @Override public void logExtendedResponse(final ExtendedOperation extendedOperation) { if (!isResponseLoggable(extendedOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(extendedOperation, "EXTENDED", CATEGORY_RESPONSE, buffer); if (isCombinedMode) { appendExtendedRequest(extendedOperation, buffer); } final String oid = extendedOperation.getResponseOID(); if (oid != null) { final ExtendedOperationHandler<?> extOpHandler = DirectoryServer .getExtendedOperationHandler(oid); if (extOpHandler != null) { String name = extOpHandler.getExtendedOperationName(); appendLabelIfNotNull(buffer, "name", name); } appendLabel(buffer, "oid", oid); } buffer.append(" result="); buffer.append(extendedOperation.getResultCode().getIntValue()); appendMessage(buffer, extendedOperation); logAdditionalLogItems(extendedOperation, buffer); appendEtime(buffer, extendedOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the modify DN * request associated with the provided modify DN operation. * * @param modifyDNOperation * The modify DN operation containing the info to use to log the * modify DN request. */ @Override public void logModifyDNRequest(final ModifyDNOperation modifyDNOperation) { if (isCombinedMode || !isRequestLoggable(modifyDNOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(modifyDNOperation, "MODIFYDN", CATEGORY_REQUEST, buffer); appendModifyDNRequest(modifyDNOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the modify DN * response associated with the provided modify DN operation. * * @param modifyDNOperation * The modify DN operation containing the information to use to log * the modify DN response. */ @Override public void logModifyDNResponse(final ModifyDNOperation modifyDNOperation) { if (!isResponseLoggable(modifyDNOperation)) { return; } 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()); appendMessage(buffer, modifyDNOperation); logAdditionalLogItems(modifyDNOperation, buffer); appendLabelIfNotNull(buffer, "authzDN", modifyDNOperation .getProxiedAuthorizationDN()); appendEtime(buffer, modifyDNOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the modify * request associated with the provided modify operation. * * @param modifyOperation * The modify operation containing the information to use to log the * modify request. */ @Override public void logModifyRequest(final ModifyOperation modifyOperation) { if (isCombinedMode || !isRequestLoggable(modifyOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(modifyOperation, "MODIFY", CATEGORY_REQUEST, buffer); appendModifyRequest(modifyOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the modify * response associated with the provided modify operation. * * @param modifyOperation * The modify operation containing the information to use to log the * modify response. */ @Override public void logModifyResponse(final ModifyOperation modifyOperation) { if (!isResponseLoggable(modifyOperation)) { return; } 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()); appendMessage(buffer, modifyOperation); logAdditionalLogItems(modifyOperation, buffer); appendLabelIfNotNull(buffer, "authzDN", modifyOperation .getProxiedAuthorizationDN()); appendEtime(buffer, modifyOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the search * request associated with the provided search operation. * * @param searchOperation * The search operation containing the info to use to log the search * request. */ @Override public void logSearchRequest(final SearchOperation searchOperation) { if (isCombinedMode || !isRequestLoggable(searchOperation)) { return; } final StringBuilder buffer = new StringBuilder(192); appendHeader(searchOperation, "SEARCH", CATEGORY_REQUEST, buffer); appendSearchRequest(searchOperation, buffer); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the completion * of the provided search operation. * * @param searchOperation * The search operation containing the information to use to log the * search result done message. */ @Override public void logSearchResultDone(final SearchOperation searchOperation) { if (!isResponseLoggable(searchOperation)) { return; } 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()); appendMessage(buffer, searchOperation); buffer.append(" nentries="); buffer.append(searchOperation.getEntriesSent()); logAdditionalLogItems(searchOperation, buffer); appendLabelIfNotNull(buffer, "authzDN", searchOperation .getProxiedAuthorizationDN()); appendEtime(buffer, searchOperation); writer.writeRecord(buffer.toString()); } /** * Writes a message to the access logger with information about the unbind * request associated with the provided unbind operation. * * @param unbindOperation * The unbind operation containing the info to use to log the unbind * request. */ @Override public void logUnbind(final UnbindOperation unbindOperation) { if (!isRequestLoggable(unbindOperation)) { return; } final StringBuilder buffer = new StringBuilder(100); appendHeader(unbindOperation, "UNBIND", CATEGORY_REQUEST, buffer); if (unbindOperation.isSynchronizationOperation()) { buffer.append(" type=synchronization"); } writer.writeRecord(buffer.toString()); } /** * {@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) { appendLabel(buffer, "dn", addOperation.getRawEntryDN()); 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; } appendLabel(buffer, "dn", bindOperation.getRawBindDN()); appendRequestControls(bindOperation, buffer); if (bindOperation.isSynchronizationOperation()) { buffer.append(" type=synchronization"); } } private void appendCompareRequest(final CompareOperation compareOperation, final StringBuilder buffer) { appendLabel(buffer, "dn", compareOperation.getRawEntryDN()); 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) { appendLabel(buffer, "dn", deleteOperation.getRawEntryDN()); 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(); appendLabelIfNotNull(buffer, "name", name); } appendLabel(buffer, "oid", oid); appendRequestControls(extendedOperation, buffer); if (extendedOperation.isSynchronizationOperation()) { buffer.append(" type=synchronization"); } } private void appendLabel(final StringBuilder buffer, final String label, final Object obj) { buffer.append(' ').append(label).append("=\""); if (obj != null) { buffer.append(obj); } buffer.append('\"'); } private void appendLabelIfNotNull(final StringBuilder buffer, final String label, final Object obj) { if (obj != null) { appendLabel(buffer, label, obj); } } private void appendMessage(final StringBuilder buffer, final Operation operation) { final MessageBuilder msg = operation.getErrorMessage(); if ((msg != null) && (msg.length() > 0)) { appendLabel(buffer, "message", msg); } } private void appendEtime(final StringBuilder buffer, final Operation operation) { buffer.append(" etime="); // the server can be configured to log processing time as nanos xor millis long etime = operation.getProcessingNanoTime(); if (etime <= -1) { // if it is not configured for nanos, then use millis. etime = operation.getProcessingTime(); } buffer.append(etime); } /** * 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.getUserDefinedTime(timeStampFormat)); buffer.append("] "); buffer.append(opType); if (!isCombinedMode) { buffer.append(' '); buffer.append(category); } buffer.append(" conn="); buffer.append(operation.getConnectionID()); buffer.append(" op="); buffer.append(operation.getOperationID()); buffer.append(" msgID="); buffer.append(operation.getMessageID()); } private void appendModifyDNRequest(final ModifyDNOperation modifyDNOperation, final StringBuilder buffer) { appendLabel(buffer, "dn", modifyDNOperation.getRawEntryDN()); appendLabel(buffer, "newRDN", modifyDNOperation.getRawNewRDN()); appendLabel(buffer, "deleteOldRDN", modifyDNOperation.deleteOldRDN()); final ByteString newSuperior = modifyDNOperation.getRawNewSuperior(); appendLabelIfNotNull(buffer, "newSuperior", newSuperior); appendRequestControls(modifyDNOperation, buffer); if (modifyDNOperation.isSynchronizationOperation()) { buffer.append(" type=synchronization"); } } private void appendModifyRequest(final ModifyOperation modifyOperation, final StringBuilder buffer) { appendLabel(buffer, "dn", modifyOperation.getRawEntryDN()); 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) { appendLabel(buffer, "base", searchOperation.getRawBaseDN()); buffer.append(" scope="); buffer.append(searchOperation.getScope()); buffer.append(" filter=\""); searchOperation.getRawFilter().toString(buffer); final Set<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(' '); item.toString(builder); } } }