Introducing React ReExt – Sencha Ext JS Components in React! LEARN MORE

Getting More out of Logging with GXT

June 3, 2013 125 Views
Show

Introduction

Logging with GXTLogging can be an invaluable tool in the application debugging tool chest when it isn’t enough to have a great debugger and good test coverage. As applications grow more complex, logging can be quite handy, so you can see what other interactions might be occurring due to seemingly unrelated logic and events. Logging can also be useful for other processes, such as getting feedback about possible bugs while still writing code, measuring performance of various parts of the application, and tracking how the application is used.

When large atom smashers spit out loads of data, they use a hypothesis to drill down and filter the data. The same goes for logging: unless a hypothesis is developed while coding source, the logging is going to look like noise. Having this goal in mind while writing your application will enable you to produce logging details that will be helpful later when you want to filter through the output generated by your application.

Logging in GWT

There are three main logging mechanisms built into GWT, each with a slightly different purpose and use case:

  • GWT.log is for Development Mode only. When invoked, it adds a message to the Development Mode console. There are two methods that can be called, log(String) and log(String, Exception). The former writes out simple text as an info statement, the latter as an error, and it draws the full stack trace of the exception when the message is selected. This is being used less now that Super Dev Mode is beginning to be adopted, since these messages will not show up.
  • The Lightweight Logging system is meant to be a low-level, web-mode specific mechanism to track details like application startup, simple performance figures, and can be enabled or disabled without recompiling the application. This means that these log statements never compile out, so they should be used sparingly.
  • The Java Logging facilities are partially emulated in GWT, allowing access to most of the same calls that you generally have in non-GWT Java code. This can be used in dev or web mode, and it has a number of configuration options within your module so you can decide where that logging should go. This article will focus on this tool.

java.util.logging

The java.util.logging tools behave the same on the client and server—instances can be retrieved from a call to Logger.getLogger(String), which should be given the name of the current class name, and typically are kept in a private static final field to avoid getting or making new loggers each time they are needed.

If your project includes GXT, logging is already present but completely disabled. Logging is enabled in the com.sencha.gxt.core.Core module—this line adds the necessary classes to the project, but forces them all to completely compile out:


To enable logging add this definition and set the value flag to true. The possible values are TRUE or FALSE—with the LoggingDisabled inherits statement above (or GXT in your app), here is how you enable logging:


There are seven levels of logging available: severe, warning, info, config, fine, finer, finest. The higher in that list, the more important the message is —most logging configurations display the top two, and the rest are often left out except in specific situations.


Next, there are logging handlers, the mechanisms that GWT uses to display or persist the logged messages. Typically on the server, you would write the log messages to one or more files, but on the client we don’t have that capability, so we need to be more selective in how we get the log messages. These fall into several categories, and can individually be ENABLED or DISABLED:

  • Dev mode specific – these only work when in dev mode, as they use either GWT.log or the Java console to emit their messages:

  • Browser console – Firebug, Chrome’s Inspector, IE’s Developer Toolbar, etc. These show the messages in the browser in either web or dev mode, but are not visible unless the user opens one of these consoles directly.

  • Browser popups – the idea can be useful to enable users to see what is in the log, though the default implementation always renders below most GWT and GXT widgets, so isn’t often very useful; more on this at the end.
  • And finally, the server – we can send messages to the server, and ask it to log them as it normally would for later review. This option enables you to save logs to a file, though remember that all users will be logging together to the same file. This is the only option that is disabled by default, as it requires a properly configured server to receive those logs, using the com.google.gwt.logging.shared.RemoteLoggingService RPC interface.

Sample Application

This is a sample that uses most of the logging defaults. All the browser and dev mode consoles are used and remote logging is left disabled, and the popup is disabled since it won’t work well with GXT. The log level is set to FINEST to report all possible messages, and additionally GXT itself is told to emit all internal details.
















Below is a simple logger example. First the logger is instantiated and later logger.log(…) is called to produce logging output according to the options selected in the application module.

// Example using logging
public class TestLogging implements EntryPoint {
// instantiates a logger
private static final Logger logger = Logger.getLogger(TestLogging.class.getName());

@Override
public void onModuleLoad() {
logger.log(Level.INFO, “Phase A: So far so good.”);
logger.log(Level.WARNING, “Did this forget something?”);

TextField field = new TextField();
String value;
try {
value = field.getValueOrThrow();
} catch (ParseException e) {
logger.log(Level.SEVERE, “Field Error”, e);
}

if (logger.isLoggable(Level.INFO)) {
// Anything in this statement will not get compiled when logging is off.
logger.warning(“Do something extra while logging.”);
}

if (GXTLogConfiguration.loggingIsEnabled()) {
// Anything in this statement will not get compiled when logging is off.
logger.info(“Do something GXT logging is on.”);
}
}
}

More Configuration

By disabling or enabling the logging feature entirely, you are telling the compiler to either leave all logging statements in or remove them entirely. The log level is less specific; even if you set the log level to WARN, just as in non-GWT Java, some code might change it back to FINEST by calling Logger.getLogger(“”).setLevel(Level.FINEST). This further enables you to set different log levels for different parts of your application. In the same way, you can specify handlers for particular loggers, setting one log level for internal code and another for third-party code, etc. But once logging has been disabled, that code should compile out entirely.

Building Your Own Log Handler

Building your own handler is simply a matter of implementing java.util.logging.Handler and doing something with the data. Here is an example in GXT that generates a Grid that could be added to part of the application to show all log messages. Note that until you add the handler to a logger (and in this case, probably the root logger), it will not start recording logged messages.

public class GridHandler extends Handler implements IsWidget {
public interface LogRecordProperties extends PropertyAccess {
@Path(“millis”)
ModelKeyProvider key();

ValueProvider message();
ValueProvider loggerName();
ValueProvider millis();
ValueProvider level();
}
private static final LogRecordProperties properties = GWT.create(LogRecordProperties.class);
protected final ListStore store = new ListStore(properties.key());
private Grid grid;

public GridHandler() {
grid = getLogRecordGrid(getLogRecordColumnModel(getColumnConfigs()));
}
@Override
public void publish(LogRecord logRecord) {
store.add(logRecord);
}

//these methods do nothing, since we don’t need to think about files
@Override
public void flush() {
}

@Override
public void close() {
}

@Override
public Widget asWidget() {
return grid;
}

private Grid getLogRecordGrid(ColumnModel columnModel) {
Grid grid = new Grid(store, columnModel);
grid.getView().setForceFit(true);
return grid;
}
private ColumnModel getLogRecordColumnModel(List> columns) {
return new ColumnModel(columns);
}
protected List> getColumnConfigs() {
List> columns = new ArrayList>();
columns.add(new ColumnConfig(properties.loggerName(), 10, “Logger”));
columns.add(new ColumnConfig(properties.level(), 10, “Level”));
columns.add(new ColumnConfig(properties.message(), 10, “Message”));
columns.add(new ColumnConfig(new LongToDateValueProvider(properties.millis()), 10, “Timestamp”));
return columns;
}

public void clear() {
store.clear();
}
public static class LongToDateValueProvider implements ValueProvider {
private final ValueProvider base;
public LongToDateValueProvider(ValueProvider base) {
this.base = base;
}
@Override
public String getPath() {
return base.getPath();
}
@Override
public Date getValue(M object) {
return new Date(base.getValue(object));
}
@Override
public void setValue(M object, Date value) {
base.setValue(object, value.getTime());
}
}
}

Conclusion

Logging adds additional process output information which can provide invaluable process information during development, after development and while debugging. It can be completely compiled out, or added back to assist in debugging in production, and can be customized for your particular application to render exactly the details you need.

Other References

GXT