Re: Aspect questions?
Lew <noone@lewscanon.com> wrote in news:jibf3u$ee9$1@news.albasani.net:
Novice wrote:
Lew wrote:
Logging is a great use case for aspects. However, there are
alternatives to lessening logging's overhead, too. You have to weigh
costs and benefits.
What are other good ways to do logging?
log4j is my favorite.
Right now, the two main programs in my current project each create
their own logger and then pass it to the classes they call in their
parameters.
I don't think that's a good pattern. Which logging library are you
using?
I'm using Java Logging. Does that answer your question or are you asking
something else?
I have each class create its own logger (log4j style shown:
package eegee;
import org.apache.log4j.Logger;
import static org.apache.log4j.Logger.getLogger;
public class Foo
{
final Logger logger = getLogger(getClass());
public void foo()
{
logger.debug("");
// do the fooey stuff
try
{
// do the trying stuff
}
catch (FooException exc)
{
String msg = "trying stuff failed. "+
exc.getLocalizedMessage(); logger.error(msg, exc);
throw new IllegalStateException(msg, exc);
}
}
}
This boilerplate is what aspects aim to remove from the code and I
espouse retaining.
My code is not terribly different than that.
Here's the actual code minus the Javadoc comment:
================================================================
public static Logger configureLogger(String className, String
logFilePath, String logFileName, Locale locale, Formatter formatter,
Level loggingLevel) {
String METHOD_NAME = "configureLogger()"; //$NON-NLS-1$
/* Ensure that the mandatory parameters have all been specified. */
if (className == null) {
throw new IllegalArgumentException("The name of the class must
be provided."); //$NON-NLS-1$
}
if (logFilePath == null) {
throw new IllegalArgumentException("The log file path must be
provided."); //$NON-NLS-1$
}
if (logFileName == null) {
throw new IllegalArgumentException("The log file name must be
provided."); //$NON-NLS-1$
}
if (locale == null) {
throw new IllegalArgumentException("The locale must be
provided."); //$NON-NLS-1$
}
/* Create the logger. */
Logger logger = Logger.getLogger(className);
/* Create path identified by logFilePath if it does not exist. */
File path = new File(logFilePath);
if (!path.exists()) {
path.mkdir();
}
/* If the invoking class has specified a logging level, use it.
Otherwise, set a default level. */
if (loggingLevel != null) {
logger.setLevel(loggingLevel);
}
else {
logger.setLevel(Level.INFO);
}
/* Create a file handler for the logger. The log file name does NOT
need to exist prior to the execution of this method. */
String logFile = logFilePath + File.separator + logFileName;
Handler logFileHandler = null;
try {
logFileHandler = new FileHandler(logFile);
}
catch (IOException io_excp) {
String msg = CLASS_NAME + "." + METHOD_NAME + " - Couldn't
create FileHandler using file " + logFile + ".\n Details: " + io_excp +
". The stackTrace from this event has been written to the console."; //
$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
io_excp.printStackTrace();
JOptionPane.showMessageDialog(null, msg, CLASS_NAME,
JOptionPane.ERROR_MESSAGE);
return logger;
}
logger.addHandler(logFileHandler);
/* If the invoking class has specified a formatter, use it.
Otherwise, don't add a formatter. */
if (formatter != null) {
Handler[] handlers = logger.getHandlers();
for (Handler oneHandler : handlers) {
if (oneHandler.getClass().getName().equals
("java.util.logging.FileHandler")) { //$NON-NLS-1$
oneHandler.setFormatter(formatter);
}
}
}
return logger;
}
================================================================
I put that in a utility class in my Common project. Then any program that
wants a logger just executes this one method passing the appropriate
parameters.
Either way the logger should be tied to the actual class emitting log
messages, not some common class.
I'm not sure what you mean. The main program creates the logger and
passes a reference to it to each class that it instantiates (well, not
all of them but I am gradually retrofitting the classes so that all of
them get an instance of the logger as a parameter).
Is that not the right thing to do?
I realize that I could have each class create its own logger and log but
would seem to guarantee a proliferation of logs that would be a pain to
find. It seems reasonable to me that if I have a bunch of programs,
including Foo and Bar, and any of them could call one of my common date
routines or my AboutDialog, that any log messages arising from those
classes should be in the log for Foo or Bar, not in separate logs for
each class. Then I'd end up having to look in the AboutDialog log for
messages from that class, a DateUtils log for messages from that class,
etc. etc.
The called classes write their messages to that logger. I've created
a method that does the logger creation and put it in a utility class
in my Common project so that my programs only need to execute that
method (passing in a few parameters of course) and get an appropriate
logger back. Each of the classes instantiated by the main programs
gets a logger passed among their parameters. The newly instantiated
class then verifies that the logger is not null and throws an
IllegalArgumentException if it is. Each class writes to the log when
it seems appropriate. Sometimes, that is to log an Exception. Other
times, I log things that are more diagnostic in nature. If a method
isn't 100% right yet, I will log calculations that it is doing, such
as calculating the width of a column in a JTable or whatever.
Did you reinvent the logging wheel? If so, does your logging framework
feature log levels, infinitely flexible log formats, logging to screen
or (rolling) file logs, virtual elimination of logging overhead at
levels finer than configured, automatic if slow retrieval of class and
method and line where the logging happens, XML configuration,
portability, a large body of online literature describing its use,
standardization, widespread adoption, and a body of top developers
contributing to the project?
Goodness no! I am using the Java Logging classes to do my logging. Sorry
if that wasn't clear.
This works okay but it seems to me that it might be more elegant to
put the logging in aspects, especially the diagnostic stuff that I am
using to verify that the suspect methods are doing their jobs
correctly.
What is "elegant"?
Personally I promote "useful" as the useful metric.
Fair enough. I have a great deal of respect for useful and would
certainly put it above elegant! But I'm the kind of guy who likes to have
his cake and eat it too. If I can make it useful AND elegant, that trumps
just useful in my book ;-)
I'm not sure yet if all the logging code, including the creation of
the log itself, should go into aspects though. It probably makes more
sense to create the logger the way I am doing it and continue to do
the actual
Not from your description of it.
writes to the logs in my existing classes when it involves Exception
handling. The diagnostic logging, though, seems like a prime
candidate for the aspects. I could write pointcuts that specify the
methods that are a little dubious and log them in as much detail as I
need. Methods that are already working to my satisfaction could be
left alone with only their Exception logging.
Logging is logging is logging, to misquote Gertrude Stein. Suddenly
now you've created two logging aspects. That's not elegant.
Logging shouldn't be added to and removed from code. That's not the
purpose of logging. Logging is to remain in the code forever.
I think it depends on what kind of logging you're doing. I agree that
logging of Exceptions should probably be in the classes themselves as
long as the Exception can occur. (Mind you, from what I'm seeing in the
AspectJ manual, it seems as if aspects are designed to handle that too.)
But aspects seem more likely to be beneficial in tracking down wonky code
at development time. It's not that unusual for me to write a method that
does something and find that it works pretty well for most cases but then
do odd or unexpected things in some cases. For example, the method that
calculates column widths might do a reasonable job in most cases but
sometimes calculate widths that are noticeably too large or too small.
For something like that, I can see aspects being useful for digging out
what is actually going on in the method without having to recode the
method itself. And once the problem is found, if the debugging is
happening in an aspect, I just delete it from the aspect and leave my
original method nice and tidy rather than having to yank out the
statements I added.
Of course, the Eclipse debugger can make a lot of the logging unnecessary
in the first place so that you don't have to add code to either the
method or an aspect. I don't want to start writing reams of
System.out.println() statements when the debugger can often tell me
what's going on much better.
Logging is not for the developer. That's an all-too-common
misconception. Logging is for the sysop.
I agree. Mind you, I still tend to use them regularly to help with
debugging wonky code but the ultimate user of the log is the sysop.
Have you ever tried to suss out a production issue from the logs?
Frankly, no. I aspire to write very useful messages that would be helpful
in that regard but I won't know how useful they really are until I have
some major systems in production.
The sort of dynamic granularity you describe is built in to the
logging framework. You use levels - DEBUG for debug purposes (or FINE
if you're using java.util.logging), WARNING for warnings, ERROR
(SEVERE) for errors, and so forth.
Yes, I'm using those levels.
By removing logging to AspectJ, you eliminate the flexibility,
visibility and granularity of control logging should support.
It's not just in the code you weigh. Frameworks have a deployment
cost. It's harder to deploy than to write code. Framework bloat is a
major problem in the real world. Aspects also reduce visibility into
areas, sometimes a good thing and others not.
What do you mean when you say "framework"? That term puts me in mind
of things like Spring - which I know of but have never used - but I'm
not using any formal frameworks like that and know very little about
them. I'm getting the impression that you are thinking of my work as
being sort of a homegrown framework. Maybe it is; I really don't
know.
A deployment or code base that handles some part of the project under
the hood for you, and gives you a public interface for control of its
behavior. In your case I'm referring to the AspectJ framework. Logging
is also a framework, one apparently you are not using.
Not true, as mentioned above. But it's my fault; I apparently wasn't
clear enough about what I am doing.
Use log4j or java.util.logging (or both). Those are (both)
foundational and you should learn (both of) them very early in your
Java career.
Both would seem like overkill ;-) But I am indeed using
java.util.logging.
Remember - logging is meant to be there in production, for benefit of
sysops and system reliability engineers. Code is not the be-all and
end-all of programming.
Agreed.
--
Novice