Thread: [Pas-dev] Suggestion for logging
Status: Beta
Brought to you by:
mortis
From: Kyle R. B. <mo...@vo...> - 2003-06-12 23:06:05
|
In the Java projects I've been involved with, we've used Jakarta's Log4j. Probably the most important features of the logging library are that you can tweak the logging level or the destination(s) from a configuration file. The logging level (fatal, error, warn, info or debug) can be set across the entire project, on a per package or a per-object basis. For example, if you were trying to watch a constrained set of modules, you could set up a configuration like: log4perl.logger=ERROR, file log4perl.logger.Org.Bgw=INFO, screen, file log4perl.logger.Org.Bgw.Pas.Page=DEBUG, screen, file log4perl.logger.Org.Bgw.Pas.Response=ERROR, file log4perl.logger.file=Log::Dispatch::File log4perl.logger.file.filename=logs/pas.log log4perl.logger.file.layout=Log::Log4perl::Layout::PatternLayout log4perl.logger.file.layout.ConversionPattern=%d %p> %F{1}:%L %M - %m%n log4perl.logger.screen=Log::Dispatch::Screen log4perl.logger.screen.stderr=0 log4perl.logger.screen.layout=Log::Log4perl::Layout::SimpleLayout The first section sets up the default level so only ERROR, or FATAL get logged. As a default for all the objects and sub-packages of Org::Bgw, it sets up a default of INFO or more severe. For Page it turns the log level all the way up to DEBUG, while turning it back down to ERROR for Response. To make the support easier, since perl can detect the calling package, we could centralize the configuraiton and obtaining of the logger to a single module: ### package Org::Bgw::Log; ... use Log::Log4perl; Log::Log4perl->init($ENV{'PAS_BASE'}."/conf/log4perl.conf"); # get a Log4perl log object based on the caller's package name sub getLogger { Log::Log4perl::get_logger( (caller)[0] ); } Then in any random module: ### package Org::Bgw::Foo; ... my $log = Org::Bgw::Log::getLogger; sub somethingComplicated { my($self) = @_; # testing log->is_debug is an optimization so that Dumper isn't # called (since it could be expensive) unless the log level is set # to debug -- unless the level is debug, the arguments aren't # computed, and the log statement isn't called. if( $log->is_debug() ) { $log->debug("somethingComplicated, self = ", Dumper($self) ); } eval { $log->info("Entering log running loop..."); while(...) { } $log->info("major processing completed"); }; if($@) { $log->fatal("Woah, bad error in main processing, aborting: $@"); $self->throw($@); } reutrn 1; } This article gives a good overview: http://www.perl.com/pub/a/2002/09/11/log4perl.html This is the main project site: http://log4perl.sourceforge.net/ It's also available from CPAN (http://cpan.org/modules/by-module/Log/). The project comes with a few appenders: Screen, File and DBI. It can also make use of Log::Dispatch appenders, which include: Email, FileRotate (good for keeping log files from filling up all free space), Tk and Syslog. One thing that could be done for production would be to set up fatal errors to go to a File appender, and an Email appender where the Email appender can be used for easy notification of catastrophic errors. The functionality is good, the downside is that this introduces another dependency. I had thought that Log::Dispatch::Config had these features, but it turns out that it doens't really. What do you guys think about switching? It should be an easy change. Kyle -- Health Market Science, Inc. 625 Ridge Pike, Bldg. E, Ste. 400 Conshohocken, PA 19428 Office: (610) 940-4002 x116 Fax: (610) 940-4003 Email: kb...@hm... Web: http://www.hmsonline.com/ ----- End forwarded message ----- -- ------------------------------------------------------------------------------ Wisdom and Compassion are inseparable. -- Christmas Humphreys mo...@vo... http://www.voicenet.com/~mortis ------------------------------------------------------------------------------ |
From: Mental P. <me...@ne...> - 2003-06-13 14:32:39
|
On Thu, 2003-06-12 at 19:05, Kyle R. Burton wrote: > In the Java projects I've been involved with, we've used Jakarta's > Log4j. Probably the most important features of the logging library are > that you can tweak the logging level or the destination(s) from a > configuration file. >=20 I'm familiar, if not well versed, with how it works. Its a pretty cool idea. > ### > package Org::Bgw::Log; > ... > use Log::Log4perl; > Log::Log4perl->init($ENV{'PAS_BASE'}."/conf/log4perl.conf"); > # get a Log4perl log object based on the caller's package name > sub getLogger { Log::Log4perl::get_logger( (caller)[0] ); } >=20 >=20 > Then in any random module: >=20 > ### > package Org::Bgw::Foo; > ... > my $log =3D Org::Bgw::Log::getLogger; >=20 > sub somethingComplicated > { > my($self) =3D @_; >=20 > # testing log->is_debug is an optimization so that Dumper isn't > # called (since it could be expensive) unless the log level is set > # to debug -- unless the level is debug, the arguments aren't > # computed, and the log statement isn't called. > if( $log->is_debug() ) { > $log->debug("somethingComplicated, self =3D ", Dumper($self) ); > } I like everything except the if $log->is_debug. I haven't looked into or spent more than 2 seconds thinking about implementation, but wouldn't it be easier if you just wrapped it (log4perl) with Org::Bgw::Log? Then you could just call $self->log()->debug like usual. Let the wrapper do the debug level check and just return if its not set. IF it is set, it can call its getLogger method and log stuff. It'll be better separation between page objects and log levels. It'll avoid needless instantiations of the lob ogject and simplify the interface. Know what i mean?=20 That and depending on the names of the log levels it'd be a transparent change. The only differences would be config file stuff and suddenly you can log on a per-package basis.=20 Hopefully this is coherent.....=20 --=20 Mental (Me...@Ne...) Distant - An approaching age When this document falls beneath another's gaze Too late - We have lost the dawn The signal's loud and clear, but the transmitter's gone --Assemblage 23 "Document" |
From: Kyle R. B. <mo...@vo...> - 2003-06-13 16:10:59
|
> > if( $log->is_debug() ) { > > $log->debug("somethingComplicated, self = ", Dumper($self) ); > > } > > I like everything except the if $log->is_debug. I haven't looked into or > spent more than 2 seconds thinking about implementation, but wouldn't it > be easier if you just wrapped it (log4perl) with Org::Bgw::Log? Then you > could just call $self->log()->debug like usual. Let the wrapper do the > debug level check and just return if its not set. IF it is set, it can > call its getLogger method and log stuff. It'll be better separation > between page objects and log levels. It'll avoid needless instantiations > of the lob ogject and simplify the interface. Know what i mean? "if( $log->is_debug() ) {" is an _optimization_. You don't have to use it. In the example case I gave, the point was that "Dumper($self)" could be an _expensive_ operation to perform at run-time -- for Dumper to produce the string representation of "$self". If you didn't wrap the call with the "is_debug" test, then Dumper is invoked, the argument list for the "debug()" call is built (which might be expensive), "debug()" is called, and then it decides to throw away all the work that Dumper did because the log level isn't high enough. Does this make sense? You don't have to use "is_debug" (or is_info, is_error, etc.) if you don't want to. The thing to keep in mind is that the argument list you put in your code gets computed regardless of the log level. The conditional prevents that code from executing. Log4perl already does the "is_debug" test internally, the example there was just to point out that inefficiencies existed and one approach for overcoming them. As for the "$self->log->debug" approach automatically detecting the caller and constructing the logger, you could wrap all of it that way. The issue with that, I beleive, is again overhead and efficiency. If having all these instantiated loggers turns out to be an issue then we can explore optimizing it. Based on my experience using Log4j it has yet to be an issue - the logger instances tend to be very light weight. > That and depending on the names of the log levels it'd be a transparent > change. The only differences would be config file stuff and suddenly you > can log on a per-package basis. There are good reasons to follow the convention of using the package name, but there can also be good reasons not to. You might want to mix metaphores in the code - you might actually want to use a specific logging context for any of a few reasons -- maybe you want to include a database reference in the logging context, or a different context for administrative users logged in to your software. The logging infrastructre could also be used for reasons other than code inspection - you might want it to act as a simple insturmentation api. The use of a 'profile' log context could allow you to enable/disable in-code insturmentaiton of portions of the codebase. Thanks for the discussion. Kyle -- ------------------------------------------------------------------------------ Wisdom and Compassion are inseparable. -- Christmas Humphreys mo...@vo... http://www.voicenet.com/~mortis ------------------------------------------------------------------------------ |