Rolled back patches for https://issues.apache.org/activemq/browse/AMQ-1361 - as no licence headers where provided - we can't accept

git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@580273 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Davies 2007-09-28 09:46:50 +00:00
parent e21587fb9e
commit 19a83ae3cb
44 changed files with 32 additions and 3033 deletions

View File

@ -1,66 +0,0 @@
package org.apache.activemq.transport;
import java.io.IOException;
import org.apache.commons.logging.Log;
/**
* Interface for classes that will be called by the TransportLogger
* class to actually write to a log file.
* Every class that implements this interface has do be declared in
* the resources/META-INF/services/org/apache/activemq/transport/logwriters
* directory, by creating a file with the name of the writer (for example
* "default") and including the line
* class=org.apache.activemq.transport.logwriters.(Name of the LogWriter class)
*/
public interface LogWriter {
/**
* Writes a header message to the log.
* @param log The log to be written to.
*/
public void initialMessage(Log log);
/**
* Writes a message to a log when a request command is sent.
* @param log The log to be written to.
* @param command The command to be logged.
*/
public void logRequest (Log log, Object command);
/**
* Writes a message to a log when a response command is received.
* @param log The log to be written to.
* @param command The command to be logged.
*/
public void logResponse (Log log, Object response);
/**
* Writes a message to a log when an asynchronous equest command is sent.
* @param log The log to be written to.
* @param command The command to be logged.
*/
public void logAsyncRequest (Log log, Object command);
/**
* Writes a message to a log when message is sent.
* @param log The log to be written to.
* @param command The command to be logged.
*/
public void logOneWay (Log log, Object command);
/**
* Writes a message to a log when message is received.
* @param log The log to be written to.
* @param command The command to be logged.
*/
public void logReceivedCommand (Log log, Object command);
/**
* Writes a message to a log when an exception is received.
* @param log The log to be written to.
* @param command The command to be logged.
*/
public void logReceivedException (Log log, IOException error);
}

View File

@ -19,136 +19,71 @@ package org.apache.activemq.transport;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @version $Revision$
*/
public class TransportLogger extends TransportFilter {
private static int lastId;
private final Log log;
private boolean logging;
private final LogWriter logWriter;
private TransportLoggerView view;
public TransportLogger(Transport next, Log log, boolean startLogging, LogWriter logWriter) {
// Changed constructor to pass the implementation of the LogWriter interface
// that will be used to write the messages.
public TransportLogger(Transport next) {
this(next, LogFactory.getLog(TransportLogger.class.getName() + ".Connection:" + getNextId()));
}
public TransportLogger(Transport next, Log log) {
super(next);
this.log = log;
this.logging = startLogging;
this.logWriter = logWriter;
}
/**
* Returns true if logging is activated for this TransportLogger, false otherwise.
* @return true if logging is activated for this TransportLogger, false otherwise.
*/
public boolean isLogging() {
return logging;
private static synchronized int getNextId() {
return ++lastId;
}
/**
* Sets if logging should be activated for this TransportLogger.
* @param logging true to activate logging, false to deactivate.
*/
public void setLogging(boolean logging) {
this.logging = logging;
}
public Object request(Object command) throws IOException {
// Changed this method to use a LogWriter object to actually
// print the messages to the log, and only in case of logging
// being active, instead of logging the message directly.
if (logging)
logWriter.logRequest(log, command);
Object rc = super.request(command);
if (logging)
logWriter.logResponse(log, command);
return rc;
log.debug("SENDING REQUEST: " + command);
Object rc = super.request(command);
log.debug("GOT RESPONSE: " + rc);
return rc;
}
public Object request(Object command, int timeout) throws IOException {
// Changed this method to use a LogWriter object to actually
// print the messages to the log, and only in case of logging
// being active, instead of logging the message directly.
if (logging)
logWriter.logRequest(log, command);
log.debug("SENDING REQUEST: " + command);
Object rc = super.request(command, timeout);
if (logging)
logWriter.logResponse(log, command);
return rc;
log.debug("GOT RESPONSE: " + rc);
return rc;
}
public FutureResponse asyncRequest(Object command, ResponseCallback responseCallback) throws IOException {
// Changed this method to use a LogWriter object to actually
// print the messages to the log, and only in case of logging
// being active, instead of logging the message directly.
if (logging)
logWriter.logAsyncRequest(log, command);
FutureResponse rc = next.asyncRequest(command, responseCallback);
return rc;
log.debug("SENDING ASNYC REQUEST: " + command);
FutureResponse rc = next.asyncRequest(command, responseCallback);
return rc;
}
public void oneway(Object command) throws IOException {
// Changed this method to use a LogWriter object to actually
// print the messages to the log, and only in case of logging
// being active, instead of logging the message directly.
if( logging && log.isDebugEnabled() ) {
logWriter.logOneWay(log, command);
if (log.isDebugEnabled()) {
log.debug("SENDING: " + command);
}
next.oneway(command);
}
public void onCommand(Object command) {
// Changed this method to use a LogWriter object to actually
// print the messages to the log, and only in case of logging
// being active, instead of logging the message directly.
if( logging && log.isDebugEnabled() ) {
logWriter.logReceivedCommand(log, command);
if (log.isDebugEnabled()) {
log.debug("RECEIVED: " + command);
}
getTransportListener().onCommand(command);
}
public void onException(IOException error) {
// Changed this method to use a LogWriter object to actually
// print the messages to the log, and only in case of logging
// being active, instead of logging the message directly.
if( logging && log.isDebugEnabled() ) {
logWriter.logReceivedException(log, error);
if (log.isDebugEnabled()) {
log.debug("RECEIVED Exception: " + error, error);
}
getTransportListener().onException(error);
}
/**
* Gets the associated MBean for this TransportLogger.
* @return the associated MBean for this TransportLogger.
*/
public TransportLoggerView getView() {
return view;
}
/**
* Sets the associated MBean for this TransportLogger.
* @param view the associated MBean for this TransportLogger.
*/
public void setView(TransportLoggerView view) {
this.view = view;
}
public String toString() {
return next.toString();
}
/**
* We need to override this method
* so that we can unregister the associated
* MBean to avoid a memory leak.
*/
public void finalize() throws Throwable {
if (view != null) {
view.unregister();
}
}
}

View File

@ -1,33 +0,0 @@
package org.apache.activemq.transport;
import org.apache.activemq.broker.jmx.BrokerView;
import org.apache.activemq.broker.jmx.ManagementContext;
/**
* Implementation of the TransportLoggerControlMBean interface,
* which is an MBean used to control all TransportLoggers at once.
*/
public class TransportLoggerControl implements TransportLoggerControlMBean {
/**
* Constructor
*/
public TransportLoggerControl(ManagementContext managementContext) {
}
// doc comment inherited from TransportLoggerControlMBean
public void disableAllTransportLoggers() {
TransportLoggerView.disableAllTransportLoggers();
}
// doc comment inherited from TransportLoggerControlMBean
public void enableAllTransportLoggers() {
TransportLoggerView.enableAllTransportLoggers();
}
// doc comment inherited from TransportLoggerControlMBean
public void reloadLog4jProperties() throws Exception {
new BrokerView(null, null).reloadLog4jProperties();
}
}

View File

@ -1,27 +0,0 @@
package org.apache.activemq.transport;
/**
* MBean used to manage all of the TransportLoggers at once.
* Avalaible operations:
* -Enable logging for all TransportLoggers at once.
* -Disable logging for all TransportLoggers at once.
*/
public interface TransportLoggerControlMBean {
/**
* Enable logging for all Transport Loggers at once.
*/
public void enableAllTransportLoggers();
/**
* Disable logging for all Transport Loggers at once.
*/
public void disableAllTransportLoggers();
/**
* Reloads log4j.properties from the classpath
* @throws Exception
*/
public void reloadLog4jProperties() throws Exception;
}

View File

@ -1,194 +0,0 @@
package org.apache.activemq.transport;
import java.io.IOException;
import javax.management.ObjectName;
import org.apache.activemq.broker.jmx.ManagementContext;
import org.apache.activemq.util.IOExceptionSupport;
import org.apache.activemq.util.LogWriterFinder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Singleton class to create TransportLogger objects.
* When the method getInstance() is called for the first time,
* a TransportLoggerControlMBean is created and registered.
* This MBean permits enabling and disabling the logging for
* all TransportLogger objects at once.
* @see TransportLoggerControlMBean
*/
public class TransportLoggerFactory {
private static final Log log = LogFactory.getLog(TransportLoggerFactory.class);
private static TransportLoggerFactory instance;
private static int lastId=0;
private static final LogWriterFinder logWriterFinder = new LogWriterFinder("META-INF/services/org/apache/activemq/transport/logwriters/");
/**
* LogWriter that will be used if none is specified.
*/
public static String defaultLogWriterName = "default";
/**
* If transport logging is enabled, it will be possible to control
* the transport loggers or not based on this value
*/
private static boolean defaultDynamicManagement = false;
/**
* If transport logging is enabled, the transport loggers will initially
* output or not depending on this value.
* This setting only has a meaning if
*/
private static boolean defaultInitialBehavior = true;
/**
*
*/
private static int defaultJmxPort = 1099;
private boolean transportLoggerControlCreated = false;
private ManagementContext managementContext;
private ObjectName objectName;
/**
* Private constructor.
*/
private TransportLoggerFactory() {
}
/**
* Returns a TransportLoggerFactory object which can be used to create TransportLogger objects.
* @return a TransportLoggerFactory object
*/
public static synchronized TransportLoggerFactory getInstance() {
if (instance == null) {
instance = new TransportLoggerFactory();
}
return instance;
}
public void stop() {
try {
if (this.transportLoggerControlCreated) {
this.managementContext.unregisterMBean(this.objectName);
this.managementContext.stop();
this.managementContext = null;
}
} catch (Exception e) {
log.error("TransportLoggerFactory could not be stopped, reason: " + e, e);
}
}
/**
* Creates a TransportLogger object, that will be inserted in the Transport Stack.
* Uses the default initial behavior, the default log writer, and creates a new
* log4j object to be used by the TransportLogger.
* @param next The next Transport layer in the Transport stack.
* @return A TransportLogger object.
* @throws IOException
*/
public TransportLogger createTransportLogger(Transport next) throws IOException {
int id = getNextId();
return createTransportLogger(next, id, createLog(id), defaultLogWriterName, defaultDynamicManagement, defaultInitialBehavior, defaultJmxPort);
}
/**
* Creates a TransportLogger object, that will be inserted in the Transport Stack.
* Uses the default initial behavior and the default log writer.
* @param next The next Transport layer in the Transport stack.
* @param log The log4j log that will be used by the TransportLogger.
* @return A TransportLogger object.
* @throws IOException
*/
public TransportLogger createTransportLogger(Transport next, Log log) throws IOException {
return createTransportLogger(next, getNextId(), log, defaultLogWriterName, defaultDynamicManagement, defaultInitialBehavior,defaultJmxPort);
}
/**
* Creates a TransportLogger object, that will be inserted in the Transport Stack.
* Creates a new log4j object to be used by the TransportLogger.
* @param next The next Transport layer in the Transport stack.
* @param startLogging Specifies if this TransportLogger should be initially active or not.
* @param logWriterName The name or the LogWriter to be used. Different log writers can output
* logs with a different format.
* @return A TransportLogger object.
* @throws IOException
*/
public TransportLogger createTransportLogger(Transport next, String logWriterName,
boolean useJmx, boolean startLogging, int jmxport) throws IOException {
int id = getNextId();
return createTransportLogger(next, id, createLog(id), logWriterName, useJmx, startLogging, jmxport);
}
/**
* Creates a TransportLogger object, that will be inserted in the Transport Stack.
* @param next The next Transport layer in the Transport stack.
* @param id The id of the transport logger.
* @param log The log4j log that will be used by the TransportLogger.
* @param logWriterName The name or the LogWriter to be used. Different log writers can output
* @param dynamicManagement Specifies if JMX will be used to switch on/off the TransportLogger to be created.
* @param startLogging Specifies if this TransportLogger should be initially active or not. Only has a meaning if
* dynamicManagement = true.
* @param jmxPort the port to be used by the JMX server. It should only be different from 1099 (broker's default JMX port)
* when it's a client that is using Transport Logging. In a broker, if the port is different from 1099, 2 JMX servers will
* be created, both identical, with all the MBeans.
* @return A TransportLogger object.
* @throws IOException
*/
public TransportLogger createTransportLogger(Transport next, int id, Log log,
String logWriterName, boolean dynamicManagement, boolean startLogging, int jmxport) throws IOException {
try {
LogWriter logWriter = logWriterFinder.newInstance(logWriterName);
TransportLogger tl = new TransportLogger (next, log, startLogging, logWriter);
if (dynamicManagement) {
synchronized (this) {
if (!this.transportLoggerControlCreated) {
this.createTransportLoggerControl(jmxport);
}
}
TransportLoggerView tlv = new TransportLoggerView(tl, next.toString(), id, this.managementContext);
tl.setView(tlv);
}
return tl;
} catch (Throwable e) {
throw IOExceptionSupport.create("Could not create log writer object for: " + logWriterName + ", reason: " + e, e);
}
}
synchronized private static int getNextId() {
return ++lastId;
}
private static Log createLog(int id) {
return LogFactory.getLog(TransportLogger.class.getName()+".Connection:" + id);
}
/**
* Starts the management context.
* Creates and registers a TransportLoggerControl MBean which enables the user
* to enable/disable logging for all transport loggers at once.
*/
private void createTransportLoggerControl(int port) {
try {
this.managementContext = new ManagementContext();
this.managementContext.setConnectorPort(port);
this.managementContext.start();
} catch (Exception e) {
log.error("Management context could not be started, reason: " + e, e);
}
try {
this.objectName = new ObjectName(this.managementContext.getJmxDomainName()+":"+ "Type=TransportLoggerControl");
this.managementContext.getMBeanServer().registerMBean(new TransportLoggerControl(this.managementContext),this.objectName);
this.transportLoggerControlCreated = true;
} catch (Exception e) {
log.error("TransportLoggerControlMBean could not be registered, reason: " + e, e);
}
}
}

View File

@ -1,154 +0,0 @@
package org.apache.activemq.transport;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.management.ObjectName;
import org.apache.activemq.broker.jmx.ManagementContext;
import org.apache.activemq.util.JMXSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class implementing the TransportLoggerViewMBean interface.
* When an object of this class is created, it registers itself in
* the MBeanServer of the management context provided.
* When a TransportLogger object is finalized because the Transport Stack
* where it resides is no longer in use, the method unregister() will be called.
* @see TransportLoggerViewMBean.
*/
public class TransportLoggerView implements TransportLoggerViewMBean {
private static final Log log = LogFactory.getLog(TransportLoggerView.class);
/**
* Set with the TransportLoggerViews objects created.
* Used by the methods enableAllTransportLoggers and diablellTransportLoggers.
* The method unregister() removes objects from this set.
*/
private static Set<TransportLoggerView> transportLoggerViews = Collections.synchronizedSet(new HashSet<TransportLoggerView>());
private final WeakReference<TransportLogger> transportLogger;
private final String nextTransportName;
private final int id;
private final ManagementContext managementContext;
private final ObjectName name;
/**
* Constructor.
* @param transportLogger The TransportLogger object which is to be managed by this MBean.
* @param nextTransportName The name of the next TransportLayer. This is used to give a unique
* name for each MBean of the TransportLoggerView class.
* @param id The id of the TransportLogger to be watched.
* @param managementContext The management context who has the MBeanServer where this MBean will be registered.
*/
public TransportLoggerView (TransportLogger transportLogger, String nextTransportName, int id, ManagementContext managementContext) {
this.transportLogger = new WeakReference<TransportLogger>(transportLogger);
this.nextTransportName = nextTransportName;
this.id = id;
this.managementContext = managementContext;
this.name = this.createTransportLoggerObjectName();
TransportLoggerView.transportLoggerViews.add(this);
this.register();
}
/**
* Enable logging for all Transport Loggers at once.
*/
public static void enableAllTransportLoggers() {
for (TransportLoggerView view : transportLoggerViews) {
view.enableLogging();
}
}
/**
* Disable logging for all Transport Loggers at once.
*/
public static void disableAllTransportLoggers() {
for (TransportLoggerView view : transportLoggerViews) {
view.disableLogging();
}
}
// doc comment inherited from TransportLoggerViewMBean
public void enableLogging() {
this.setLogging(true);
}
// doc comment inherited from TransportLoggerViewMBean
public void disableLogging() {
this.setLogging(false);
}
// doc comment inherited from TransportLoggerViewMBean
public boolean isLogging() {
return transportLogger.get().isLogging();
}
// doc comment inherited from TransportLoggerViewMBean
public void setLogging(boolean logging) {
transportLogger.get().setLogging(logging);
}
/**
* Registers this MBean in the MBeanServer of the management context
* provided at creation time. This method is only called by the constructor.
*/
private void register() {
try {
this.managementContext.getMBeanServer().registerMBean(this, this.name);
} catch (Exception e) {
log.error("Could not register MBean for TransportLoggerView " + id + "with name " + this.name.toString() + ", reason: " + e, e);
}
}
/**
* Unregisters the MBean from the MBeanServer of the management context
* provided at creation time.
* This method is called by the TransportLogger object being managed when
* the TransportLogger object is finalized, to avoid the memory leak that
* would be caused if MBeans were not unregistered.
*/
public void unregister() {
TransportLoggerView.transportLoggerViews.remove(this);
try {
this.managementContext.getMBeanServer().unregisterMBean(this.name);
} catch (Exception e) {
log.error("Could not unregister MBean for TransportLoggerView " + id + "with name " + this.name.toString() + ", reason: " + e, e);
}
}
/**
* Creates the ObjectName to be used when registering the MBean.
* @return the ObjectName to be used when registering the MBean.
*/
private ObjectName createTransportLoggerObjectName() {
try {
return new ObjectName(
createTransportLoggerObjectNameRoot(this.managementContext)
+ JMXSupport.encodeObjectNamePart(TransportLogger.class.getSimpleName()
+ " " + this.id + ";" + this.nextTransportName));
} catch (Exception e) {
log.error("Could not create ObjectName for TransportLoggerView " + id + ", reason: " + e, e);
return null;
}
}
/**
* Creates the part of the ObjectName that will be used by all MBeans.
* This method is public so it can be used by the TransportLoggerControl class.
* @param managementContext
* @return A String with the part of the ObjectName common to all the TransportLoggerView MBeans.
*/
public static String createTransportLoggerObjectNameRoot(ManagementContext managementContext) {
return managementContext.getJmxDomainName()+":"+"Type=TransportLogger,"+"TransportLoggerName=";
}
}

View File

@ -1,37 +0,0 @@
package org.apache.activemq.transport;
/**
* MBean to manage a single Transport Logger.
* It can inform if the logger is currently writing to a log file or not,
* by setting the logging property or by using the operations
* enableLogging() and disableLogging()
*/
public interface TransportLoggerViewMBean {
/**
* Returns if the managed TransportLogger is currently active
* (writing to a log) or not.
* @return if the managed TransportLogger is currently active
* (writing to a log) or not.
*/
public boolean isLogging();
/**
* Enables or disables logging for the managed TransportLogger.
* @param logging Boolean value to enable or disable logging for
* the managed TransportLogger.
* true to enable logging, false to disable logging.
*/
public void setLogging(boolean logging);
/**
* Enables logging for the managed TransportLogger.
*/
public void enableLogging();
/**
* Disables logging for the managed TransportLogger.
*/
public void disableLogging();
}

View File

@ -1,142 +0,0 @@
package org.apache.activemq.transport.logwriters;
import java.io.IOException;
import org.apache.activemq.command.BaseCommand;
import org.apache.activemq.command.ConnectionInfo;
import org.apache.activemq.command.Message;
import org.apache.activemq.command.MessageAck;
import org.apache.activemq.command.MessageDispatch;
import org.apache.activemq.command.ProducerAck;
import org.apache.activemq.command.ProducerId;
import org.apache.activemq.command.WireFormatInfo;
import org.apache.activemq.transport.LogWriter;
import org.apache.commons.logging.Log;
/**
* Custom implementation of LogWriter interface.
*/
public class CustomLogWriter implements LogWriter {
// doc comment inherited from LogWriter
public void initialMessage(Log log) {
}
// doc comment inherited from LogWriter
public void logRequest (Log log, Object command) {
log.debug("$$ SENDREQ: " + CustomLogWriter.commandToString(command));
}
// doc comment inherited from LogWriter
public void logResponse (Log log, Object response) {
log.debug("$$ GOT_RESPONSE: "+response);
}
// doc comment inherited from LogWriter
public void logAsyncRequest (Log log, Object command) {
log.debug("$$ SENDING_ASNYC_REQUEST: "+command);
}
// doc comment inherited from LogWriter
public void logOneWay (Log log, Object command) {
log.debug("$$ SENDING: " + CustomLogWriter.commandToString(command));
}
// doc comment inherited from LogWriter
public void logReceivedCommand (Log log, Object command) {
log.debug("$$ RECEIVED: " + CustomLogWriter.commandToString(command));
}
// doc comment inherited from LogWriter
public void logReceivedException (Log log, IOException error) {
log.debug("$$ RECEIVED_EXCEPTION: "+error, error);
}
/**
* Transforms a command into a String
* @param command An object (hopefully of the BaseCommand class or subclass)
* to be transformed into String.
* @return A String which will be written by the CustomLogWriter.
* If the object is not a BaseCommand, the String
* "Unrecognized_object " + command.toString()
* will be returned.
*/
private static String commandToString(Object command) {
StringBuilder sb = new StringBuilder();
if (command instanceof BaseCommand) {
BaseCommand bc = (BaseCommand)command;
sb.append(command.getClass().getSimpleName());
sb.append(' ');
sb.append(bc.isResponseRequired() ? 'T' : 'F');
Message m = null;
if (bc instanceof Message) {
m = (Message)bc;
}
if (bc instanceof MessageDispatch){
m = ((MessageDispatch)bc).getMessage();
}
if (m != null) {
sb.append(' ');
sb.append(m.getMessageId());
sb.append(',');
sb.append(m.getCommandId());
ProducerId pid = m.getProducerId();
long sid = pid.getSessionId();
sb.append(',');
sb.append(pid.getConnectionId());
sb.append(',');
sb.append(sid);
sb.append(',');
sb.append(pid.getValue());
sb.append(',');
sb.append(m.getCorrelationId());
sb.append(',');
sb.append(m.getType());
}
if (bc instanceof MessageDispatch){
sb.append(" toConsumer:");
sb.append(((MessageDispatch)bc).getConsumerId());
}
if (bc instanceof ProducerAck) {
sb.append(" ProducerId:");
sb.append(((ProducerAck)bc).getProducerId());
}
if (bc instanceof MessageAck) {
MessageAck ma = (MessageAck)bc;
sb.append(" ConsumerID:");
sb.append(ma.getConsumerId());
sb.append(" ack:");
sb.append(ma.getFirstMessageId());
sb.append('-');
sb.append(ma.getLastMessageId());
}
if (bc instanceof ConnectionInfo) {
ConnectionInfo ci = (ConnectionInfo)bc;
sb.append(' ');
sb.append(ci.getConnectionId());
}
} else if (command instanceof WireFormatInfo){
sb.append("WireFormatInfo");
} else {
sb.append("Unrecognized_object ");
sb.append(command.toString());
}
return sb.toString();
}
}

View File

@ -1,50 +0,0 @@
package org.apache.activemq.transport.logwriters;
import java.io.IOException;
import org.apache.activemq.transport.LogWriter;
import org.apache.commons.logging.Log;
/**
* Implementation of LogWriter interface to keep ActiveMQ's
* old logging format.
*/
public class DefaultLogWriter implements LogWriter {
// doc comment inherited from LogWriter
public void initialMessage(Log log) {
// Default log writer does nothing here
}
// doc comment inherited from LogWriter
public void logRequest (Log log, Object command) {
log.debug("SENDING REQUEST: "+command);
}
// doc comment inherited from LogWriter
public void logResponse (Log log, Object response) {
log.debug("GOT RESPONSE: "+response);
}
// doc comment inherited from LogWriter
public void logAsyncRequest (Log log, Object command) {
log.debug("SENDING ASNYC REQUEST: "+command);
}
// doc comment inherited from LogWriter
public void logOneWay (Log log, Object command) {
log.debug("SENDING: "+command);
}
// doc comment inherited from LogWriter
public void logReceivedCommand (Log log, Object command) {
log.debug("RECEIVED: " + command);
}
// doc comment inherited from LogWriter
public void logReceivedException (Log log, IOException error) {
log.debug("RECEIVED Exception: "+error, error);
}
}

View File

@ -38,7 +38,6 @@ import org.apache.activemq.openwire.OpenWireFormat;
import org.apache.activemq.transport.InactivityMonitor;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportLogger;
import org.apache.activemq.transport.TransportLoggerFactory;
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.transport.WireFormatNegotiator;
import org.apache.activemq.util.IOExceptionSupport;
@ -105,12 +104,7 @@ public class SslTransportFactory extends TcpTransportFactory {
sslTransport.setSocketOptions(socketOptions);
if (sslTransport.isTrace()) {
try {
transport = TransportLoggerFactory.getInstance().createTransportLogger(transport,
sslTransport.getLogWriterName(), sslTransport.isDynamicManagement(), sslTransport.isStartLogging(), sslTransport.getJmxPort());
} catch (Throwable e) {
LOG.error("Could not create TransportLogger object for: " + sslTransport.getLogWriterName() + ", reason: " + e, e);
}
transport = new TransportLogger(transport);
}
transport = new InactivityMonitor(transport);

View File

@ -36,7 +36,6 @@ import javax.net.SocketFactory;
import org.apache.activemq.Service;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportLoggerFactory;
import org.apache.activemq.transport.TransportThreadSupport;
import org.apache.activemq.util.IntrospectionSupport;
import org.apache.activemq.util.ServiceStopper;
@ -72,12 +71,7 @@ public class TcpTransport extends TransportThreadSupport implements Transport, S
* This parameter is most probably set in Connection or TransportConnector URIs.
*/
protected boolean trace = false;
/**
* Name of the LogWriter implementation to use.
* Names are mapped to classes in the resources/META-INF/services/org/apache/activemq/transport/logwriters directory.
* This parameter is most probably set in Connection or TransportConnector URIs.
*/
protected String logWriterName = TransportLoggerFactory.defaultLogWriterName;
/**
* Specifies if the TransportLogger will be manageable by JMX or not.
* Also, as long as there is at least 1 TransportLogger which is manageable,
@ -207,16 +201,6 @@ public class TcpTransport extends TransportThreadSupport implements Transport, S
this.trace = trace;
}
public String getLogWriterName() {
return logWriterName;
}
public void setLogWriterName(String logFormat) {
this.logWriterName = logFormat;
}
public boolean isDynamicManagement() {
return dynamicManagement;
}

View File

@ -30,7 +30,7 @@ import org.apache.activemq.openwire.OpenWireFormat;
import org.apache.activemq.transport.InactivityMonitor;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportFactory;
import org.apache.activemq.transport.TransportLoggerFactory;
import org.apache.activemq.transport.TransportLogger;
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.transport.WireFormatNegotiator;
import org.apache.activemq.util.IOExceptionSupport;
@ -84,12 +84,7 @@ public class TcpTransportFactory extends TransportFactory {
tcpTransport.setSocketOptions(socketOptions);
if (tcpTransport.isTrace()) {
try {
transport = TransportLoggerFactory.getInstance().createTransportLogger(transport, tcpTransport.getLogWriterName(),
tcpTransport.isDynamicManagement(), tcpTransport.isStartLogging(), tcpTransport.getJmxPort());
} catch (Throwable e) {
LOG.error("Could not create TransportLogger object for: " + tcpTransport.getLogWriterName() + ", reason: " + e, e);
}
transport = new TransportLogger(transport);
}
if (isUseInactivityMonitor(transport)) {

View File

@ -33,7 +33,6 @@ import javax.net.ServerSocketFactory;
import org.apache.activemq.command.BrokerInfo;
import org.apache.activemq.openwire.OpenWireFormatFactory;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportLoggerFactory;
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.transport.TransportServerThreadSupport;
import org.apache.activemq.util.IOExceptionSupport;
@ -67,12 +66,6 @@ public class TcpTransportServer extends TransportServerThreadSupport {
* This parameter is most probably set in Connection or TransportConnector URIs.
*/
protected boolean trace = false;
/**
* Name of the LogWriter implementation to use.
* Names are mapped to classes in the resources/META-INF/services/org/apache/activemq/transport/logwriters directory.
* This parameter is most probably set in Connection or TransportConnector URIs.
*/
protected String logWriterName = TransportLoggerFactory.defaultLogWriterName;
/**
* Specifies if the TransportLogger will be manageable by JMX or not.
* Also, as long as there is at least 1 TransportLogger which is manageable,
@ -177,15 +170,6 @@ public class TcpTransportServer extends TransportServerThreadSupport {
this.trace = trace;
}
public String getLogWriterName() {
return logWriterName;
}
public void setLogWriterName(String logFormat) {
this.logWriterName = logFormat;
}
public boolean isDynamicManagement() {
return dynamicManagement;
}
@ -219,7 +203,6 @@ public class TcpTransportServer extends TransportServerThreadSupport {
options.put("maxInactivityDuration", Long.valueOf(maxInactivityDuration));
options.put("minmumWireFormatVersion", Integer.valueOf(minmumWireFormatVersion));
options.put("trace", Boolean.valueOf(trace));
options.put("logWriterName", logWriterName);
options.put("dynamicManagement", Boolean.valueOf(dynamicManagement));
options.put("startLogging", Boolean.valueOf(startLogging));

View File

@ -28,7 +28,7 @@ import org.apache.activemq.transport.CommandJoiner;
import org.apache.activemq.transport.InactivityMonitor;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportFactory;
import org.apache.activemq.transport.TransportLoggerFactory;
import org.apache.activemq.transport.TransportLogger;
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.transport.reliable.DefaultReplayStrategy;
import org.apache.activemq.transport.reliable.ExceptionIfDroppedReplayStrategy;
@ -78,11 +78,7 @@ public class UdpTransportFactory extends TransportFactory {
transport = new CommandJoiner(transport, asOpenWireFormat(format));
if (udpTransport.isTrace()) {
try {
transport = TransportLoggerFactory.getInstance().createTransportLogger(transport);
} catch (Throwable e) {
log.error("Could not create TransportLogger object for: " + TransportLoggerFactory.defaultLogWriterName + ", reason: " + e, e);
}
transport = new TransportLogger(transport);
}
transport = new InactivityMonitor(transport);
@ -114,7 +110,7 @@ public class UdpTransportFactory extends TransportFactory {
OpenWireFormat openWireFormat = asOpenWireFormat(format);
if (udpTransport.isTrace()) {
transport = TransportLoggerFactory.getInstance().createTransportLogger(transport);
transport = new TransportLogger(transport);
}
transport = new InactivityMonitor(transport);

View File

@ -1,122 +0,0 @@
package org.apache.activemq.util;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.activemq.transport.LogWriter;
import org.apache.activemq.transport.TransportLoggerView;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class used to find a LogWriter implementation, and returning
* a LogWriter object, taking as argument the name of a log writer.
* The mapping between the log writer names and the classes
* implementing LogWriter is specified by the files in the
* resources/META-INF/services/org/apache/activemq/transport/logwriters
* directory.
*/
public class LogWriterFinder {
private static final Log log = LogFactory.getLog(TransportLoggerView.class);
private final String path;
private final ConcurrentHashMap classMap = new ConcurrentHashMap();
/**
* Builds a LogWriterFinder that will look for the mappings between
* LogWriter names and classes in the directory "path".
* @param path The directory where the files that map log writer names to
* LogWriter classes are.
*/
public LogWriterFinder(String path) {
this.path = path;
}
/**
* Returns a LogWriter object, given a log writer name (for example "default", or "detailed").
* Uses a ConcurrentHashMap to cache the Class objects that have already been loaded.
* @param logWriterName a log writer name (for example "default", or "detailed").
* @return a LogWriter object to be used by the TransportLogger class.
* @throws IllegalAccessException
* @throws InstantiationException
* @throws IOException
* @throws ClassNotFoundException
*/
public LogWriter newInstance(String logWriterName)
throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException
{
Class clazz = (Class) classMap.get(logWriterName);
if (clazz == null) {
clazz = newInstance(doFindLogWriterProperties(logWriterName));
classMap.put(logWriterName, clazz);
}
return (LogWriter)clazz.newInstance();
}
/**
* Loads and returns a class given a Properties object with a "class" property.
* @param properties a Properties object with a "class" property.
* @return a Class object.
* @throws ClassNotFoundException
* @throws IOException
*/
private Class newInstance(Properties properties) throws ClassNotFoundException, IOException {
String className = properties.getProperty("class");
if (className == null) {
throw new IOException("Expected property is missing: " + "class");
}
Class clazz;
try {
clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
clazz = LogWriterFinder.class.getClassLoader().loadClass(className);
}
return clazz;
}
/**
* Given a log writer name, returns a Properties object with a "class" property
* whose value is a String with the name of the class to be loaded.
* @param logWriterName a log writer name.
* @return a Properties object with a "class" property
* @throws IOException
*/
protected Properties doFindLogWriterProperties (String logWriterName) throws IOException {
String uri = path + logWriterName;
// lets try the thread context class loader first
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) classLoader = getClass().getClassLoader();
InputStream in = classLoader.getResourceAsStream(uri);
if (in == null) {
in = LogWriterFinder.class.getClassLoader().getResourceAsStream(uri);
if (in == null) {
log.error("Could not find log writer for resource: " + uri);
throw new IOException("Could not find log writer for resource: " + uri);
}
}
// lets load the file
BufferedInputStream reader = null;
Properties properties = new Properties();
try {
reader = new BufferedInputStream(in);
properties.load(reader);
return properties;
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
}

View File

@ -1 +0,0 @@
class=org.apache.activemq.transport.logwriters.CustomLogWriter

View File

@ -1 +0,0 @@
class=org.apache.activemq.transport.logwriters.DefaultLogWriter

View File

@ -1,14 +0,0 @@
"""
Module Main
"""
from loganalyzergui.Application import Application
def main():
"""
Entrance point for the application
"""
app = Application(0)
app.MainLoop()
if __name__ == '__main__':
main()

View File

@ -1,64 +0,0 @@
Readme file for the LogAnalyzer application.
1. Requirements:
-Python 2.5.1 (http://www.python.org/download/releases/2.5.1/ or your favorite package)
-wxPython 2.8.4 (http://www.wxpython.org/download.php or your favorite package)
2. How to execute:
Run 'python Main.py'.
3. Some instructions:
-This tool will analyze ActiveMQ log files that have been produced
using the 'custom' transport log format. To analyze the files,
put them in a directory, choose that directory and click 'Parse'.
Please don't put any other kind of files in the same directory
(sub-directories won't cause any problem, but the files inside
them will not be analyzed).
For example, imagine you have a setup with 4 machines: 1 has producers,
2 are brokers, and 1 has consumers. As long as you have 1 JVM per machine,
you should have 4 log files. Call the files p.log, b1.log, b2.log,
and c.log, for example. Put the 4 files in the same directory,
choose that directory and click the 'Parse' button.
-The first tab of the tool shows incorrect situations at transport level:
(i) Messages that were sent through a connection, but were not received
at the other end.
(ii) Messages that were received through a connection, but were not sent
(probably you are missing the log file of the JVM that sent the message).
(iii) Messages that are sent 2 times through the same connection.
(iv) Messages that were sent 2 times by the same JVM, but through
different connections.
By clicking the 'Show results with short ids' checkbox, you can switch
between the real connection / producer id used by ActiveMQ,
or a unique integer assigned by the tool.
Often it's easier to compare and browse with this integers than with
the original id's which are often long strings.
The 'Message id' column shows 2 things: the id of the producer that
originally issued the message, and the 'Producer Sequence Id' of a message.
These 2 items identify a message in a unique way.
You can use the checkboxes to filter per type.
You can also filter by a given connection (but then problems of type (iv)
will not appear because they 'belong' to more than one connection).
You can input a 'long id' (the original ActiveMQ id) or a 'short id'
(a short integer assigned by the tool to each connection).
-The second tab of the tool allows you to get a lot of information
about a single message. Input the producer id of the original producer
of the message, and the message's 'Producer Sequence Id'.
You can choose to use the original ActiveMQ producer id (long id)
or the short integer assigned to a producer by the tool.
You can also use the 'Jump to Message Browsing' button of the 1st tab
to see the information about the problems of a given message
without having to copy the message id manually.
In this tab you can also use the 'Show results with short ids' checkbox.
-The third tab gives a summary of the clients (producer and consumers)
which appear in the log files. Each client is identified by a short id,
and belongs to a connection (whose 'short id' and 'long id' are shown).
-The fourth tab gives a summary of the connections involved,
and the clients that belong to them.
-The fifth tab gives a summary of the log files analyzed,
and the connections in each of the files.

View File

@ -1,337 +0,0 @@
"""
Module Connection
"""
import itertools
class Connection(object):
"""
This class represents an ActiveMQ Connection.
It also stores a collection of the connections
that have been read in the log files.
A Connection's id is the ActiveMQConnection's id. Since this is usually a long
alphanumerical string, it is called 'longId'.
Each new Connection gets also assigned an integer 'shortId' automatically.
The function of this 'shortId' is to make understanding of displayed data easier.
A Connection has 2 LogFile members, who represent:
-the log file of the JVM that initiates a connection.
-the log file of the JVM that receives a connection request.
The purpose of every Connection is to store the following data:
-messages sent through this connection, as a dictionary where the
key is a tuple (message, direction) and the value is
a list of timestamps. If the message was sent only one time (normal case),
the timestamp list will have 1 item only.
-messages received through this connection, as a dictionary
analogous to the previous one.
-messages sent but not received through this connection, as a list of
tuples (storedMessage, ntimes, timestamps).
'storedMessage' is a (message, direction) tuple
ntimes, an integer, is the number of times a message was sent but not received
timestamps is a list of timestamps of when the message was sent or received.
For a message to be in this list, ntimes must be >= 1.
-messages received but not sent through this connection.
Analog to previous point.
-messages sent more than 2 more times through this connection, as a list of
tuples (storedMessage, ntimes, timestamps).
'storedMessage' is a (message, direction) tuple
ntimes, an integer, is the number of times a message was sent.
timestamps is a list of timestamps of when the message was sent.
For a message to be in this list, ntimes must be >= 2.
-messages received more than 2 more times through this connection.
Identical structure to the previous point.
The 'direction' value is either True or False.
True represents that the message was sent from the JVM writing to the
'from' file, to the JVM writing to the 'to' file.
False represents the opposite.
"""
#dictionary whose keys are connection ids, and whose values
#are Connection objects
connections = {}
nConnections = 0
connectionIdList = []
def __init__(self, longId, fromFile = None, toFile = None):
"""
Constructs a Connection object.
longId : string
fromFile: LogFile object
to: LogFile object
The ActiveMQConnection's id has to be provided.
Optionally, 2 LogFile objects can be provided.
The 'from' file is the log file of the JVM that initiates a connection.
The 'to' file is the log file of the JVM that receives a connection request.
A new connection gets automatically a new 'shortId', which is an integer.
The longId gets also stored in a list of longIds.
Returns a Connection object.
"""
self.longId = longId
self.fromFile = fromFile
self.toFile = toFile
self.shortId = Connection.nConnections
Connection.connectionIdList.append(longId)
Connection.nConnections += 1
self.producers = set()
self.consumers = set()
self.sent = {}
self.received = {}
self.duplicateSent = None
self.duplicateReceived = None
self.sentButNotReceived = None
self.receivedButNotSent = None
self.calculated = False
@classmethod
def clearData(cls):
"""
Deletes all information read about connections.
Returns nothing.
"""
cls.connections.clear()
cls.nConnections = 0
del cls.connectionIdList[:]
@classmethod
def getConnectionByLongId(cls, longId):
"""
Retrieves the connection whose id is 'longId'.
If there is no connection with this id, a new
one is created with this id.
Returns a Connection object.
"""
if longId not in cls.connections:
cls.connections[longId] = Connection(longId)
return cls.connections[longId]
@classmethod
def getConnectionByShortId(cls, shortId):
"""
Retrieves the connection whose shortId is 'shortId'.
If there is no connection with this id,
an IndexError exception will be thrown.
Returns a Connection object.
Throws an IndexError if the short id does not exist.
"""
return cls.connections[cls.connectionIdList[shortId]]
@classmethod
def shortIdToLongId(cls, shortId):
"""
Transforms a connection's short id to a long id.
Returns the long id.
Throws an IndexError if the short id does not exist.
"""
return cls.connectionIdList[shortId]
@classmethod
def longIdToShortId(cls, longId):
"""
Transforms a connection's long id to a short id.
Returns the short id.
Throws an KeyError if the short id does not exist.
"""
try:
return cls.connections[longId].shortId
except KeyError:
print longId
print cls.connections
raise
@classmethod
def setFrom(cls, longId, fromFile):
"""
Sets the 'from' LogFile object for the connection whose id is 'longId'.
The 'from' file is the log file of the JVM that initiates a connection.
If there is not yet a connection whose id is 'longId', a new one is
created with this longId and this 'from' file.
Returns nothing.
"""
if longId not in cls.connections:
cls.connections[longId] = Connection(longId, fromFile = fromFile)
else:
cls.connections[longId].fromFile = fromFile
@classmethod
def setTo(cls, longId, toFile):
"""
Sets the 'to' LogFile object for the connection whose id is 'longId'.
The 'to' file is the log file of the JVM that receives a connection request.
If there is not yet a connection whose id is 'longId', a new one is
created with this longId and this 'to' file.
Returns nothing.
"""
if longId not in cls.connections:
cls.connections[longId] = Connection(longId, toFile = toFile)
else:
cls.connections[longId].toFile = toFile
@classmethod
def exists(cls, longId):
"""
Returns if there is a connection whose id is 'longId'
"""
return longId in cls.connections
def addProducer(self, producer):
"""
Adds a producer to the set of this connection's producers.
Returns nothing.
"""
self.producers.add(producer)
def addConsumer(self, consumer):
"""
Adds a consumer to the set of this connection's consumers.
Returns nothing.
"""
self.consumers.add(consumer)
def addSentMessage(self, message, direction, timestamp):
"""
Adds a message to the set of messages sent through this connection.
message: a Message object
direction: True if this message was sent from self.fromFile to self.to
False if this message was sent from self.toFile to self.fromFile
timestamp: a string with the time this message was sent
If the message has already been sent in this direction, it gets added to the
collection of duplicate sent messages.
Returns nothing.
"""
storedMessage = (message, direction)
if storedMessage in self.sent:
self.sent[storedMessage].append(timestamp)
else:
self.sent[storedMessage] = [timestamp]
def addReceivedMessage(self, message, direction, timestamp):
"""
Adds a message to the set of messages received through this connection.
message: a message object
direction: True if this message was sent from self.fromFile to self.to
False if this message was sent from self.toFile to self.fromFile
timestamp: a string with the time this message was sent
If the message has already been received in this direction, it gets added to the
collection of duplicate received messages.
Returns nothing.
"""
storedMessage = (message, direction)
if storedMessage in self.received:
self.received[storedMessage].append(timestamp)
else:
self.received[storedMessage] = [timestamp]
def getErrors(self):
"""
Processes the data previously gathered to find incorrect situations.
Returns a 4-tuple with:
-collection of sent but not received messages, through this Connection.
This collection is a list of (storedMessage, ntimes, timestamps) tuples where:
*'storedMessage' is a (message, direction) tuple.
*'ntimes' is an integer, representing how many times the message was sent but not received.
*'timestamps' is a list of strings with the timestamps when this message was sent / received.
-collection of received but not sent messages, through this Connection.
This collection is a list of (storedMessage, ntimes, timestamps) tuples where:
*'storedMessage' is a (message, direction) tuple.
*'ntimes' is an integer, representing how many times the message was received but not sent.
*'timestamps' is a list of strings with the timestamps when this message was sent / received.
-collection of duplicate sent messages, through this Connection.
This collection is a list of (message, timestamps) tuples where:
*'storedMessage' is a (shortId, commandId, direction) tuple.
*'ntimes' is an integer, representing how many times the message sent.
*'timestamps' is a list of strings with the timestamps when this message was sent.
-collection of duplicate received messages, through this Connection.
This collection is a list of (message, timestamps) tuples where:
*'storedMessage' is a (message, direction) tuple.
*'ntimes' is an integer, representing how many times the message received.
*'timestamps' is a list of strings with the timestamps when this message was received.
The data is only calculated once, and then successive calls of this method return always
the same erros unles self.calculated is set to False.
"""
if not self.calculated:
self.sentButNotReceived = []
for message, timestamps in self.sent.iteritems():
if message not in self.received:
self.sentButNotReceived.append((message, len(timestamps), timestamps))
else:
difference = len(timestamps) - len(self.received[message])
if difference > 0:
self.sentButNotReceived.append((message, difference,
itertools.chain(timestamps, self.received[message])))
self.receivedButNotSent = []
for message, timestamps in self.received.iteritems():
if message not in self.sent:
self.receivedButNotSent.append((message, len(timestamps), timestamps))
else:
difference = len(timestamps) - len(self.sent[message])
if difference > 0:
self.receivedButNotSent.append((message, difference,
itertools.chain(timestamps, self.sent[message])))
self.duplicateSent = [(message, len(timestamps), timestamps)
for message, timestamps in self.sent.iteritems() if len(timestamps) > 1]
self.duplicateReceived = [(message, len(timestamps), timestamps)
for message, timestamps in self.received.iteritems() if len(timestamps) > 1]
self.sentButNotReceived.sort(key = lambda message: (message[0][0].producer.shortId, message[0][0].prodSeqId))
self.receivedButNotSent.sort(key = lambda message: (message[0][0].producer.shortId, message[0][0].prodSeqId))
self.duplicateSent.sort(key = lambda message: (message[0][0].producer.shortId, message[0][0].prodSeqId))
self.duplicateReceived.sort(key = lambda message: (message[0][0].producer.shortId, message[0][0].prodSeqId))
self.calculated = True
return self.sentButNotReceived, self.receivedButNotSent, self.duplicateSent, self.duplicateReceived
def __str__(self):
"""
Represents this Connection object as a string.
"""
return ''.join([self.longId, ' from:', str(self.fromFile), ' to:', str(self.toFile)])

View File

@ -1,76 +0,0 @@
"""
Module Consumer
"""
class Consumer(object):
"""
This class represents an ActiveMQ Consumer.
Each consumer is identified by its long id.
However each consumer also has a short id (an integer) to identify it more easily.
"""
nConsumers = 0
consumerIdList = []
consumers = {}
def __init__(self, longId):
"""
Constructor
"""
self.longId = longId
self.shortId = Consumer.nConsumers
self.connectionId, sessionId, value = longId.rsplit(':', 2)
self.sessionId = int(sessionId)
self.value = int(value)
Consumer.consumers[longId] = self
Consumer.consumerIdList.append(self.longId)
Consumer.nConsumers += 1
@classmethod
def clearData(cls):
"""
Deletes all information read about Consumers.
Returns nothing.
"""
cls.consumers.clear()
cls.nConsumers = 0
del cls.consumerIdList[:]
@classmethod
def getConsumerByLongId(cls, longId):
"""
Returns a consumer given its long id.
If there is no consumer with this long id yet, it will be created.
"""
if longId not in cls.consumers:
cls.consumers[longId] = Consumer(longId)
return cls.consumers[longId]
@classmethod
def shortIdToLongId(cls, shortId):
"""
Transforms a consumer's short id to a long id.
Returns a long id.
Throws an IndexError if the short id does not exist.
"""
return cls.consumerIdList[shortId]
@classmethod
def longIdToShortId(cls, longId):
"""
Transforms a consumer's long id to a short id.
Returns a long id.
Throws an KeyError if the long id does not exist.
"""
return cls.consumers[longId].shortId

View File

@ -1,187 +0,0 @@
"""
Module LogFile
"""
import os
class LogFile(object):
"""
Class that represents an ActiveMQ log file read by the application.
It also stores a list of all the LogFile objects.
A LogFile object stores the following information:
-A list of 'outgoing' Connection objects that represent the connections
created by the JVM that writes this LogFile.
-A list of 'incoming' Connection objects that represent the connections
requests received by the JVM that writes this LogFile.
-A dictionary of messages that were sent in this file.
The keys are Message objects and the values
are lists of timestamps of when the message was sent.
-A list of messages that were received in this file.
The keys are Message objects and the values
are lists of timestamps of when the message was received.
-A list of messages that were sent in this file more than 1 time (duplicates)
The list is made of (message, ntimes, timestamps) tuples.
-A list of messages that were received in this file more than 1 time (duplicates),
analogous to the previous structure.
"""
logfiles = []
def __init__(self, path):
"""
Constructs a LogFile object.
path: a string with the path to the ActiveMQ log file.
"""
self.__path = os.path.abspath(path)
self.file = open(self.__path, 'r')
self.outgoing = []
self.incoming = []
self.sent = {}
self.received = {}
self.duplicateReceived = None
self.duplicateSent = None
self.calculated = False
LogFile.logfiles.append(self)
@classmethod
def clearData(cls):
"""
Class method erases all the LogFile objects stored in the LogFile class.
Returns nothing.
"""
del cls.logfiles[:]
@classmethod
def closeFiles(cls):
"""
Class method that closes all the LogFile objects stored in the LogFile class.
Returns nothing.
"""
for logFile in cls.logfiles:
logFile.file.close()
def addOutgoingConnection(self, con):
"""
Adds an 'outgoing' Connection object to this LogFile.
Returns nothing.
"""
self.outgoing.append(con)
def addIncomingConnection(self, con):
"""
Adds an 'incoming' Connection object to this LogFile.
Returns nothing.
"""
self.incoming.append(con)
def addSentMessage(self, message, timestamp):
"""
Adds a message to the set of messages that were sent thtough this file.
If a message gets sent 2 times, it gets added to the set of duplicate sent messages.
message: a Message object.
timestamp: a string with the time where this message was sent.
Returns nothing.
"""
if message in self.sent:
self.sent[message].append(timestamp)
else:
self.sent[message] = [timestamp]
def addReceivedMessage(self, message, timestamp):
"""
Adds a message to the set of messages that were received in this file.
If a message gets sent 2 times, it gets added to the set of duplicate received messages.
message: a Message object.
timestamp: a string with the time where this message was sent.
Returns nothing.
"""
#message = (shortProdId, prodSeqId, False)
if message in self.received:
self.received[message].append(timestamp)
else:
self.received[message] = [timestamp]
def getErrors(self):
"""
Returns a 2-tuple with:
-a list of (message, ntimes, timestamps) tuples, with the duplicate sent messages
that appear in more than one connection in this file.
'message' is a Message object.
'ntimes' is an integer stating how many times the message was sent ( always >= 2)
'timestamps' is a list of timestamps with the instants the message was sent.
-a list of (message, ntimes, timestamps) tuples, with the duplicate received messages
that appear in more than one connection in this file.
Structure analogous to previous one.
The data is only calculated once, and then successive calls of this method return always
the same erros unles self.calculated is set to False.
"""
if not self.calculated:
duplicateSentTemp = [(message, len(timestamps), timestamps)
for message, timestamps in self.sent.iteritems() if len(timestamps) > 1]
self.duplicateSent = []
for message, _, timestamps in duplicateSentTemp:
connections = []
for connection, direction in message.sendingConnections:
if direction and connection.fromFile == self \
or not direction and connection.toFile == self:
connections.append(connection)
if len(connections) > 1:
self.duplicateSent.append((message, len(timestamps), timestamps))
duplicateReceivedTemp = [(message, len(timestamps), timestamps)
for message, timestamps in self.received.iteritems() if len(timestamps) > 1]
self.duplicateReceived = []
for message, _, timestamps in duplicateReceivedTemp:
connections = []
for connection, direction in message.receivingConnections:
if direction and connection.toFile == self \
or not direction and connection.fromFile == self:
connections.append(connection)
if len(connections) > 1:
self.duplicateReceived.append((message, len(timestamps), timestamps))
self.duplicateSent.sort(key = lambda message: (message[0].producer.shortId, message[0].prodSeqId))
self.duplicateReceived.sort(key = lambda message: (message[0].producer.shortId, message[0].prodSeqId))
self.calculated = True
return self.duplicateSent, self.duplicateReceived
def close(self):
"""
Closes the underlying file.
Returns nothing.
"""
self.file.close()
def __str__(self):
"""
Returns a string representation of this object.
"""
return self.__path

View File

@ -1,188 +0,0 @@
"""
Module LogParser
"""
import os, sys, time
from LogFile import LogFile
from Connection import Connection
from Producer import Producer
from Consumer import Consumer
from Message import Message
MESSAGE_TYPES = frozenset(['ActiveMQBytesMessage', 'ActiveMQTextMessage'])
DISPATCH_MESSAGE = 'MessageDispatch'
ADVISORY_TEXT = 'Advisory'
CONSUMER_TEXT = 'toConsumer:'
class LogParser(object):
"""
This class is in charge of parsing the log files and storing the data
as Connection, LogFile and Message objects.
"""
instance = None
@classmethod
def getInstance(cls):
"""
Returns the sole instance of the class.
"""
if cls.instance is None:
cls.instance = LogParser()
return cls.instance
@classmethod
def deleteInstance(cls):
"""
Deletes the sole instance of the class
"""
cls.instance = None
def parse (self, logFile):
"""
Parses the information in a log file.
logFile should be a LogFile object.
Returns nothing.
"""
try:
for line in logFile.file:
loggedMessage = line.partition('$$ ')[2]
if loggedMessage != '':
spacedStrings = loggedMessage.split()
if spacedStrings[1] == 'ConnectionInfo':
connectionId = spacedStrings[3]
if spacedStrings[0] == 'SENDING:':
logFile.addOutgoingConnection(Connection.getConnectionByLongId(connectionId))
Connection.setFrom(connectionId, logFile)
elif spacedStrings[0] == 'RECEIVED:':
logFile.addIncomingConnection(Connection.getConnectionByLongId(connectionId))
Connection.setTo(connectionId, logFile)
else:
raise Exception('Exception: ConnectionInfo: not SENDING or RECEIVED')
elif spacedStrings[1] in MESSAGE_TYPES or spacedStrings[1] == DISPATCH_MESSAGE:
timestamp = line[0:23]
commaValues = spacedStrings[3].split(',')
messageId = commaValues[0]
producerId = messageId[:messageId.rindex(':')]
connection = Connection.getConnectionByLongId(commaValues[2]) #commaValues[2] = connectionId
producer = Producer.getProducerByLongId(producerId)
producerConnection = Connection.getConnectionByLongId(producerId.rsplit(':', 2)[0]) #producerConnectionId
message = Message.getMessage(producer,
int(messageId[messageId.rindex(':') + 1:]), #producerSequenceId
commaValues[-1] == ADVISORY_TEXT)
producerConnection.addProducer(producer)
if spacedStrings[1] in MESSAGE_TYPES:
if spacedStrings[0] == 'SENDING:':
direction = (logFile == connection.fromFile)
connection.addSentMessage(message, direction, timestamp)
logFile.addSentMessage(message, timestamp)
message.addSendingConnection(connection, direction, connection,
int(commaValues[1]), timestamp) #commaValues[1] = commandId
elif spacedStrings[0] == 'RECEIVED:':
direction = (logFile == connection.toFile)
connection.addReceivedMessage(message, direction, timestamp)
logFile.addReceivedMessage(message, timestamp)
message.addReceivingConnection(connection, direction, connection,
int(commaValues[1]), timestamp) #commaValues[1] = commandId
elif spacedStrings[1] == DISPATCH_MESSAGE:
#additional parsing to get the consumer
consumerId = spacedStrings[4][len(CONSUMER_TEXT):]
consumer = Consumer.getConsumerByLongId(consumerId)
consumerConnection = Connection.getConnectionByLongId(':'.join(consumerId.split(':')[:3]))
consumerConnection.addConsumer(consumer)
if spacedStrings[0] == 'SENDING:':
direction = (logFile == connection.fromFile)
consumerConnection.addSentMessage(message, direction, timestamp)
logFile.addSentMessage(message, timestamp)
message.addSendingConnection(consumerConnection, direction, connection,
int(commaValues[1]), timestamp) #commaValues[1] = commandId
elif spacedStrings[0] == 'RECEIVED:':
direction = (logFile == connection.toFile)
consumerConnection.addReceivedMessage(message, direction, timestamp)
logFile.addReceivedMessage(message, timestamp)
message.addReceivingConnection(consumerConnection, direction, connection,
int(commaValues[1]), timestamp) #commaValues[1] = commandId
except Exception:
print logFile, line
raise
def clearData(self):
"""
Clears all the data parsed.
"""
Connection.clearData()
Producer.clearData()
Consumer.clearData()
Message.clearData()
LogFile.clearData()
def parseDirectory(self, directory):
"""
Parses a directory of log files.
"""
self.clearData()
fileNames = os.walk(directory).next()[2]
logFiles = [LogFile(directory + os.sep + fileName) for fileName in fileNames]
for logFile in logFiles:
self.parse(logFile)
LogFile.closeFiles()
def main():
"""
Entrance point for the command line test.
"""
if len(sys.argv) != 2:
print 'Usage: python LogParser.py directory'
else:
startTime = time.time()
LogParser.getInstance().parseDirectory(sys.argv[1])
LogParser.deleteInstance()
print str(Message.messageCount) + ' messages parsed'
print 'in ' + str(time.time() - startTime) + ' seconds'
print 'press a key'
sys.stdin.read(3)
startTime = time.time()
for connection in Connection.connections.itervalues():
connection.getErrors()
for logFile in LogFile.logfiles:
logFile.getErrors()
print 'additional: ' + str(time.time() - startTime) + ' seconds'
time.sleep(36000)
if __name__ == '__main__':
main()

View File

@ -1,120 +0,0 @@
"""
Module Message
"""
class Message(object):
"""
Objects of this class represent ActiveMQ messages.
They are used to store the 'travel path' of every message.
This class also stores a collection of all the Message objects.
"""
messages = {}
messageCount = 0
def __init__(self, producer, prodSeqId, advisory):
"""
Constructs a message object, given a producer and producer sequence id.
"""
self.producer = producer
self.prodSeqId = prodSeqId
self.advisory = advisory
self.sendingConnections = {}
self.receivingConnections = {}
Message.messageCount += 1
@classmethod
def clearData(cls):
"""
Deletes all the messages.
Returns nothing.
"""
cls.messages.clear()
cls.messageCount = 0
@classmethod
def getMessage(cls, producer, prodSeqId, advisory = False):
"""
Returns the Message object identified by (producer, prodSeqId)
where producer is a producer object and prodSeqId is a producer sequence id
message was first sent.
If the Message object does not exist, it will be created.
Returns a Message object.
"""
messageId = (producer, prodSeqId)
if messageId not in cls.messages:
cls.messages[messageId] = Message(producer, prodSeqId, advisory)
return cls.messages[messageId]
@classmethod
def exists(cls, producer, prodSeqId):
"""
Returns if there is a Message object identified by (producer, prodSeqId)
"""
return (producer, prodSeqId) in cls.messages
def addSendingConnection(self, connection, direction, mostRecentConId, commandId, timestamp):
"""
Adds a connection to the set of connections through which this message was sent.
The 'direction' argument is True if the message was sent from the file
connection.fromFile to the file connection.toFile, and False otherwise.
'timestamp' is a string with the moment this message was sent trough the connection.
Returns nothing.
"""
storedConnection = (connection, direction)
if storedConnection in self.sendingConnections:
self.sendingConnections[storedConnection].append((mostRecentConId, commandId, timestamp))
else:
self.sendingConnections[storedConnection] = [(mostRecentConId, commandId, timestamp)]
def addReceivingConnection(self, connection, direction, mostRecentConId, commandId, timestamp):
"""
Adds a connection to the set of connections where this message was received.
The 'direction' argument is True if the message was sent from the file
connection.fromFile to the file connection.toFile, and False otherwise.
'timestamp' is a string with the moment this message was received trough the connection.
Returns nothing.
"""
storedConnection = (connection, direction)
if storedConnection in self.receivingConnections:
self.receivingConnections[storedConnection].append((mostRecentConId, commandId, timestamp))
else:
self.receivingConnections[storedConnection] = [(mostRecentConId, commandId, timestamp)]
def getFiles(self):
"""
Returns a 2-tuple with the following 2 sets:
-set of LogFile objects where this message was sent.
-set of LogFile objects where this message was received.
"""
sendingFiles = set()
receivingFiles = set()
for connection, direction in self.sendingConnections:
if direction:
sendingFiles.add(connection.fromFile)
else:
sendingFiles.add(connection.toFile)
for connection, direction in self.receivingConnections:
if direction:
receivingFiles.add(connection.toFile)
else:
receivingFiles.add(connection.fromFile)
return sendingFiles, receivingFiles

View File

@ -1,94 +0,0 @@
"""
Module Producer
"""
class Producer(object):
"""
This class represents an ActiveMQ Producer.
Each producer is identified by its long id.
However each producer also has a short id (an integer) to identify it more easily.
"""
nProducers = 0
producerIdList = []
producers = {}
def __init__(self, longId):
"""
Constructor
"""
self.longId = longId
self.shortId = Producer.nProducers
self.connectionId, sessionId, value = longId.rsplit(':', 2)
self.sessionId = int(sessionId)
self.value = int(value)
Producer.producers[longId] = self
Producer.producerIdList.append(self.longId)
Producer.nProducers += 1
@classmethod
def clearData(cls):
"""
Deletes all information read about producers.
Returns nothing.
"""
cls.producers.clear()
cls.nProducers = 0
del cls.producerIdList[:]
@classmethod
def getProducerByLongId(cls, longId):
"""
Returns a producer given its long id.
If there is no producer with this long id yet, it will be created.
"""
if longId not in cls.producers:
cls.producers[longId] = Producer(longId)
return cls.producers[longId]
@classmethod
def getProducerByShortId(cls, shortId):
"""
Returns a producer given its short id.
If there is no producer with thi short id yet, IndexError will be thrown.
"""
return cls.producers[cls.producerIdList[shortId]]
@classmethod
def exists(cls, longid):
"""
Returns if a producer with the given long id exists.
"""
return longid in cls.producers
@classmethod
def shortIdToLongId(cls, shortId):
"""
Transforms a producer's short id to a long id.
Returns a long id.
Throws an IndexError if the short id does not exist.
"""
return cls.producerIdList[shortId]
@classmethod
def longIdToShortId(cls, longId):
"""
Transforms a producer's long id to a short id.
Returns a long id.
Throws an KeyError if the long id does not exist.
"""
return cls.producers[longId].shortId

View File

@ -1,71 +0,0 @@
"""
Module Application
"""
import wx
from DirectoryPanel import DirectoryPanel
from TabbedPanel import TabbedPanel
class MainPanel(wx.Panel):
"""
Panel contained into the window of the application.
It contains a DirectoryPanel and a TabbedPanel.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
self.tabbedPanel = TabbedPanel(self)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(DirectoryPanel(self), 0, wx.EXPAND|wx.ALL, 5)
sizer.Add(self.tabbedPanel, 1, wx.EXPAND)
self.SetSizer(sizer)
def logDataUpdated(self):
"""
Method to be called when the parsed data has been updated.
The Panel will notify its children components.
"""
self.tabbedPanel.logDataUpdated()
class MainFrame(wx.Frame):
"""
This class represents the window of the application.
It contains a MainPanel object.
We need to add a wx.Panel to a wx.Frame to avoid
graphical problems in Windows.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Frame.__init__(self, parent, 100,
'ActiveMQ Log Analyzer Tool', size=(1024,800))
# ib = wx.IconBundle()
# ib.AddIconFromFile("logparser.ico", wx.BITMAP_TYPE_ANY)
# self.SetIcons(ib)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(MainPanel(self), 1, wx.EXPAND)
self.SetSizer(sizer)
self.Centre()
self.Show(True)
class Application(wx.App):
"""
Main class of the application
"""
def OnInit(self):
"""
To be executed when Application is launched
"""
MainFrame(None)
return True

View File

@ -1,61 +0,0 @@
"""
Module DirectoryPanel
"""
import wx
import os
from loganalyzerengine.LogParser import LogParser
class DirectoryPanel(wx.Panel):
"""
Panel to choose the directory with the log files to be parsed,
and launch the parsing / analyzing process.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
self.mainPanel = parent
self.textctrl = wx.TextCtrl(self, -1, 'C:\logs')
self.lastDirectoryOpen = ''
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(wx.StaticText(self, -1, 'Directory'), 0, wx.CENTER|wx.RIGHT, 5)
sizer.Add(self.textctrl, 1, wx.CENTER|wx.RIGHT, 5)
sizer.Add(wx.Button(self, 100 , 'Choose'), 0, wx.CENTER|wx.RIGHT, 5)
sizer.Add(wx.Button(self, 101 , 'Parse'), 0, wx.CENTER)
self.Bind(wx.EVT_BUTTON, self.OnChoose, id=100)
self.Bind(wx.EVT_BUTTON, self.OnParse, id=101)
self.SetSizer(sizer)
def OnChoose(self, event):
"""
Action to be executed when the 'Choose' button is pressed.
"""
dialog = wx.DirDialog(self, defaultPath=self.lastDirectoryOpen)
if dialog.ShowModal() == wx.ID_OK:
self.textctrl.SetValue(dialog.GetPath())
self.lastDirectoryOpen = dialog.GetPath()
else:
wx.MessageDialog(self, 'Please choose an appropiate directory', style=wx.OK).ShowModal()
def OnParse(self, event):
"""
Action to be executed when the 'Parse' button is pressed.
"""
path = self.textctrl.GetValue()
if os.path.isdir(path):
LogParser.getInstance().parseDirectory(path)
self.mainPanel.logDataUpdated()
LogParser.deleteInstance()
else:
wx.MessageDialog(self, 'That directory does not exist', style=wx.OK).ShowModal()

View File

@ -1,184 +0,0 @@
"""
Module IncorrectSequenceList
"""
import wx
from loganalyzerengine.Connection import Connection
from loganalyzerengine.LogFile import LogFile
def advisoryString(message):
"""
Helper method
"""
if message.advisory:
return ' (ADVISORY)'
else:
return ''
class IncorrectSequenceList(wx.ListCtrl):
"""
List of the incorrect events detected after parsing the logs.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.ListCtrl.__init__(self, parent, -1,
style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_HRULES|wx.LC_VRULES)
self.incorrectSequencePanel = parent
self.datapresent = False
self.connectionForFilter = None
self.InsertColumn(0, 'Type')
self.InsertColumn(1, 'Connection id / File')
self.InsertColumn(2, 'Message id (prod id | prod seq id)')
self.InsertColumn(3, 'ntimes')
self.InsertColumn(4, 'timestamps')
self.SetColumnWidth(0, 150)
self.SetColumnWidth(1, 250)
self.SetColumnWidth(2, 300)
self.SetColumnWidth(3, 100)
self.SetColumnWidth(4, 300)
def logDataUpdated(self):
"""
This method must be called to notify the list that the parsed data
has changed.
"""
self.datapresent = True
self.updateList()
def checkConnectionForFilter(self):
"""
Returns True if the connection that was inputed by the user
to filter the events is valid.
self.connectionForFilter is a (string, boolean) tuple.
The boolean value is True if the string is a shortId, and False if it is a long id.
"""
if self.connectionForFilter is None:
return False
if self.connectionForFilter[1]:
#shortId
return self.connectionForFilter[0].isdigit() and \
int(self.connectionForFilter[0]) > -1 and \
int(self.connectionForFilter[0]) < len(Connection.connectionIdList)
else:
#longId
return self.connectionForFilter[0] in Connection.connections
def updateList(self):
"""
Updates the display of the list of incorrect events
"""
self.DeleteAllItems()
if self.datapresent:
options = self.incorrectSequencePanel.options
row = 0
# we construct a list of connection long ids to be displayed,
# depending on the filter desired
if self.checkConnectionForFilter():
if self.connectionForFilter[1]:
#shortId
connectionIds = [Connection.connectionIdList[int(self.connectionForFilter[0])]]
else:
connectionIds = [self.connectionForFilter[0]]
else:
if self.connectionForFilter is None or self.connectionForFilter[0] == '':
connectionIds = Connection.connections.keys()
else:
connectionIds = []
# we display the problems tied to connections
showShortIDs = options['showShortIds']
for longId in connectionIds:
# we display long or short ids depending on the option chosen
connection = Connection.getConnectionByLongId(longId)
errors = connection.getErrors()
if showShortIDs:
printedConnectionId = connection.shortId
else:
printedConnectionId = longId
# sent but not received messages
if options['sentButNotReceived']:
for storedMessage, n, timestamps in errors[0]:
message = storedMessage[0]
self.insertRow(row, 'sentButNotReceived' + advisoryString(message), printedConnectionId,
message.producer.shortId if showShortIDs else message.producer.longId,
message.prodSeqId, n, timestamps, wx.WHITE)
row += 1
# received but not sent messages
if options['receivedButNotSent']:
for storedMessage, n, timestamps in errors[1]:
message = storedMessage[0]
self.insertRow(row, 'receivedButNotSent' + advisoryString(message), printedConnectionId,
message.producer.shortId if showShortIDs else message.producer.longId,
message.prodSeqId, n, timestamps, wx.WHITE)
row += 1
# duplicate sent or received messages through a connection
if options['duplicateInConnection']:
for storedMessage, n, timestamps in errors[2]:
message = storedMessage[0]
self.insertRow(row, 'duplicateSentInConnection' + advisoryString(message), printedConnectionId,
message.producer.shortId if showShortIDs else message.producer.longId,
message.prodSeqId, n, timestamps, wx.WHITE)
row += 1
for storedMessage, n, timestamps in errors[3]:
message = storedMessage[0]
self.insertRow(row, 'duplicateReceivedInConnection' + advisoryString(message), printedConnectionId,
message.producer.shortId if showShortIDs else message.producer.longId,
message.prodSeqId, n, timestamps, wx.WHITE)
row += 1
# duplicate sent or received messages in the same log file.
# right now they are only shown when the connection filter is not used.
if options['duplicateInFile'] and not self.checkConnectionForFilter() and \
(self.connectionForFilter is None or self.connectionForFilter[0] == ''):
for logfile in LogFile.logfiles:
errors = logfile.getErrors()
for message, n, timestamps in errors[0]:
self.insertRow(row, 'duplicateSentInFile' + advisoryString(message), str(logfile),
message.producer.shortId if showShortIDs else message.producer.longId,
message.prodSeqId, n, timestamps, wx.WHITE)
row += 1
for message, n, timestamps in errors[1]:
self.insertRow(row, 'duplicateReceivedInFile' + advisoryString(message), str(logfile),
message.producer.shortId if showShortIDs else message.producer.longId,
message.prodSeqId, n, timestamps, wx.WHITE)
row += 1
def insertRow(self, rownumber, typeOfError, connectionId, producerId, producerSequenceId, n, timestamps, col):
"""
Helper method to insert a row into the list
"""
self.InsertStringItem(rownumber, typeOfError)
self.SetStringItem(rownumber, 1, str(connectionId))
self.SetStringItem(rownumber, 2, str(producerId) + ' | ' + str(producerSequenceId))
self.SetStringItem(rownumber, 3, str(n))
self.SetStringItem(rownumber, 4, ' | '.join(timestamps))
self.SetItemBackgroundColour(rownumber, col)

View File

@ -1,135 +0,0 @@
"""
Module IncorrectSequencePanel
"""
import wx
from IncorrectSequenceList import IncorrectSequenceList
class IncorrectSequencePanel(wx.Panel):
"""
This panel contains a list of incorrect events dectected by the parsing,
and many controls to filter which events appear.
Also the user can change if long ids (original ActiveMQConnection id strings, long)
or short ids (a different integer for each connection) is desired.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
self.parent = parent
self.options = {}
self.incorrectSequenceList = IncorrectSequenceList(self)
self.showShortIds = wx.CheckBox(self, 100, 'Show results with short ids')
self.sentButNotReceived = wx.CheckBox(self, 101, 'Sent but not received')
self.receivedButNotSent = wx.CheckBox(self, 102, 'Received but not sent')
self.duplicateInConnection = wx.CheckBox(self, 103, 'Duplicate in connection')
self.duplicateInFile = wx.CheckBox(self, 104, 'Duplicate in log file')
self.connectionText = wx.TextCtrl(self, -1, '')
self.rbshortId = wx.RadioButton(self, -1, style=wx.RB_GROUP, label="Short id")
self.rblongId = wx.RadioButton(self, -1, label="Long id")
self.showShortIds.SetValue(True)
self.sentButNotReceived.SetValue(True)
self.receivedButNotSent.SetValue(True)
self.duplicateInConnection.SetValue(True)
self.duplicateInFile.SetValue(True)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer2 = wx.GridSizer()
sizer2 = wx.GridSizer(2, 2, 5, 5)
sizer2.AddMany([self.sentButNotReceived,
self.receivedButNotSent,
self.duplicateInConnection,
self.duplicateInFile
])
sizer3 = wx.BoxSizer(wx.HORIZONTAL)
sizerrb = wx.BoxSizer(wx.VERTICAL)
sizerrb.Add(self.rbshortId, 0, wx.DOWN, 5)
sizerrb.Add(self.rblongId, 0)
sizer3.Add(wx.StaticText(self, -1, 'Filter by connection\n(leave blank to view all)'), 0, wx.CENTER|wx.RIGHT, 5)
sizer3.Add(self.connectionText, 1, wx.CENTER|wx.RIGHT, 5)
sizer3.Add(sizerrb, 0, wx.CENTER|wx.RIGHT, 5)
sizer3.Add(wx.Button(self, 105, 'Filter'), 0, wx.CENTER)
sizer4 = wx.BoxSizer(wx.HORIZONTAL)
sizer4.Add(sizer2, 0, wx.CENTER)
sizer4.Add(sizer3, 1, wx.EXPAND|wx.CENTER|wx.LEFT, 20)
sizer.Add(sizer4, 0, wx.EXPAND|wx.ALL, 5)
sizer5 = wx.BoxSizer(wx.HORIZONTAL)
sizer5.Add(self.showShortIds, 0, wx.RIGHT|wx.CENTER, 5)
sizer5.Add(wx.Button(self, 106, 'Jump to Message Browsing'), 0, wx.CENTER)
sizer.Add(sizer5, 0, wx.ALL, 5)
sizer.Add(self.incorrectSequenceList, 1, wx.EXPAND)
self.Bind(wx.EVT_CHECKBOX, self.OptionsChanged, id=100)
self.Bind(wx.EVT_CHECKBOX, self.OptionsChanged, id=101)
self.Bind(wx.EVT_CHECKBOX, self.OptionsChanged, id=102)
self.Bind(wx.EVT_CHECKBOX, self.OptionsChanged, id=103)
self.Bind(wx.EVT_CHECKBOX, self.OptionsChanged, id=104)
self.Bind(wx.EVT_BUTTON, self.OnFilter, id=105)
self.Bind(wx.EVT_BUTTON, self.OnJump, id=106)
self.parseOptions()
self.SetSizer(sizer)
def logDataUpdated(self):
"""
This method must be called to notify the panel that the parsed data has been updated.
It will in turn notify the list.
"""
self.incorrectSequenceList.logDataUpdated()
def parseOptions(self):
"""
Stores the values of the various checkboxes into self.options.
"""
self.options['showShortIds'] = self.showShortIds.IsChecked()
self.options['sentButNotReceived'] = self.sentButNotReceived.IsChecked()
self.options['receivedButNotSent'] = self.receivedButNotSent.IsChecked()
self.options['duplicateInConnection'] = self.duplicateInConnection.IsChecked()
self.options['duplicateInFile'] = self.duplicateInFile.IsChecked()
def OptionsChanged(self, event):
"""
Action to be executed every time one of the checkboxes is clicked.
It calls parseOptions() and then updates the display of incorrect events.
"""
self.parseOptions()
self.incorrectSequenceList.updateList()
def OnFilter(self, event):
"""
Action to be executed every time the button 'filter' is pressed.
"""
self.incorrectSequenceList.connectionForFilter = (self.connectionText.GetValue(), self.rbshortId.GetValue())
self.OptionsChanged(event)
def OnJump(self, event):
"""
Action to be executed when the 'jump' button is pressed.
"""
if self.incorrectSequenceList.GetFirstSelected() != -1:
connectionId, messageId = self.incorrectSequenceList.GetItem(self.incorrectSequenceList.GetFirstSelected(), 2).GetText().split(' | ')
self.parent.GetParent().browsingMessagesPanel.textctrl1.SetValue(connectionId)
self.parent.GetParent().browsingMessagesPanel.textctrl2.SetValue(messageId)
self.parent.GetParent().browsingMessagesPanel.rbshortId.SetValue(self.showShortIds.GetValue())
self.parent.GetParent().browsingMessagesPanel.rblongId.SetValue(not self.showShortIds.GetValue())
self.parent.GetParent().browsingMessagesPanel.displayMessageInfo(event)
self.parent.SetSelection(1)

View File

@ -1,66 +0,0 @@
"""
Module MessageTravelPanel
"""
import wx
from MessageTravelText import MessageTravelText
class MessageTravelPanel(wx.Panel):
"""
The function of this panel is to show the travel history of a message.
This means the connections that the message went through.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
self.rbshortId = wx.RadioButton(self, -1, style=wx.RB_GROUP, label="Short id")
self.rblongId = wx.RadioButton(self, -1, label="Long id")
self.textctrl1 = wx.TextCtrl(self, -1, '')
self.textctrl2 = wx.TextCtrl(self, -1, '')
self.chkshowshortId = wx.CheckBox(self, 100, 'Show result with short ids')
self.messageTravelPanel = MessageTravelText(self)
self.chkshowshortId.SetValue(True)
sizerrb = wx.BoxSizer(wx.VERTICAL)
sizerrb.Add(self.rbshortId, 0, wx.DOWN, 5)
sizerrb.Add(self.rblongId, 0)
sizerinput = wx.BoxSizer(wx.HORIZONTAL)
sizerinput.Add(sizerrb, 0, wx.RIGHT, 5)
sizerinput.Add(wx.StaticText(self, -1, 'Producer id'), 0, wx.CENTER|wx.RIGHT, 5)
sizerinput.Add(self.textctrl1, 2, wx.CENTER|wx.RIGHT, 5)
sizerinput.Add(wx.StaticText(self, -1, 'Producer Sequence id'), 0, wx.CENTER|wx.RIGHT, 5)
sizerinput.Add(self.textctrl2, 1, wx.CENTER|wx.RIGHT, 5)
sizerinput.Add(wx.Button(self, 101 , 'Browse'), 0, wx.CENTER)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(sizerinput, 0, wx.EXPAND|wx.ALL, 5)
sizer.Add(self.chkshowshortId, 0, wx.LEFT|wx.UP, 5)
sizer.Add(self.messageTravelPanel, 1, wx.EXPAND|wx.ALL, 5)
self.Bind(wx.EVT_CHECKBOX, self.displayMessageInfo, id=100)
self.Bind(wx.EVT_BUTTON, self.displayMessageInfo, id=101)
self.SetSizer(sizer)
def displayMessageInfo(self, event):
"""
Action to be executed when the 'Browse' button is pushed.
"""
self.messageTravelPanel.displayMessageInfo(self.textctrl1.GetValue(),
self.textctrl2.GetValue(),
self.rbshortId.GetValue())
def logDataUpdated(self):
"""
This method must be called to notify the panel that the parsed data has been updated.
It will in turn notify the message travel panel.
"""
self.messageTravelPanel.logDataUpdated()

View File

@ -1,158 +0,0 @@
"""
Module MessageTravelText
"""
import wx
from loganalyzerengine.Producer import Producer
from loganalyzerengine.Message import Message
class MessageTravelText(wx.TextCtrl):
"""
Text box where the travel path of a message is displayed.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.TextCtrl.__init__(self, parent, -1, style=wx.TE_MULTILINE)#, style=wx.TE_CENTRE)
self.parent = parent
self.datapresent = False
self.SetEditable(False)
def logDataUpdated(self):
"""
Informs the text control that there is some parsed data.
"""
self.datapresent = True
def displayMessageInfo(self, producerId, prodSeqId, isShortId):
"""
Displays the travel information of a message as text.
connectionId must be a shortId if isShortId == True, and a longId if isShortId == False
"""
if self.datapresent:
# we run some checks on the connection id and command id,
# and transform connectionId into a shortId
if isShortId:
if not producerId.isdigit():
wx.MessageDialog(self, 'That short producer id is not an integer', style=wx.OK).ShowModal()
return
producerId = int(producerId)
if producerId < 0 or producerId > Producer.nProducers - 1:
wx.MessageDialog(self, 'That short producer id does not exist', style=wx.OK).ShowModal()
return
else:
if Producer.exists(producerId):
producerId = Producer.longIdToShortId(producerId)
else:
wx.MessageDialog(self, 'That connection id does not exist', style=wx.OK).ShowModal()
return
if not prodSeqId.isdigit():
wx.MessageDialog(self, 'That command id is not an integer', style=wx.OK).ShowModal()
return
# we ensure the shortId and the commandId are integers
producerId = int(producerId)
prodSeqId = int(prodSeqId)
# we check that the message exists
if Message.exists(Producer.getProducerByShortId(producerId), prodSeqId):
message = Message.getMessage(Producer.getProducerByShortId(producerId), prodSeqId)
sendingFiles, receivingFiles = message.getFiles()
printShortIds = self.parent.chkshowshortId.GetValue()
# we set the value of the text field
self.SetValue(
"\n".join(['Message Id:',
'\tProducer Id: ' + str(producerId if printShortIds else Producer.shortIdToLongId(producerId)),
'\tProducer Sequence id: ' + str(prodSeqId),
'ADVISORY' if message.advisory else '(not advisory)',
'Connections that sent this message:',
#one line for every connection that sent a message
"\n".join([''.join([
'\t',
# if direction == True, message went from connection.fromFile to connection.toFile
str(connection.shortId if printShortIds else connection.longId),
''.join([
', from ',
str(connection.fromFile)
if direction else
str(connection.toFile)
,
' to ',
str(connection.toFile)
if direction else
str(connection.fromFile),
', ',
' | '.join([''.join([
'ConID: ',
str(connection.shortId if printShortIds else connection.longId),
', CommandID: ',
str(commandid),
', ',
timestamp
])
for (connection, commandid, timestamp) in values
])
])
])
for (connection, direction), values in message.sendingConnections.iteritems()
]),
'Connections that received this message:',
#one line for every connection that received a message
"\n".join([''.join([
'\t',
# if direction == True, message went from connection.fromFile to connection.toFile
str(connection.shortId if printShortIds else connection.longId),
''.join([
', from ',
str(connection.fromFile)
if direction else
str(connection.toFile)
,
' to ',
str(connection.toFile)
if direction else
str(connection.fromFile)
,
', ',
' | '.join([''.join([
'ConID: ',
str(connection.shortId if printShortIds else connection.longId),
', CommandID: ',
str(commandid),
', ',
timestamp
])
for (connection, commandid, timestamp) in values
])
])
])
for (connection, direction), values in message.receivingConnections.iteritems()
]),
'Log files where this message was sent:',
'\t' + ", ".join([str(f) for f in sendingFiles]),
'Log files where this message was received:',
'\t' + ", ".join([str(f) for f in receivingFiles])
])
)
else:
# the message doesn't exist
wx.MessageDialog(self, 'That message does not exist', style=wx.OK).ShowModal()
return
else:
# there is no data present
wx.MessageDialog(self, 'Please parse some files first', style=wx.OK).ShowModal()
return

View File

@ -1,56 +0,0 @@
"""
Module TabbedPanel
"""
import wx
from IncorrectSequencePanel import IncorrectSequencePanel
from MessageTravelPanel import MessageTravelPanel
from ViewClientsPanel import ViewClientsPanel
from ViewConnectionsPanel import ViewConnectionsPanel
from ViewFilesPanel import ViewFilesPanel
class TabbedPanel(wx.Panel):
"""
Panel with the tabs that will display the information once the log files are parsed.
It contains 4 tabs, via a wx.Notebook object:
-IncorrectSequencePanel.
-BrowsingMessagesPanel.
-ViewConnectionsPanel.
-ViewFilesPanel.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
notebook = wx.Notebook(self, -1)
self.incorrectSequencePanel = IncorrectSequencePanel(notebook)
self.browsingMessagesPanel = MessageTravelPanel(notebook)
self.viewClientsPanel = ViewClientsPanel(notebook)
self.viewConnectionsPanel = ViewConnectionsPanel(notebook)
self.viewFilesPanel = ViewFilesPanel(notebook)
notebook.AddPage(self.incorrectSequencePanel, 'Incorrect Sequences')
notebook.AddPage(self.browsingMessagesPanel, 'Message Browsing')
notebook.AddPage(self.viewClientsPanel, 'View clients')
notebook.AddPage(self.viewConnectionsPanel, 'View connections')
notebook.AddPage(self.viewFilesPanel, 'View files')
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(notebook, 1, wx.EXPAND|wx.ALL, 5)
self.SetSizer(sizer)
def logDataUpdated(self):
"""
When this panel is notified that the parsed data has changed,
it notifies the 4 sub panels.
"""
self.incorrectSequencePanel.logDataUpdated()
self.browsingMessagesPanel.logDataUpdated()
self.viewClientsPanel.logDataUpdated()
self.viewConnectionsPanel.logDataUpdated()
self.viewFilesPanel.logDataUpdated()

View File

@ -1,100 +0,0 @@
"""
Module ViewConnectionsPanel
"""
import wx
from loganalyzerengine.Connection import Connection
from loganalyzerengine.Producer import Producer
from loganalyzerengine.Consumer import Consumer
class ViewClientsPanel(wx.Panel):
"""
This panel shows the list of connections that appear in the log files.
Also, it enables the user to copy the long id of a connection to the system clipboard,
and to 'jump' to the IncorrectSequencePanel, filtering the display so that only the
events of that connection are displayed.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
self.notebook = parent
sizer = wx.BoxSizer(wx.VERTICAL)
self.producerList = wx.ListCtrl(self, -1,
style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_HRULES|wx.LC_VRULES|wx.LC_EDIT_LABELS)
self.producerList.InsertColumn(0, 'Short id')
self.producerList.InsertColumn(1, 'Short Connection id')
self.producerList.InsertColumn(2, 'Session Id')
self.producerList.InsertColumn(3, 'Value')
self.producerList.InsertColumn(4, 'Long Connection id')
self.producerList.SetColumnWidth(0, 80)
self.producerList.SetColumnWidth(1, 120)
self.producerList.SetColumnWidth(2, 80)
self.producerList.SetColumnWidth(3, 80)
self.producerList.SetColumnWidth(4, 500)
self.consumerList = wx.ListCtrl(self, -1,
style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_HRULES|wx.LC_VRULES|wx.LC_EDIT_LABELS)
self.consumerList.InsertColumn(0, 'Short id')
self.consumerList.InsertColumn(1, 'Short Connection id')
self.consumerList.InsertColumn(2, 'Session Id')
self.consumerList.InsertColumn(3, 'Value')
self.consumerList.InsertColumn(4, 'Long Connection id')
self.consumerList.SetColumnWidth(0, 80)
self.consumerList.SetColumnWidth(1, 120)
self.consumerList.SetColumnWidth(2, 80)
self.consumerList.SetColumnWidth(3, 80)
self.consumerList.SetColumnWidth(4, 500)
sizer.Add(wx.StaticText(self, -1, 'Producers'), 0, wx.CENTER|wx.LEFT|wx.TOP|wx.RIGHT, 15)
sizer.Add(self.producerList, 1, wx.EXPAND|wx.ALL, 5)
sizer.Add(wx.StaticText(self, -1, 'Consumers'), 0, wx.CENTER|wx.LEFT|wx.TOP|wx.RIGHT, 15)
sizer.Add(self.consumerList, 1, wx.EXPAND|wx.ALL, 5)
self.SetSizer(sizer)
def logDataUpdated(self):
"""
Informs this panel that new data has been parsed,
and that the list of connections should be updated.
"""
self.producerList.DeleteAllItems()
self.consumerList.DeleteAllItems()
shortId = 0
for longId in Producer.producerIdList:
producer = Producer.getProducerByLongId(longId)
self.insertRow(self.producerList, shortId, Connection.longIdToShortId(producer.connectionId),
producer.sessionId, producer.value,
Connection.getConnectionByLongId(producer.connectionId))
shortId += 1
shortId = 0
for longId in Consumer.consumerIdList:
consumer = Consumer.getConsumerByLongId(longId)
self.insertRow(self.consumerList, shortId, Connection.longIdToShortId(consumer.connectionId),
consumer.sessionId, consumer.value,
Connection.getConnectionByLongId(consumer.connectionId))
shortId += 1
def insertRow(self, targetList, shortId, shortConnectionId, sessionId, value, longConnectionId):
"""
Helper method to insert a new row in the list of connections.
"""
targetList.InsertStringItem(shortId, str(shortId))
targetList.SetStringItem(shortId, 1, str(shortConnectionId))
targetList.SetStringItem(shortId, 2, str(sessionId))
targetList.SetStringItem(shortId, 3, str(value))
targetList.SetStringItem(shortId, 4, str(longConnectionId))

View File

@ -1,103 +0,0 @@
"""
Module ViewConnectionsPanel
"""
import wx
from loganalyzerengine.Connection import Connection
class ViewConnectionsPanel(wx.Panel):
"""
This panel shows the list of connections that appear in the log files.
Also, it enables the user to copy the long id of a connection to the system clipboard,
and to 'jump' to the IncorrectSequencePanel, filtering the display so that only the
events of that connection are displayed.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
self.notebook = parent
sizer = wx.BoxSizer(wx.VERTICAL)
self.list = wx.ListCtrl(self, -1,
style=wx.LC_REPORT|wx.LC_SINGLE_SEL|wx.LC_HRULES|wx.LC_VRULES|wx.LC_EDIT_LABELS)
self.list.InsertColumn(0, 'Short id')
self.list.InsertColumn(1, 'Long id')
self.list.InsertColumn(2, 'Connection established FROM file')
self.list.InsertColumn(3, 'Connection established TO file')
self.list.InsertColumn(4, 'Producers')
self.list.InsertColumn(5, 'Consumers')
self.list.SetColumnWidth(0, 80)
self.list.SetColumnWidth(1, 250)
self.list.SetColumnWidth(2, 200)
self.list.SetColumnWidth(3, 200)
sizer2 = wx.BoxSizer(wx.HORIZONTAL)
sizer2.Add(wx.Button(self, 100, 'Copy selected long id to clipboard'), 0, wx.RIGHT, 5)
sizer2.Add(wx.Button(self, 101, "Jump to this connection's problems"))
sizer.Add(sizer2, 0, wx.ALL, 5)
sizer.Add(self.list, 1, wx.EXPAND|wx.LEFT|wx.BOTTOM|wx.RIGHT, 5)
self.Bind(wx.EVT_BUTTON, self.OnCopy, id=100)
self.Bind(wx.EVT_BUTTON, self.OnJump, id=101)
self.SetSizer(sizer)
def logDataUpdated(self):
"""
Informs this panel that new data has been parsed,
and that the list of connections should be updated.
"""
self.list.DeleteAllItems()
shortId = 0
for longId in Connection.connectionIdList:
connection = Connection.getConnectionByLongId(longId)
self.insertRow(shortId, longId, connection.fromFile, connection.toFile, connection.producers, connection.consumers)
shortId += 1
def insertRow(self, shortId, longId, fromFile, to, producers, consumers):
"""
Helper method to insert a new row in the list of connections.
"""
self.list.InsertStringItem(shortId, str(shortId))
self.list.SetStringItem(shortId, 1, str(longId))
self.list.SetStringItem(shortId, 2, str(fromFile))
self.list.SetStringItem(shortId, 3, str(to))
self.list.SetStringItem(shortId, 4, ", ".join(str(p.shortId) for p in producers))
self.list.SetStringItem(shortId, 5, ", ".join(str(c.shortId) for c in consumers))
def OnCopy(self, event):
"""
Action to be executed when pressing the 'Copy selected long id to clipboard' button.
The longId of the selected connection will be copied to the clipboard.
"""
shortId = self.list.GetFirstSelected()
if shortId != -1 and wx.TheClipboard.Open():
wx.TheClipboard.SetData(wx.TextDataObject(str(Connection.connectionIdList[shortId])))
wx.TheClipboard.Close()
def OnJump(self, event):
"""
Action to be executed when pressing the 'Jump to this connection's problems' button.
The tab with the 'IncorrectSequencePanel' will be selected and the list of incorrect
events will be automatically filtered by the selected connection.
"""
shortId = self.list.GetFirstSelected()
if shortId != -1:
incorrectSequencePanel = self.notebook.GetParent().incorrectSequencePanel
incorrectSequencePanel.connectionText.SetValue(str(shortId))
incorrectSequencePanel.rbshortId.SetValue(True)
incorrectSequencePanel.rblongId.SetValue(False)
incorrectSequencePanel.OnFilter(event)
self.notebook.SetSelection(0)

View File

@ -1,45 +0,0 @@
"""
Module ViewFilesPanel
"""
import wx
from loganalyzerengine.LogFile import LogFile
class ViewFilesPanel(wx.Panel):
"""
This panel shows the list of log files that have been read.
"""
def __init__(self, parent):
"""
Constructor
"""
wx.Panel.__init__(self, parent, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
self.text = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
sizer.Add(self.text, 1, wx.EXPAND|wx.ALL, 5)
self.SetSizer(sizer)
def logDataUpdated(self):
"""
The panel is informed that new data has been parsed,
and the list of files should be updated.
"""
self.text.SetValue(
'\n'.join(
'\n'.join([
str(file),
'\tConnections established from this file:',
'\n'.join(['\t\t' + str(con.shortId) + ' ' + str(con.longId) for con in file.outgoing]),
'\tConnections established to this file:',
'\n'.join(['\t\t' + str(con.shortId) + ' ' + str(con.longId) for con in file.incoming])
])
for file in LogFile.logfiles)
)

View File

@ -1 +0,0 @@
python Main.py

View File

@ -1 +0,0 @@
python Main.py

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB