Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Tracking Events Using a Semi-Structured Repository

Tracking the activities of a system and its users is too often not considered until the application is already deployed. Learn how to demonstrate an (almost) no hassle persistent bean pool that you can use to track the activities of an already developed system for later review.

ave you ever used a multi-user system and found yourself wondering, "How did this get changed?" or, "Who created this and why?" Helping your users track the system changes and the reasons for these changes is an important part of running an efficient business. However, these questions are not easily answered by many multi-user systems.

Questions do not only come from the users of a system. Just as frequently, the developers of a system need to understand what was happening in the system at the time of an unexpected event. Developers sometimes find it difficult to determine what actually happened solely by looking at issue reports.

Having a good integrated tracking system can save both the users' time and the developers' time by answering questions quickly.

Adding event tracking to an already developed system does not require significant changes. On the contrary, as this article demonstrates, you can track system events without much up-front effort and achieve immediate benefits through the ability to browse past events and query them to answer questions.

Logging Exceptions
Every developer, at some point, has wished that more time was spent collecting the state of the system during an exception. Relying on users to take a screenshot or copy an exception message often does not yield enough reliable information, particularly when you need to resolve multiple concurrent issues. Typically, what you find for exception reporting in many Java applications is a try-catch block that looks something like this:

try { // do something here } catch (SQLException e) { logger.error(e.toString(), e); }

The logging statement might contain a message explaining what the code above was trying to do or just be e.printStackTrace();. Either way, there is often not enough information in this log entry or printout (if the printout is even readable) to determine the initial cause of the problem.

The lack of information in a stack trace is sometimes addressed by loading the exception message with additional information. The message property can only hold a limited amount of information and quickly loses the purpose of a readable message. Another solution is to add properties to an exception, such as an error code, or state information. The exception may also contain a cause or a list of exceptions. However, this information does not always make it into the log entry or print out. A common example is JDBC's batch exception. Some JDBC drivers use the SQLException's nextException property to link exceptions together, but these critical properties are not always reported.

There is a better way to preserve the exception in its entirety and easily locate it later: persisting the exception and all its properties into a local repository for later review.
Some implementations use a database to persist exception information, but this is difficult to maintain because as new exception properties or types are added, you also need to modify an independently maintained database schema. Instead, using a semi-structured repository can significantly reduce the overhead of maintaining a persistent JavaBean pool.

Rather then printing the exception to a flat file, you can persist the exception to a repository as shown below.

try { // do something here } catch (Exception e) { LogManager.log(e); } public class LogManager { private static EntityManagerFactory factory = createEntityManagerFactory("rdf"); public static void log(Throwable t) { EntityManager manager = factory.createEntityManager(); manager.merge(t); manager.close(); } }

However, you must inform the manager what information needs to be persisted in the exception and how it should be extracted. To do this with the OpenRDF Elmo library, you can create an interface that contains all the exception properties and annotate it with unique identifiers for the class and properties.

package org.example.logging.concepts; @rdf("http://example.org/rdf/logging#Throwable") public interface IThrowable { @rdf("http://example.org/rdf/logging#cause") IThrowable getCause(); void setCause(IThrowable cause); @rdf("http://example.org/rdf/logging#message") String getMessage(); void setMessage(String message); @rdf("http://example.org/rdf/logging#stackTrace") List getStackTraceElements(); void setStackTraceElements(List list); @rdf("http://example.org/rdf/logging#date") Date getDate(); void setDate(Date date); }

Then you need to provide a method to copy the properties from the Exception into the managed interface.

package org.example.logging.support; public abstract class ThrowableMerger implements IThrowable, Mergeable, Entity { public void merge(Object source) { if (source instanceof Throwable) { Throwable t = (Throwable) source; setMessage(t.getMessage()); ElmoManager manager = getElmoManager(); StackTraceElement[] stack = t.getStackTrace(); setStackTraceElements((List) manager.merge(Arrays.asList(stack))); setCause((IThrowable) manager.merge(t.getCause())); setDate(new Date()); } } }

You also need to do the same to the child classes, if applicable (see Listing 1). For the code in Listing 1 to work correctly you need to include a persistence.xml file in the class-path like the one in Listing 2.

You will also need to include the file org.openrdf.elmo.roles in the classpath to the registered merger classes and the interfaces.

  • # META-INF/org.openrdf.elmo.roles
  • org.example.concepts.IThrowable
  • org.example.concepts.IStackTraceElement
  • org.example.concepts.ISQLException
  • org.example.support.ThrowableMerger
  • org.example.support.StackTraceElementMerger
  • org.example.support.SQLExceptionMerger
  • org.example.exceptions.MyException
  • java.lang.Throwable = http://example.org/rdf/logging#Throwable
  • java.lang.StackTraceElement = http://example.org/rdf/logging#StackTraceElement
  • java.sql.SQLException = http://example.org/rdf/logging#SQLException

You can include other properties of other classes in a similar way. For classes that you can modify, you can add annotations onto the getter methods instead of creating a merger class. If you are not interested in reading the persisted repository from Java (just using the browser and scripting languages) you can also forgo the interface and place all the annotations on the exception class.

package org.example.exceptions; @rdf("http://example.org/rdf/logging#MyException") public class MyException extends Exception { private int code; public MyException(int code, String message) { super(message); this.code = code; } @rdf("http://example.org/rdf/logging#code") public int getCode() { return code; } }

Now that your exceptions are being persisted in a repository, take a look at some of the tools available to inspect them.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date