mirror of https://github.com/apache/activemq.git
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:
parent
e21587fb9e
commit
19a83ae3cb
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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=";
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
class=org.apache.activemq.transport.logwriters.CustomLogWriter
|
|
@ -1 +0,0 @@
|
|||
class=org.apache.activemq.transport.logwriters.DefaultLogWriter
|
|
@ -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()
|
|
@ -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.
|
|
@ -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)])
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
|
@ -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()
|
|
@ -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
|
||||
|
||||
|
|
@ -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()
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
||||
)
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
python Main.py
|
|
@ -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 |
Loading…
Reference in New Issue