|
From: Jim C. <jc...@di...> - 2003-12-30 03:27:48
|
Pete Siemsen wrote:
>I'd like log4perl to indent messages to show caller depth in the call
>stack. A sample of this style is
>
> main: starting
> function1: called with arg1, arg2
> function1: returning value1
> function2: called
> function3: called
> function4: called with arg4
> function4: returning
> function3: returning
> function2: returning
> main: ending
>
>For years, I've been doing this by passing around a scalar named
>$CallDepth, and incrementing/decrementing it at function entry/exit. I
>pass around a second scalar named $DbgLevel that works sorta like the
>levels in log4perl, but with more levels.
>
I have several reactions:
1. Not gonna happen - using caller() on every invocation would bethe
only way to get stack depth,
and is horrifically expensive for an application-wide logging facility.
I once proposed that
caller(1) be used to provide more info to filtering operations, and that
was deemed infeasable
due to efficiency concerns. Your idea would require N times as many
(log2(N) if youre clever)
uses of caller in order to get the depth of the stack.
2. I didnt really like that answer, but it was true. So I went digging
into optimizer.pm, and eventually
wrote Log::Log4perl::Autocategorize. In this package, AUTOLOAD catches
all logging calls,
uses caller() to figure out who called, consults L4perl to determine
whether to log it, and does so.
my $predicate = "is_$level";
eval { $runnable = $log->$predicate() };
Now, heres the tricky part - because I used optimizer to munge the
method-name into a new, unique
name (ex: from debug to debug_$somevalue), I could create a custom
subroutine to do the right thing
FOR THAT CALL-POINT. Thereafter - calls to that subroutine (ie FROM
THAT CALLER)
were handled at full-speed, and with full customized behavior.
It would not be hard to extend to accommodate your desires, WITH SOME
CAVEATS
a. Depth must be perfectly correlated to the invoker.
The simplest explanation is by way of a counter-example: a
debug_print() utility would likely be
used from dozens of places, and from different depths of the code.
These differences would
not be seen by my package, since the logging call is coming from
debug_print, not from its
many users. To amplify this point - indent-level could easily be wong,
and undetectably so.
b. L4P has no way to express filtering based on call-stack.
I extended the meaning of 'logging category' to carry all the info that
caller() provides,
thus you can filter on class, subroutine, line number.
log4perl.category.Frob.nicate.debug = INFO # 2
log4perl.category.Frob.nicate.debug.118 = DEBUG # 3
#log4perl.category.Frob.Vernier.twiddle.debug = DEBUG # 4
But this runs out of gas after 1st caller level. Theres no
obvious/clean way to extend this
to operate on results of caller(N). I discuss some options in my POD,
please read there,
but heres a strawman:
log4perl.caller(2).Frob.nicate.debug = INFO # 2
There are problems with this - its hard (impossibl?) to know whether
is_debug()
says yes/no due to an exact match or a bubble-up match, hence its hard
to know
whether a level-N filter is even pertinent. and it would be silly to
invoke caller(42)
to check for filters speced at that level - on average, there wont be.
c. your idea could/should extend to nested lexical scopes within
subroutines, but
thats a whole nuther can of worms.
d. Im unwilling to extend Autocat in ways that appear to be incompatible
with L4P.
And since L4P strives to be compatible with L4J, Id expect some
reluctance there to
do whats needed to the configs. Hopefully L4J has reserved some part
of the config-space
to accomodate special needs/features (such as 'log4perl.caller(2)' above)
3. Have you looked at Hook::Scope ? Its more deep magic that I dont
understand well,
but it can hang/hook actions on scope entry/exit that you could use in a
wrapper class.
4. IIRC, Log::Agent can represent enter,exits via indentation
WRT
The $caller_level variable is especially intriguing, as it's *almost*
what I need. Instead of being "how many levels back up the call stack
you have to go to find the caller", which isn't very useful, I want it
to be "how many levels back up the call stack you have to go to find the
do you mean caller_depth ? If so, that doesnt mean what you think it means.
Basically, it allows a wrapper class to tell L4P that "Im just a
middleman!, caller(N) is
the end-customer". The levels up to main is a run-time entity - its
always changing.
hope this helps, and creates discussion..
|