| 1 | ## ------------------------------------------------------------------------ | 
| 2 | ##  $Id: Logger.pm,v 1.5 2003/05/13 08:40:15 joko Exp $ | 
| 3 | ## ------------------------------------------------------------------------ | 
| 4 | ##  $Log: Logger.pm,v $ | 
| 5 | ##  Revision 1.5  2003/05/13 08:40:15  joko | 
| 6 | ##  minor update: renamed attribute | 
| 7 | ## | 
| 8 | ##  Revision 1.4  2003/03/29 07:16:00  joko | 
| 9 | ##  minor update: changed default log-file-name to 'perl_app.log' | 
| 10 | ## | 
| 11 | ##  Revision 1.3  2003/03/28 07:23:25  joko | 
| 12 | ##  fix to basepath calculation | 
| 13 | ## | 
| 14 | ##  Revision 1.2  2003/02/20 21:06:27  joko | 
| 15 | ##  + configurable by package globals | 
| 16 | ##  + standalone mode (object creation via 'new') | 
| 17 | ##  + some comments | 
| 18 | ##  + Log::Dispatch gets configured inside here now | 
| 19 | ##  + coordinated stack tracing | 
| 20 | ##  + convenient shortcut methods resembling the syslog levels | 
| 21 | ## | 
| 22 | ##  Revision 1.1  2003/02/19 00:34:52  joko | 
| 23 | ##  + initial commit, again refactored to this place | 
| 24 | ## | 
| 25 | ##  Revision 1.3  2003/02/18 18:33:59  joko | 
| 26 | ##  + fix: just logs if possible | 
| 27 | ## | 
| 28 | ##  Revision 1.2  2003/02/18 16:37:22  joko | 
| 29 | ##  + fix: ...::Hardwired is an optional module | 
| 30 | ## | 
| 31 | ##  Revision 1.1  2003/02/18 15:57:34  joko | 
| 32 | ##  + initial commit, refactored from DesignPattern::Object::Logger | 
| 33 | ## | 
| 34 | ##  Revision 1.2  2003/02/14 14:20:42  joko | 
| 35 | ##  + decreased default log-level to debug | 
| 36 | ## | 
| 37 | ##  Revision 1.1  2002/12/22 14:17:38  joko | 
| 38 | ##  + initial check-in, refactored | 
| 39 | ## | 
| 40 | ##  Revision 1.1  2002/12/19 01:14:48  cvsjoko | 
| 41 | ##  + initial check-in | 
| 42 | ## | 
| 43 | ## ------------------------------------------------------------------------ | 
| 44 |  | 
| 45 |  | 
| 46 | =pod | 
| 47 |  | 
| 48 | This is weird! Forget it. | 
| 49 | But we depend on it! | 
| 50 | Hmmm.... | 
| 51 |  | 
| 52 |  | 
| 53 | =cut | 
| 54 |  | 
| 55 |  | 
| 56 | package DesignPattern::Logger; | 
| 57 |  | 
| 58 | use strict; | 
| 59 | use warnings; | 
| 60 |  | 
| 61 | use Data::Dumper; | 
| 62 | use Log::Dispatch; | 
| 63 | use Log::Dispatch::Screen; | 
| 64 | use Log::Dispatch::Config; | 
| 65 | use Cwd; | 
| 66 | use DesignPattern::Object; | 
| 67 |  | 
| 68 | use vars qw( | 
| 69 | $LEVEL_DEFAULT | 
| 70 | $ENABLE_NAMESPACE | 
| 71 | $NAMESPACE_LEVEL | 
| 72 | $ENABLE_METHODS | 
| 73 | $METHOD_LEVELS | 
| 74 | $TRACE_STACK_LEVELS | 
| 75 | ); | 
| 76 |  | 
| 77 | # default behaviour | 
| 78 | $LEVEL_DEFAULT = 'debug'; | 
| 79 | $ENABLE_NAMESPACE = 1; | 
| 80 | $NAMESPACE_LEVEL = undef; | 
| 81 | $ENABLE_METHODS = 0; | 
| 82 | $METHOD_LEVELS = [3]; | 
| 83 | $TRACE_STACK_LEVELS = []; | 
| 84 |  | 
| 85 |  | 
| 86 | # TODO: review, revise and rewire this! | 
| 87 | #my $logger = Log::Dispatch::Config->instance; | 
| 88 |  | 
| 89 | sub __init { | 
| 90 | my $self = shift; | 
| 91 | my $action = shift; | 
| 92 | my $message = shift; | 
| 93 | #my $options = shift;   # ??? or: $self->{__logger}->{options} already...? | 
| 94 |  | 
| 95 | # configure | 
| 96 | if (!$self->{__logger}->{configured}) { | 
| 97 | $self->{__logger}->{configured} = 1; | 
| 98 | $self->_configure_LogDispatch(); | 
| 99 | } | 
| 100 |  | 
| 101 | # set passed-in arguments | 
| 102 | $self->{__logger}->{action} = $action; | 
| 103 | $self->{__logger}->{message} = $message; | 
| 104 |  | 
| 105 | # trace | 
| 106 | #my $tmp = caller(2); | 
| 107 | #print Dumper($tmp); | 
| 108 | #exit; | 
| 109 |  | 
| 110 |  | 
| 111 | # handle caller | 
| 112 |  | 
| 113 | # determine class- and subroutinename of context calling us | 
| 114 | # this task can be controlled by tuning its behaviour | 
| 115 | # this can be changed by setting | 
| 116 | #   DesignPattern::Logger::... | 
| 117 | #   ... | 
| 118 | #   ... | 
| 119 | my $calling_class; | 
| 120 | my $calling_sub; | 
| 121 |  | 
| 122 |  | 
| 123 | # Default behaviour is: | 
| 124 | #   Use classname from $self and suppress subroutine name. | 
| 125 | #   In case of using this together with DesignPattern::Bridge, | 
| 126 | #   the classnames of child objects are suppressed. | 
| 127 | #   Instead, the classname of the parent container object | 
| 128 | #   gets used *always*. | 
| 129 | $calling_class = ''; | 
| 130 | $calling_sub = ''; | 
| 131 |  | 
| 132 | # Log class names (en-/disabled, take from specified level) | 
| 133 | if ($ENABLE_NAMESPACE) { | 
| 134 | $calling_class = ref $self; | 
| 135 | if ($NAMESPACE_LEVEL) { | 
| 136 | my @entry = caller($NAMESPACE_LEVEL); | 
| 137 | $calling_class = $entry[0]; | 
| 138 | } | 
| 139 | } | 
| 140 |  | 
| 141 | # Log method names (en-/disabled, multi-level) | 
| 142 | if ($ENABLE_METHODS) { | 
| 143 | my @methods; | 
| 144 | foreach (@{$METHOD_LEVELS}) { | 
| 145 | (my $c_package, my $c_filename, my $c_line, my $c_subroutine, my $c_hasargs, | 
| 146 | my $c_wantarray, my $c_evaltext, my $c_is_require, my $c_hints, my $c_bitmask) = caller($_); | 
| 147 | $c_subroutine ||= ''; | 
| 148 | $c_subroutine =~ s/.*:://; | 
| 149 | push @methods, $c_subroutine; | 
| 150 | } | 
| 151 | $calling_sub = join(': ', @methods) if @methods; | 
| 152 | } | 
| 153 |  | 
| 154 |  | 
| 155 |  | 
| 156 | # set default arguments | 
| 157 | $self->{__logger}->{classname} = $calling_class; | 
| 158 | $self->{__logger}->{functionname} = $calling_sub; | 
| 159 | # FIXME: deprecate this! | 
| 160 | $self->{__logger}->{caller} = caller; | 
| 161 |  | 
| 162 | # set default values | 
| 163 | $self->{__logger}->{action} ||= ''; | 
| 164 | $self->{__logger}->{options}->{tag} ||= ''; | 
| 165 |  | 
| 166 | # FIXME: deprecate this! | 
| 167 | #$self->{__logger}->{level} ||= 'info'; | 
| 168 | #$self->{__logger}->{level} ||= 'debug'; | 
| 169 | $self->{__logger}->{level} ||= $LEVEL_DEFAULT; | 
| 170 |  | 
| 171 | # handle stacktrace | 
| 172 | foreach (@$TRACE_STACK_LEVELS) { | 
| 173 | #print "trace-level: ", $_, "\n"; | 
| 174 | #(my $c_package, my $c_filename, my $c_line, my $c_subroutine, my $c_hasargs, | 
| 175 | #my $c_wantarray, my $c_evaltext, my $c_is_require, my $c_hints, my $c_bitmask) = caller($_); | 
| 176 | my @entry = caller($_); | 
| 177 | push @{$self->{__logger}->{trace}->{stack}}, \@entry; | 
| 178 | } | 
| 179 |  | 
| 180 | } | 
| 181 |  | 
| 182 | sub __out { | 
| 183 | my $self = shift; | 
| 184 | my $message = shift; | 
| 185 |  | 
| 186 | # if message given here, append to message stored inside object | 
| 187 | $self->{__logger}->{message} .= ' - ' . $message if $message; | 
| 188 |  | 
| 189 | # TODO: refactor this: | 
| 190 | #  - push '$self->__get_prefix()' to additional log-entry-metadata, don't show it on/in any console/logfile output | 
| 191 | #$logger->log( level => $self->{__logger}->{level}, message => $self->__get_prefix_long() . $message ); | 
| 192 | #$logger->log( level => $self->{__logger}->{level}, message => $message ); | 
| 193 | #$logger->warning( __PACKAGE__ . "->_skip: '$caller' (mixed into '$classname'): ($options->{tag}) $message."); | 
| 194 | #$self->{__logger}->{instance}->log( level => $self->{__logger}->{level}, message => $self->__get_prefix_short() . $self->{__logger}->{message} ); | 
| 195 | #$self->{__logger}->{instance}->log( level => $self->{__logger}->{level}, message => $self->__get_prefix_module() . $self->{__logger}->{message} ); | 
| 196 | $self->{__logger}->{instance}->log( level => $self->{__logger}->{level}, message => $self->__get_prefix_best() . $self->{__logger}->{message} ); | 
| 197 | } | 
| 198 |  | 
| 199 |  | 
| 200 | sub __handle_options { | 
| 201 | my $self = shift; | 
| 202 | #my $options = shift;   # ??? or: $self->{__logger}->{options} already...? | 
| 203 | } | 
| 204 |  | 
| 205 | sub __get_prefix_long { | 
| 206 | my $self = shift; | 
| 207 | return __PACKAGE__ . "->$self->{__logger}->{action}: '$self->{__logger}->{caller}' (mixed into '$self->{__logger}->{classname}'): ($self->{__logger}->{options}->{tag}) - "; | 
| 208 | } | 
| 209 |  | 
| 210 | sub __get_prefix_short { | 
| 211 | my $self = shift; | 
| 212 | my $tag_append = ''; | 
| 213 | $tag_append = "$self->{__logger}->{options}->{tag} - " if $self->{__logger}->{options}->{tag}; | 
| 214 | return $self->{__logger}->{classname} . "->$self->{__logger}->{action}: $tag_append"; | 
| 215 | } | 
| 216 |  | 
| 217 | sub __get_prefix_module { | 
| 218 | my $self = shift; | 
| 219 | my $tag_append = ''; | 
| 220 | $tag_append = "$self->{__logger}->{options}->{tag} - " if $self->{__logger}->{options}->{tag}; | 
| 221 | return $self->{__logger}->{classname} . ": $tag_append"; | 
| 222 | } | 
| 223 |  | 
| 224 |  | 
| 225 | sub __get_prefix_best { | 
| 226 | my $self = shift; | 
| 227 | my $namespace = $self->{__logger}->{classname}; | 
| 228 | my $method = ''; | 
| 229 | my $tag_append = ''; | 
| 230 | #$namespace = $namespace . ": " if $namespace; | 
| 231 | $method = "->" . $self->{__logger}->{functionname} if $self->{__logger}->{functionname}; | 
| 232 | #$method .= ': ' if $method; | 
| 233 | $tag_append = "$self->{__logger}->{options}->{tag} - " if $self->{__logger}->{options}->{tag}; | 
| 234 |  | 
| 235 | my @line; | 
| 236 | push @line, $namespace if $namespace; | 
| 237 | push @line, $method if $method; | 
| 238 | push @line, $tag_append if $tag_append; | 
| 239 |  | 
| 240 | #print Dumper(@line); | 
| 241 |  | 
| 242 | #return join(': ', @line); | 
| 243 | my $result = join('', @line); | 
| 244 | $result .= ': ' if $result; | 
| 245 |  | 
| 246 | return $result; | 
| 247 |  | 
| 248 | } | 
| 249 |  | 
| 250 |  | 
| 251 | sub log { | 
| 252 | my $self = shift; | 
| 253 | my $message = shift; | 
| 254 |  | 
| 255 | # automagically instantiate the object if not there | 
| 256 | # TODO: do this by creating a dummy object inheriting from DesignPattern::Logger, | 
| 257 | # for now this just don't work because which dummy one to take: Data::Code::Null? | 
| 258 | #if (! ref $self) { | 
| 259 | #  $self = DesignPattern::Object->fromPackage('DesignPattern::Logger'); | 
| 260 | #} | 
| 261 |  | 
| 262 | $self->{__logger}->{level} = shift;    # TODO: implement propagation of this into Log::Dispatch-object '$logger'! | 
| 263 | $self->{__logger}->{options} = shift;   # TODO: implement additional behaviours: e.g. { exit => 1 } would exit after tracedump | 
| 264 |  | 
| 265 | #$self->__init('log', $message); | 
| 266 | $self->__init('log', $message); | 
| 267 | $self->__out(); | 
| 268 |  | 
| 269 | } | 
| 270 |  | 
| 271 | sub skip { | 
| 272 | my $self = shift; | 
| 273 | my $message = shift; | 
| 274 | my $condition = shift; | 
| 275 | $self->{__logger}->{level} = shift;    # TODO: implement propagation of this into Log::Dispatch-object '$logger'! | 
| 276 | $self->{__logger}->{options} = shift;   # TODO: implement additional behaviours: e.g. { exit => 1 } would exit after tracedump | 
| 277 |  | 
| 278 | my $classname = ref $self; | 
| 279 | my $caller = caller; | 
| 280 |  | 
| 281 | $self->{__logger}->{options}->{tag} ||= ''; | 
| 282 |  | 
| 283 | if ($condition) { | 
| 284 | # V1: | 
| 285 | #$logger->warning( __PACKAGE__ . "->_skip: '$caller' (mixed into '$classname'): ($options->{tag}) $message."); | 
| 286 | # V2: | 
| 287 | $self->__init('skip', $message); | 
| 288 | $self->__out(); | 
| 289 | return 1; | 
| 290 | } | 
| 291 | } | 
| 292 |  | 
| 293 | sub trace { | 
| 294 | my $self = shift; | 
| 295 | my $message = shift; | 
| 296 | my $data = shift; | 
| 297 | my $condition = shift; | 
| 298 | $self->{__logger}->{level} = shift;    # TODO: implement propagation of this into Log::Dispatch-object '$logger'! | 
| 299 | $self->{__logger}->{options} = shift;   # TODO: implement additional behaviours: e.g. { exit => 1 } would exit after tracedump | 
| 300 |  | 
| 301 | my $classname = ref $self; | 
| 302 | my $caller = caller; | 
| 303 |  | 
| 304 | $self->{__logger}->{options}->{tag} ||= ''; | 
| 305 |  | 
| 306 | # don't do _any_ tracing if disabled | 
| 307 | return if $self->{TRACE_DISABLED}; | 
| 308 |  | 
| 309 | # overwrite condition with local configured want-TRACE | 
| 310 | $condition ||= $self->{TRACE}; | 
| 311 |  | 
| 312 | # dump data | 
| 313 | my $result = 0; | 
| 314 | if ($condition) { | 
| 315 | $message .= " - " . Dumper($data); | 
| 316 |  | 
| 317 | # V1: | 
| 318 | # $self->__init('trace'); | 
| 319 | # $logger->info( __PACKAGE__ . "->trace: '$caller' (mixed into '$classname'): ($options->{tag}) $message."); | 
| 320 |  | 
| 321 | # V2: | 
| 322 | $self->__init('trace', $message); | 
| 323 | $self->__out(); | 
| 324 |  | 
| 325 | $result = 1; | 
| 326 | } else { | 
| 327 | $result = 0; | 
| 328 | } | 
| 329 |  | 
| 330 | # handle additional options | 
| 331 | if ($self->{TRACE_OPTIONS}) { | 
| 332 | exit if $self->{__logger}->{options}->{exit}; | 
| 333 | } | 
| 334 |  | 
| 335 | return $result; | 
| 336 | } | 
| 337 |  | 
| 338 |  | 
| 339 | sub _configure_LogDispatch { | 
| 340 | my $self = shift; | 
| 341 |  | 
| 342 | my $configurator = DesignPattern::Object->fromPackage('Log::Dispatch::Configurator::Hardwired'); | 
| 343 |  | 
| 344 | # check if running inside a 'PROJECT' space | 
| 345 | my $basepath = cwd(); | 
| 346 | my $postpone = '/..'; | 
| 347 | if (-d $basepath . $postpone . '/PROJECT') { | 
| 348 | $basepath .= $postpone; | 
| 349 | $basepath .= "/var/log"; | 
| 350 | } | 
| 351 |  | 
| 352 | # configure custom logHandler | 
| 353 |  | 
| 354 | if ($configurator) { | 
| 355 |  | 
| 356 | $configurator->{file_filename} = "$basepath/perl_app.log"; | 
| 357 | $configurator->{screen_min_level} = 'info'; | 
| 358 |  | 
| 359 | if ($self->{quiet}) { | 
| 360 | $configurator->{screen_min_level} = 8; | 
| 361 | } | 
| 362 |  | 
| 363 | if ($self->{debug}) { | 
| 364 | $configurator->{screen_min_level} = 'debug'; | 
| 365 | } | 
| 366 |  | 
| 367 | if ($self->{LOG_VERBOSE}) { | 
| 368 | $configurator->{verbose} = 1; | 
| 369 | } | 
| 370 |  | 
| 371 | Log::Dispatch::Config->configure($configurator); | 
| 372 | $self->{__logger}->{instance} = Log::Dispatch::Config->instance; | 
| 373 |  | 
| 374 | } else { | 
| 375 |  | 
| 376 | #Log::Dispatch::Config->configure(); | 
| 377 | #Log::Dispatch->configure(); | 
| 378 | #$self->{__logger}->{instance} = Log::Dispatch->new; | 
| 379 | $self->{__logger}->{instance} = Log::Dispatch::Screen->new( name => 'screen', 'min_level' => 'debug', stderr => 1 ); | 
| 380 |  | 
| 381 | } | 
| 382 |  | 
| 383 |  | 
| 384 | } | 
| 385 |  | 
| 386 | sub configure_logger { | 
| 387 | my $self = shift; | 
| 388 |  | 
| 389 | # automagically instantiate the object if not there | 
| 390 | # TODO: do this by creating a dummy object inheriting from DesignPattern::Logger, | 
| 391 | # for now this just don't work because which dummy one to take: Data::Code::Null? | 
| 392 | if (! ref $self) { | 
| 393 | $self = DesignPattern::Object->fromPackage('DesignPattern::Logger'); | 
| 394 | } | 
| 395 |  | 
| 396 | #print "logconfig\n"; | 
| 397 | $self->_configure_LogDispatch(); | 
| 398 | } | 
| 399 |  | 
| 400 |  | 
| 401 |  | 
| 402 | # for standalone use | 
| 403 | sub new { my $class = shift; my $self = {}; bless $self, $class; } | 
| 404 |  | 
| 405 | # convenient shortcuts | 
| 406 | # FIXME: create these automagically by mungling with symbolic references | 
| 407 | # or: make this object tied to recieve kinda events on method calls | 
| 408 |  | 
| 409 | sub debug { | 
| 410 | my $self = shift; | 
| 411 | my $message = shift; | 
| 412 | $self->log($message, 'debug'); | 
| 413 | } | 
| 414 |  | 
| 415 | sub warning { | 
| 416 | my $self = shift; | 
| 417 | my $message = shift; | 
| 418 | $self->log($message, 'warning'); | 
| 419 | } | 
| 420 |  | 
| 421 | sub info { | 
| 422 | my $self = shift; | 
| 423 | my $message = shift; | 
| 424 | $self->log($message, 'info'); | 
| 425 | } | 
| 426 |  | 
| 427 | sub notice { | 
| 428 | my $self = shift; | 
| 429 | my $message = shift; | 
| 430 | $self->log($message, 'notice'); | 
| 431 | } | 
| 432 |  | 
| 433 |  | 
| 434 |  | 
| 435 | =pod | 
| 436 |  | 
| 437 | =head1 TODO | 
| 438 |  | 
| 439 | o $TRACE_STACK_LEVELS   (traces stack history) | 
| 440 | o $ENABLE_DEBUG   (en-/disables level 'debug') | 
| 441 | o $ENABLE_LOG   (en-/disables logging completely) | 
| 442 |  | 
| 443 |  | 
| 444 | =cut | 
| 445 |  | 
| 446 |  | 
| 447 | 1; | 
| 448 | __END__ |