The document provides an overview of the PNotifyAppender, which is an appender that sends log messages as instant messages using GTalk. It stores log events in an internal cyclic buffer and sends them as an IM when a triggering condition is met, such as an error-level event. It uses the Smack API to connect to GTalk and relies on a username, password, recipient address, and other properties to operate. The appender aims to provide real-time notification of exceptions to system administrators via an IM client.
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Rein_in_the_ability_of_log4j
1. Instant Messenger logging:
Rein-
Rein-in the ability of log4j with
abi
Gtalk
(PNotifyAppender)
NotifyAppender)
BY
SAURAV PAUL
1|Page
2. TABLE OF CONTENTS
SL. NO TOPICS PAGE NO.
1. Introduction 3
2. Revisit-log4j framework in brief 3-7
3. Case study: real-world example appender 7-8
4. Overview of PNotifyAppender 9-11
5. Putting it all together 11-13
6. Conclusion 13
7. Resources 13
Note:
1. For Technical details – refer page no. 3-11.
2. Guide to quick start – refer page no. 11-13.
2|Page
3. 1. Introduction:
No matter how many well-designed test cases you write, even the smallest application will hide one or more bugs
once it’s deployed in the production environment. While test-driven development and QA practices improve
code quality and provide confidence in the application, when a system fails, dev elopers and system
administrators need contextual information about its execution. With the appropriate information, they can
identify the nature of the problem and fix it quickly, saving time and money.
Logging application events is a central part of many applications. Most of our applications do some sort of
logging, using a variety of mechanisms. When things go wrong, the first course of action is usually to get hold of
the application log file and go through its contents.
Someone supporting live, deployed applications would probably appreciate the importance of getting notified of
application errors as early as possible. It is much better to be proactive dealing with errors, rather than waiting to
hear from the customer that something seems to have gone wrong.
So, how about getting notified immediately, by utilizing an instant messenger client like Gtalk
Messenger?
2. Revisit-log4j framework in brief:
Revisit
it- brief:
The log4j framework is the de facto logging framework written in the Java language. As part of the Jakarta
project, it is distributed under the Apache Software License, a popular open source license certified by the
Open Source Initiative (OSI). The log4j environment is fully configurable programmatically or through
configuration files, either in properties or XML format. In addition, it allows dev elopers to filter out logging
requests selectively without modifying the source code.
The log4j environment has three main components:
Loggers: Control which logging statements are enabled or disabled. Loggers may be assigned the levels ALL
DEBUG, INFO, WARN, ERROR, FATAL, or OFF. To make a logging request, you invoke one of the printing
methods of Logger instance.
Layout: Format the logging request according to the user's wishes.
Appenders: Send formatted output to its destinations.
2.1 Under the hood:
All appenders must extend the org.apache.log4j.AppenderSkeleton class, an abstract class that implements the
org.apache.log4j.Appender and org.apache.log4j.spi.OptionHandler interfaces. The UML class diagram of the
AppenderSkeleton:
3|Page
4. Let's examine the methods of the Appender interface implemented by the AppenderSkeleton class. As Listing
above indicates, nearly all methods in the Appender interface are setter and getter methods:
Appender Interface:
package org.apache.log4j;
public interface Appender
{ void addFilter (Filter newFilter);
public Filter getFilter ();
public void close ();
public void clearFilters ();
public void close ();
public String getName ();
public void setErrorHandler (ErrorHandler errorHandler);
public ErrorHandler setErrorHandler ();
public void set Layout (Layout layout);
public Layout getLayout ();
public void setName (String name);
public boolean requiresLayout () ;}
These methods handle appender properties like the following:
Name: Appenders are named entities so there is a setter/getter for its name.
4|Page
5. Layout: Appenders may have a Layout associated with them, so there is another setter/getter method for the lay
out.
Note as said it may but not must. The reason is that some appenders don't require a lay out. Layout manages
format output -- that is, it returns a String representation of the LoggingEvent. On the other hand, JMSAppender
sends the event serialized, so you're not required to attach a layout with it. If your custom appender doesn't
require a layout, the requiresLayout () method must return false to prevent log4j from complaining about
missing layout information.
ErrorHandler: Another setter/getter method exists for ErrorHandler. Appenders may delegate their error
handling to an ErrorHandler object -- an interface in the org.apache.log4j.spi package. There are two
implementing classes: OnlyOnceErrorHandler and FallbackErrorHandler. The OnlyOnceErrorHandler
implements log4j's default error handling policy, which consists of emitting a message for the first error in an
appender and ignoring all following errors. The error message is printed on System.err. FallbackErrorHandler
implements the ErrorHandler interface such that a secondary appender may be specified. This secondary
appender takes over if the primary appender fails. The error message is printed on System.err, then logged in
new secondary appender.
There are other methods to manage filters (such as the addFilter (), clearFilters (), and getFilter () methods).
Even though log4j has several built-in ways to filter log requests (such as repository -wide level, logger level, and
appender threshold), it is also very powerful in its approach to using custom filters.
An appender can contain multiple filters. Custom filters must extend the org.apache.log4j.spi.Filter abstract
class. This abstract class requires filters to be organized in a linear chain. The decide (LoggingEvent) method,
org.apache.log4j.spi.Filter, an abstract Class, of each filter is called sequentially, in the order it was added to the
chain. Custom filters are based on ternary logic. The decide () method must return one of the DENY,
NEUTRAL, or ACCEPT integer constants.
Besides setter/getter methods and the ones related with filters, there are two other methods: close () and
doAppend (). The close () method releases any resources allocated within the appender, such as file handlers,
network connections, and so on. When coding your custom appender, be sure you implement this method so
that when your appender is closed, its closed field is set to true.
The doAppend () method, follows the Gang of Four (GOF) Template Method design pattern
This method provides a skeleton of an algorithm, deferring some steps to subclasses:
public synchronized void doAppend (LoggingEvent event) {
if (closed) { // s t e p 1
LogLog.error("Attempted to append to closed appender [" + name + "].");
return;
}
if (! isAsSevereAsThreshold (event.level)) { // s t e p 2
return;
}
Filter f = this.headFilter; // s t e p 3
FILTER_LOOP:
while (f! = null) {
switch (f .decide (event)) {
case Filter. DENY: return;
case Filter.ACCEPT: break FILTER_LOOP;
case Filter.NEUTRAL: f = f.next;
5|Page
6. }
}
this. append (e v e n t); // s t e p 4
}
2.1.1 The algorithm:
1. Checks whether the appender is closed.
2. Checks whether the logging event is below the threshold of the appender.
3. Checks whether filters attached to the appender, if any, deny the request.
4. Invokes append () method of the appender. This step is delegated to each subclass.
The methods and properties that AppenderSkeleton inherits from Appender. Let's see why AppenderSkeleton
implements the OptionHandler interface. OptionHandler contains only one method: activateOptions (). This
method is invoked by a configurator class after calling setter methods for properties. Some properties depend on
each other so they cannot be activated until all of them have been loaded, such as in the activateOptions ()
method. This method is a mechanism for developers to perform whatever tasks were necessary before the
appender became activated and ready.
In addition to all the methods mentioned, look again at Class diagram above. Notice that AppenderSkeleton
provides a new abstract method (append () method) and a new JavaBeans property (threshold). The threshold
property is used to filter logging requests by the appender, with only requests over the threshold handled. We
mentioned append () method before when we talked about the doAppend () method. It is an abstract method
that your custom appender must implement because the framework calls it within the doAppend () method. The
append () method is one of the hooks of the framework.
Now that we’ve seen all the methods available in the AppenderSkeleton class, let's see what's happening behind
the scenes. Below figure illustrates the life cycle of an appender object inside log4j:
6|Page
7. 2.1.2 Let’s take a look in brief what is going on above diagram:
The appender instance does not exist: Perhaps the framework has not been yet configured.
The framework instantiates a new appender: This happens when the configurator classes parse an appender
declaration in configuration scripts. The configurator classes call Class.newInstance
YourCustomAppender.class), which is the dynamic equivalent of calling new YourCustomAppender (). The
framework does this so that the framework is not hard-coded to any specific appender name; the framework is
generic and works with any appender.
The framework determines whether the appender requires a layout: If the appender does not require a lay
out, then configurators don't try to load lay out information from configuration scripts.
Log4j configurator calls setter methods: The framework transparently handles appender's properties
following JavaBeans naming conventions.
Configurator invokes the activateOptions () method: After all the properties have been set, the framework
invokes this method. Programmers can activate properties here that have to be activated at the same time.
Appender is ready: At this point, the framework can call append () method to handle a logging request. This
method is invoked by the AppenderSkeleton. doAppend () method.
Finally, the appender is closed: When the framework is about to remove y our custom appender instance, it
calls y our appender's close () method. close () is a cleanup method, which means you need to free all the
resources you have allocated. It is a required method, and it takes no parameters. It must set the closed field
to true, alerting the framework when an attempt to use a closed appender occurs. We've reviewed the concepts
related to building y our own appender.
real
eal-
3. Case study: real-world example appender.
3.1 Writing an Instant Messenger -based appender:
The code presented in this document shows how you can extend the log4j framework to integrate IM features.
It's designed to enable a log4j-compliant application to log its output onto Instant Messenger like GTalk. The
PNotifyAppender actually works as a customized IM client. However, instead of System.out, files, or TCP sockets,
it takes Instant Messenger as the underlying output device.
To provide IM support, we don't need to reinvent the wheel when developing ad-hoc solutions. Instead, we're
going to leverage a tool, here I considered the best of breed: GTalk, which internally uses XML based protocol
for instant messaging and presence i.e. XMPP (Extensible Messaging and Presence Protocol) which is an
open-standard communications protocol for message-oriented middleware based on XML, the protocol
originally named as Jabber.
We chose Jabber over other IM systems because it offers a wide variety of benefits, including its:
1. Open source.
2. Simplicity.
3. Interoperability with other IM systems.
4. Resource awareness.
7|Page
8. 3.2 Usage Scenario of PNotifyAppender:
Below diagram shows the PNotifyAppender usage scenario: Any application configured to use
PNotifyAppender logs its debugging data wrapped as GTalk IM message. The instant message is routed over
organization network to the system administrator. Thus whenever system administrator needs to check on the
application status, they simply log into their Gmail account using Gtalk IM using his desktop or laptops at
work, or when he is away from his desk, he may use Gtalk IM running on any handheld devices. As Gtalk IM
has the capability to notify us even when we are offline once we log in it will notify system administrator with the
pending notifications or real time.
But why do we need PNotifyAppender? The answer is that sending messages to IM (Gtalk) allows us to
monitor application behavior more easily with tools at our disposal.
PNotifyAppender offers several advantages:
1. You get real-time notification about the exception occurred at server, we can consider as “Immediate
Logging”
2. GTalk IM isn’t just for desktop computers. It is also being developed for wireless devices such as PDA’s and
mobile phones.
3. No setup overhead.
4. PNotifyAppender notifies the real time exception immediately with the exception details automatically to the
Instant Messenger like GTalk and has the ability to keep track of the exception history into the GMail account.
8|Page
9. 4. Overview of PNotifyAppender:
Overview PNotifyAppender:
PNotifyAppender is modeled based on SMTPAppender logging strategy. PNotifyAppender stores logging
events in an internal cyclic buffer and send them as an instant message only when the logging request received
triggers a user-specified condition. Optionally, users may provide a triggering event evaluator class. However, by
default, delivery is triggered on events assigned the level ERROR or higher.
The number of logging events delivered in each message is limited by the buffer size. The cyclic buffer holds only
the last buffer size logging events, overflowing when it becomes full and throwing away the older events.
To connect to a GTalk, PNotifyAppender relies on Smack API. Smack is an open source, high-level library that
handles the protocol details for communicating with GTalk servers. As a result, you don't need any special
Jabber or XML expertise to understand the code.
The properties for PNotifyAppender are summarized in Table 1
Property Description Type Required
username Gmail Account username e.g. anthing123 String Yes
If SSL is true then e.g. anything123@gmail.com
password Gmail Account password. String Yes
toNotify Recipient's address. just as String Yes
e-mail addresses do : xxxx@gmail.com
SSL Used to secure connections. boolean No, defaults to
false
The maximum number of logging events that can be kept in
buffersize the cyclic buffer. int defaults is 16
Takes as a value a string representing the fully qualified
name of a class that implements the org.apache.log4j.spi.
TriggeringEventEvaluator interface (in other words, a class
that contains a custom triggering logic that overrides the No, defaults to
evaluatorClass default one). If this option is not specified, String DefaultEvaluator
PNotifyAppender uses an instance of the DefaultEvaluator
class, which triggers a response to events assigned the level
ERROR or higher.
proxy It is used to identify whether log4j application behind any boolean No. Default is
proxy. false.
proxyHost If the proxy property is true, proxyHost takes the proxy String Yes, if proxy sets
address. Example : 192.168.xx.xx to true else No.
proxyPort Proxy Port. e.g. 3245 String Yes, if proxy sets
to true else No.
history This property is used, if we want to log the exception boolean No. Default is false
history of the real time notification
subject The subject option takes a string value which should be a String Yes if history sets
the subject of the e-mail message to true else No.
cc Set the cc recipient addresses. String No.
bcc Set the bcc recipient addresses String No.
SMTPHost The SMTPHost option takes a string value which should be String Yes if history sets
a the host name of the SMTP server that will send the email to true else No.
message
9|Page
10. 4.1 Let’s take a closer look at below code:
package com.pnotify.appender;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.TriggeringEventEvaluator;
public class PNotifyAppender extends AppenderSkeleton {
protected CyclicBuffer cb;
private int buffersize;
private String username;
private String password;
private String toNotify;
private boolean SSL;
private String proxyHost;
private String proxyPort;
private boolean proxy;
protected boolean checkEntryConditions () {}
public boolean verifyRosterEntryJabberClient (XMPPConnection connection) {}
// AppenderSkeleton callback method
public void activateOptions () {}
protected void sendBuffer () {}
public synchronized void close () {}
public boolean requiresLayout () {
return true;
}
protected void append (LoggingEvent event) {
}
}
Notice the following about the PNotifyAppender:
a) The PNotifyAppender class extends org.apache.log4j.AppenderSkeleton, which all custom appenders
must do. PNotifyAppender inherits common functionalities from AppenderSkeleton such as appender
threshold and custom filtering.
b) The first part of our appender is straight-forward. We have the appender's fields and set/get methods f or each
of them. Properties and method signatures obey the JavaBeans naming convention. Thus, log4j can analyze the
appender using ref lection, transparently handling the appender configuration.
c) To complete our appender, we must implement the callback methods that the log4j framework calls to
manage our appender: requiresLayout (), activateOptions (), append (), and close ().The log4j framework
calls the requiresLayout () method to know whether the custom appender requires a lay out. Notice that some
appenders use a built-in format or don't format the events at all, so they don't require a Layout object. The
PNotifyAppender requires a lay out, hence the method returns true.
10 | P a g e
11. d) Notice that AppenderSkeleton implements the org.apache.log4j.spi.OptionHandler interface.
AppenderSkeleton implements the single method of this interface, activateOptions (), as an empty method.
PNotifyAppender needs this method because of the interdependence between its properties. For example, the
connection with a GTalk Server depends on the Host, Port, username, password and toNotify properties. so
PNotifyAppender cannot establish a connection until these five properties have been initialized. The log4j
framework calls the activateOptions () method to signal to the appenders that all the properties have been set.
e) Once the activateOptions () method returns, the appender is ready to handle logging requests.
AppenderSkeleton.doAppend () does most of the real appending work.
f) The checkEntryConditions () method checks whether there is a Chat object available to append the output
and also whether there is a Layout object to format the incoming event object. If these preconditions are not
satisfied, then append () outputs a warning message and returns without proceeding.
g) The sendBuffer () method sends the contents of the buffer as an IM message. The method loops through the
events kept in the buffer, formatting each one by invoking the format () method of the layout object. The string
representation of the events is appended to a StringBuffer object. Finally, sendBuffer () calls the sendMessage
() method of the chat object, sending the message.
h) The AppenderSkeleton.doAppend () method, which invokes append (), is synchronized so sendBuffer ()
already owns the monitor for the appender. This frees us from needing to synchronize the CyclicBuffer Object,
cb.
i) The verifyRosterEntryJabber () method subscribes to the given email Id provided to toNotify property of
the PNotifyAppender, if it is not present into account.
5. Putting it all together
We'll wrap up by showing you PNotifyAppender in action.
STEP 1: Drop in the requisite Jars into application.
a) commons-logging-1.1.1.jar
b) log4j-1.2.16.jar
c) mail.jar
d) smack.jar (3.2.1)
e) smackx.jar (3.2.1)
f) pnotifyappender.jar
STEP 2: Configuring log4j.properties
log4j.rootLogger=GTALK
STEP 2.1: Minimum configuration required if we want real time notification of the exception in GTalk.
log4j.appender.GTALK=com.pnotify.appender.PNotifyAppender
log4j.appender.GTALK.Threshold=ERROR
log4j.appender.GTALK.username=xxxxxxx or xxxxx@gmail.com (if SSL/history property is set to true)
log4j.appender.GTALK.password=xxxxxxx
log4j.appender.GTALK.toNotify=xxxx@gmail.com
log4j.appender.GTALK.layout=org.apache.log4j.PatternLayout
log4j.appender.GTALK.layout.ConversionPattern=%d{MM-dd@HH:mm:ss}[%-5p][%C.%M:%L]-(%F):
%m%n
11 | P a g e
12. log4j.additivity.GTALK=false
// if behind a proxy
log4j.appender.GTALK.proxy=true
log4j.appender.GTALK.proxyHost=xxx.xx.xx.xx
log4j.appender.GTALK.proxyPort=xxxx
STEP 2.2: If we want to keep the history of notified exception into the respective GMail account.
2.2
log4j.appender.GTALK=com.pnotify.appender.PNotifyAppender
log4j.appender.GTALK.Threshold=ERROR
log4j.appender.GTALK.username=xxxxx@gmail.com
log4j.appender.GTALK.password=xxxxxxx
log4j.appender.GTALK.toNotify=xxxx@gmail.com
log4j.appender.GTALK.layout=org.apache.log4j.PatternLayout
log4j.appender.GTALK.layout.ConversionPattern=%d{MM-dd@HH:mm:ss}[%-5p][%C.%M:%L]-(%F):
%m%n
log4j.appender.GTALK.history=true
log4j.appender.GTALK.SMTPHost=smtp.gmail.com
log4j.appender.GTALK.subject=Email Notification from Application
log4j.appender.GTALK.cc=xxxxx@xxx.com
log4j.appender.GTALK.bcc=xxxxx@xxx.com
log4j.additivity.GTALK=false
The above configuration script adds PNotifyAppender to the root logger, so each logging request received will
be dispatched to our appender.
When we run our respective application, An IM message then pops up on the recipient's screen and the below
screenshots shows the resulting message received by GTalk and GMail.
12 | P a g e
13. Notice that the above figure depicts the real time notification about the exception occurred in the application at
GTalk in the mail Id mentioned in toNotify property of PNotifyAppender
The above figure depicts the exception got recorded into the given GMail Account when history property in
PNotifyAppender is set to true and setting the other dependent property.
As we can see in the both cases the exception information contained in the last Logging events has been correctly
transmitted.
This example application simply showcases use of the PNotifyAppender, so explore and have fun!
6. Conclusion
The log4j network appenders, SocketAppender, JMSAppender, and SMTPAppender, already provide mechanisms
to monitor Java-distributed applications. However, several factors make IM a suitable technology for remote
logging in real-time. In this article, we've covered the basics of extending log4j with y our custom appenders, and
we've seen the implementation of a basic PNotifyAppender step by step. Many developers and system
administrators can benefit from their use.
7. Resources
1. http://logging.apache.org/log4j/1.2/
2. http://www.javaworld.com/javaworld/jw-10-1999/jw-10-cooltools.html
3. Book: Template Method - Design Patterns in Java by Steven John Metsker and William C.Wake
4. http://www.igniterealtime.org/projects/smack/
5. Book: Instant Messaging in Java: the jabber Protocol by Ian Shigeoka
6. MagicwithMerling: Exception and Logging http://www.ibm.com/developerworks/java/library/j-
mer1211/index.html.
13 | P a g e