This article is the description of what, in my humble option, J2ME logging framework should be.
It's purpose, requirements, features and how MoMELog
fits to them.
It is also an explanation of ideas that led to development of this project and the answer to the question:
"Does final J2ME application need logging facility?".
First of all a couple of words about all known features and specificities of J2ME architecture and it's differences from J2SE. It is not a secret, that J2ME is much more modest, small and a subject to a lot of restrictions and boundaries in comparison to J2SE. Only one J2ME application can be executing at one time and this application is definitely not a server or complex standalone application. It doesn't need to be administered or monitored by analyzing logging information. Users of J2ME application are definitely not interested in exploring logging, even if this application is a business application (say some mail agent or browser) and not a game. Of course, it is nice, if the application sends feedbacks to the developers in a case of an error. Besides, such feature is useful, user may be not too glad, when some application notifies him/her about it's internal error and that it wants to send some information via http, email or even sms. To my mind, users are caring much more about security of phone than a personal computer. Taking above into consideration, in my humble option, the answer to the question "Does final J2ME application need logging facility?" is "mostly not".
The reverse situation is at development time. While developing, J2ME application is mostly executed on emulators and definitely is probed on devices. Debugging (tracing) of application executed on emulator is, of course, possible but cumbersome. Debugging (tracing) of application executed on device is (almost) impossible. Here logging facility (like with server applications) is not just helpful but required. Developers just need logging facility as debugging tool.
I am not stating, that using of logging framework in final J2ME application is inadvisable.. I just want to tell, that the main aim of J2ME logging framework should be it's use in development process as debugging tool. And this is the aim of this project.
Because of restrictions and boundaries of J2ME platform, such framework should be as simple and extensible as possible. The core of such logging framework should consist of only few classes. The other classes should be the implementations that realize particular formatted method or make logging information accessible to the developer via some particular mean (e.g. show on a screen, append to a file, send via http/https as email or sms or save in RMS).
In my humble option, while debugging, there is no need to distinguishing logging events by priority.
We don't need different treatment of logging events of priorities say ERROR
, FATAL
or
WARNING
. All logging should be of priority of DEBUG
. In other words, there should be
no priority at all. All logging events must be even. Of course, it should be possible to filter logging events
based on existence of not-null
error. For example, mentioned above feature of sending feedbacks
can be realized using this.
Developers while debugging also don't need different treatment of logging events issued by different loggers. Of course, possibility to suppress logging of group of loggers or vice versa to allow logging only of group of loggers is needed. But, at development time we don't need to format logging events from one logger using one method and logging from another - some other method, or to show logging of some group of loggers on the screen and logging information from others to be appended to a file. All logging events should be formatted using one method and presented via one mean (except, that some of them can be filtered out). In other words, it should be only one formatter and one implementation to present logging information via some particular mean. The above said, makes maintaining of hierarchy of loggers needless. Disallowing and/or allowing logging of group of loggers can be realized by matching categories to some allowed or disallowed patterns.
J2ME logging framework should provide two ways to configure it and it's extensions: declarative and programmatical. Declarative should be based on character sequence supplied to the framework (e.g. file contained in application jar archive). Programmatical configuration is based on inserting some code in J2ME application. J2ME logging framework and it's extensions should provide meaningful default values for their properties to minimize needed configuration. As such logging framework is intended to be used at development time and by developers some programming work to setup some artifacts of framework (i.e. extensions) should be not considered as huge problem.
MoMELog
logging framework is very simple. It consists of three parts:
Logger
, Formatter
,
LogListener
.
Fig-1. MoMELog Framework.
Logger
is the main part of MoMELog
framework. It generates logging events
on user request and directs them to the configured LogListener
. LogListener
converts
these events to formatted strings using configured Formatter
and makes obtained strings available
to the user (e.g. shows on screen, puts in RMS, sends via http/https, appends to a file or any other way).
MoMELog
logging framework doesn't maintain the hierarchy of loggers. All loggers are even.
Loggers are distinguished by categories, that are the case-sensitive identification strings. It is possible to
suppress logging of group of loggers or allow logging of only group of loggers, by specifying
disallowed categories and allowed categories lists respectively. They are a comma separated lists
of patterns. Logger can only log it's events, if it's category matches one of the patterns in allowed categories
list and not in disallowed categories list. If any of the lists is empty, it is not considered.
It is recommended to use structural categories. This can be very helpful in formatting logging events and gives better
possibilities to suppress or allow logging of a group of loggers. It is also possible to specify group of
loggers that will generate logging event only if user request contains not-null
error.
This feature is realized by onlywitherror categories list, that is also the list of patterns.
MoMELog
framework doesn't support the notion of priority. All logging events are even.
The feature of filtering out logging events based on their properties (except not-null
error)
can be built in a particular LogListener
implementation and is not realized by the core of the framework.
Developers have only two methods to request logging of a message and/or error: Logger.log(String message)
and
Logger.log(String message, Throwable error)
.
In MoMELog
framework logging events have the following properties:
Throwable
supplied by the user.Logger
class loading time. It, of course, can be changed later.
MoMELog
framework can be configured declaratively and/or programmatically. Declarative configuration
is done by supplying initialization file .momelog.txt
(don't omit preceding dot ;-))
in the application jar archive. This is a text properties file, where lines are separated by new-line characters and
empty lines and lines that start with hash (#
) (preceding spaces are allowed) are ignored.
Framework can be also configured programmatically by using static methods of Logger
class.
Programmatical configuration of LogListener
and/or
Formatter
can be done by using instance methods of respective implementation. Declarative configuration
occurs at Logger
class loading time and programmatical one, of course, overrides it.
The core framework provides means to set the type of LogListener
used for presenting logging
to the developer, type of Formatter
used for converting logging events to strings and
allowed categories, disallowed categories and onlywitherror categories lists.
MoMELog
is preconfigured with PatternFormatter
, no LogListener
and
empty allowed categories, disallowed categories and onlywitherror categories lists.
MoMELog
is not preconfigured with some
LogListener
, because in J2ME platform there is no regular mean where to put formatted logging information.
Of course, it is possible to put logging in RMS. But, it is impossible to execute application and
analyze logging simultaneously. Not all devices support FileConection API
. Sending logging information
via http/https, email or sms can be a solution for a final applications (developers feedback), but at development time
can be cumbersome. Presenting logging information on a screen requires from developer some programming work.
In other words the choice of mean to use for presenting logging information depends on application and/or
device used and relies on developer.
Some MoMELog
extensions can require from developers programming work to be done to use them
(especially when dealing with presenting logging information on a screen). As mentioned above, this is not considered
as big problem.
The core of the MoMELog
framework consists of 5 classes.
Logger
is the main class of MoMELog
logging framework.
It is the class that developers will mostly deal with. It provides static methods for general framework configuration
and obtaining Logger
instances. It also provides instance methods to request logging.
Formatter
is an abstract base class, that every Formatter
needs
to extend. It provides three methods intended to convert logging events to strings or any other objects.
LogListener
is an interface, that every LogListener
needs to implement. It provides the only method onLog(LogEvent event)
called on every logging event.
LogEvent
is a bean, that represents information about logging event.
Configurable
is an interface, that every class interesting
to be configured from character sequence needs to implement. It contains the only method
configure(char[] lines, int offset, int length)
where actual configuration should be realized.
MoMELog
framework now consists also of three extensions
PatternFormatter
is a Formatter
that converts logging events to strings
based on conversion pattern. It's functionality and format of conversion pattern
resemble very much that of log4J
framework. It can be configured declaratively and/or programmatically.
It is preset with default pattern and doesn't require configuration.
See PatternFormatter Guide for details.
LogCanvas
is a LogListener
intended to display logging events on device's
or emulator's screen. It is fully functional Canvas
. It has very simple and convenient GUI.
Users can scroll one line or page down or up and go to the beginning or to the end of text, toggle
fullscreen
mode and reset buffer by pressing one key. Scrollbar's cursor has
different color at the edge positions (top and bottom) from that at the middle, this way, indicating first and
last line. It is fully customizable. It is possible to completely configure it's view. Key events are processed
in separate thread, started, when canvas is shown and stopped on hide.
This eliminates conflicts with callbacks processing thread. It can be configured declaratively
and/or programmatically. Like any other Displayable
it requires some programming work to be done
to make use of it. See LogCanvas Guide for details.
LogFile
is a LogListener
intended to collect formatted logging events
in the destination file. It can be used only on devices or emulators that support FileConnection API
.
It is fully configurable. Users can specify path, character encoding, header and footer lines of destination file
and prefix and suffix of each logging line. It can be configured declaratively and/or programmatically.
It provides meaningful default values for all properties and doesn't require configuration.
See LogFile Guide for details.
Usage of MoMELog
framework is similar to other logging frameworks.
Developer needs to obtain Logger
instance by invoking one of static factory methods of Logger
class
Logger.getLogger(String category)
Logger.getLogger(Class clazz)
Logger.getLogger()
The first method returns Logger
instance of the specified category.
In the second case category will be set to the name of the given class (like from Class.getName()
method).
The last convenient Logger.getLogger()
method returns Logger
instance of default category,
that is a slash (/
). I mentioned default but not root, because there is no hierarchy
of loggers and there is no root (besides, the category is the same ;-)). If somebody likes to call it root category, I have
nothing against. Although, the same Logger
instance is returned for the same category, it is recommended
to save this instance in a static variable.
Developers can use one of two methods log(String message)
or
log(String message, Throwable error)
of Logger
instance
to request logging of messages and/or errors. For example
public class SomeClass { // obtaining logger of SomeClass category. private static final Logger log = Logger.getLogger( SomeClass.class); ... public method someMethod() { // logging a message. log.log( "logging the message"); ... try { ... // logging another message. log.log( "logging another message"); } catch( Throwable err) { log.log( "logging message with error", err); ... } } ... }