File Coverage

blib/lib/Carp/Datum/Cfg.pm
Criterion Covered Total %
statement 75 99 75.7
branch 21 40 52.5
condition 3 6 50.0
subroutine 15 15 100.0
pod 0 9 0.0
total 114 169 67.4


line stmt bran cond sub pod time code
1             # -*- Mode: perl -*-
2             #
3             # $Id: Cfg.pm,v 0.1.1.1 2001/07/13 17:05:28 ram Exp $
4             #
5             # Copyright (c) 2000-2001, Christophe Dehaudt & Raphael Manfredi
6             #
7             # You may redistribute only under the terms of the Artistic License,
8             # as specified in the README file that comes with the distribution.
9             #
10             # HISTORY
11             # $Log: Cfg.pm,v $
12             # Revision 0.1.1.1 2001/07/13 17:05:28 ram
13             # patch2: random cleanup (from CDE)
14             #
15             # Revision 0.1 2001/03/31 10:04:36 ram
16             # Baseline for first Alpha release.
17             #
18             # $EndLog$
19             #
20              
21 5     5   28 use strict;
  5         12  
  5         254  
22              
23             package Carp::Datum::Cfg;
24              
25 5     5   28 use Carp::Datum::Flags;
  5         9  
  5         914  
26              
27 5     5   27 use Getargs::Long qw(ignorecase);
  5         11  
  5         48  
28              
29             require Exporter;
30 5     5   1317 use vars qw(@ISA @EXPORT);
  5         9  
  5         404  
31             @ISA = qw(Exporter);
32             @EXPORT = (qw(
33             ),
34             @Carp::Datum::Flags::EXPORT);
35              
36 5     5   25 use vars qw($DEBUG_TABLE);
  5         9  
  5         4151  
37              
38             #
39             # Structure of the hash ref that is returned by the parser:
40             #
41             # FLAG_SETTING:
42             # { debug => [ DTM_SET, DTM_CLEAR ],
43             # trace => [ DTM_SET, DTM_CLEAR ],
44             # args => VAL
45             # }
46             #
47             # debug and trace correspond to a two values array. First value is the
48             # set mask and the second is the clear one.
49             #
50             # args indicates the maximum number of arguments that is printed
51             # during the tracing of the flow. -1 means all arguments.
52             #
53             #
54             # DEBUG_TABLE:
55             # { default => FLAG_SETTING,
56             #
57             # routine => { "routine_name1" => FLAG_SETTING,
58             # "routine_name2" => FLAG_SETTING,
59             # ....
60             # },
61             #
62             # file => { flags => { "path1" => FLAG_SETTING,
63             # "path2" => FLAG_SETTING,
64             # ....
65             # },
66             # routine => { "routine_name1" => FLAG_SETTING,
67             # "routine_name2" => FLAG_SETTING,
68             # ....
69             # }
70             # },
71             #
72             # type => { flags => { "type1" => FLAG_SETTING,
73             # "type2" => FLAG_SETTING,
74             # ....
75             # },
76             # routine => { "routine_name1" => FLAG_SETTING,
77             # "routine_name2" => FLAG_SETTING,
78             # ....
79             # }
80             # },
81             #
82             # alias => [ [ "path1", "alias1" ],
83             # [ "path2", "alias2" ],
84             # ....
85             # ],
86             #
87             # define => { "name1" => FLAG_SETTING,
88             # "name2" => FLAG_SETTING,
89             # ....
90             # }
91             # }
92             #
93             #
94              
95              
96              
97             # default debug table
98             $DEBUG_TABLE = {default => { debug => [DBG_ALL, 0],
99             trace => [TRC_ALL, 0],
100             args => -1
101             },
102             alias => []
103             };
104              
105             #
106             # ->make
107             #
108             #
109             # Arguments:
110             # -file => $filename: file to load [optionnal]
111             # -config => $string: string which contains config set up [optionnal]
112             #
113             sub make {
114 5     5 0 21 my $self = bless {}, shift;
115 5         30 my ($filename, $raw_config) = cgetargs(@_, [qw(file config)]);
116              
117 5         5581 $self->{cfg_table} = $DEBUG_TABLE;
118 5         14 local $_ = '';
119            
120 5 50 33     35 if (defined $filename && open(XFILE, $filename)) {
121            
122 0         0 $_ = "\n" . join('', );
123 0 0       0 die $@ if $@;
124 0         0 close XFILE;
125             }
126              
127 5 50       25 if (defined $raw_config) {
128 0         0 $_ .= "\n" . $raw_config;
129 0 0       0 $filename .= " + " if defined $filename;
130 0         0 $filename .= "'RAW DATA CONFIGURATION'";
131             }
132              
133             # to prevent the parsing when the given parameter is a fake
134             # filename, there is a test on the string to parse. It must
135             # contain a blank character to possibly be parsed. A non existing
136             # path will not contain this character.
137 5 50       18 if (/\s/) {
138             # use the parser to populate the debug tree structure
139 0         0 my $p = Carp::Datum::Parser->new(\&Carp::Datum::Parser::yylex,
140             \&Carp::Datum::Parser::yyerror, 0);
141 0         0 $p->init_parser($filename);
142 0         0 my $result = $p->yyparse();
143            
144             # add the default values to the result if they have not been
145             # set during the parsing
146 0         0 while (my ($k, $v) = each %$DEBUG_TABLE) {
147 0 0       0 $result->{$k} = $v unless defined $result->{$k};
148             }
149            
150 0         0 $self->{cfg_table} = $result;
151             }
152              
153             # separate the result in different attibutes to speed-up the
154             # processing (one dereference is saved). That is also beautifying
155             # the code.
156 5         20 $self->{cfg_file} = $self->cfg_table->{file};
157 5         16 $self->{cfg_routine} = $self->cfg_table->{routine};
158 5         14 $self->{cfg_cluster} = $self->cfg_table->{cluster};
159 5         15 $self->{cfg_type} = $self->cfg_table->{type};
160 5         15 $self->{cfg_alias} = $self->cfg_table->{alias};
161              
162 5         23 return $self;
163             }
164              
165              
166             #########################################################################
167             # Internal Attribute Access: these methods are not intended to be used #
168             # from the external of the object. #
169             #########################################################################
170              
171 48     48 0 192 sub cfg_table {$_[0]->{cfg_table}}
172 16     16 0 73 sub cfg_alias {$_[0]->{cfg_alias}}
173              
174             #
175             # ->basename
176             #
177             sub basename {
178 23     23 0 29 my $name = shift;
179 23         28 my $result = $name;
180              
181 23 50       69 if ($name =~ /\//) {
182 23         99 ($result) = $name =~ /.*\/(\S+)/;
183             }
184 23         48 return $result;
185             }
186              
187              
188             #
189             # ->add_flag
190             #
191             # static class function that is used by the flag routine when additive
192             # method is requested for flag computation.
193             #
194             # Arguments:
195             # $old: old value,
196             # $new: new value (can be undef or null)
197             #
198             # Returns:
199             # the clear bits of new are cleared on old and set bits of new are
200             # set on old.
201             #
202             sub add_flag {
203 90     90 0 138 my ($old, $new) = @_;
204              
205 90 100 66     239 if (defined $new && $new != 0) {
206 18         61 return $old & ~$new->[DTM_CLEAR] | $new->[DTM_SET];
207             }
208 72         107 return $old;
209             }
210              
211             #
212             # ->add_args
213             #
214             # static class function that is used by the flag routine when replacing
215             # method is requested for flag computation.
216             #
217             # Arguments:
218             # $old: old value,
219             # $new: new value (can be undef or null)
220             #
221             # Returns:
222             # the new value if defined
223             #
224             sub add_args {
225 25     25 0 42 my ($old, $new) = @_;
226              
227 25 100       67 return $old unless defined $new;
228 5         11 return $new;
229             }
230              
231             #########################################################################
232             # Class Feature: usable from the external world #
233             #########################################################################
234              
235              
236             #
237             # ->check_debug
238             #
239             # return true when the given mask matches the flag setting for debug
240             # mode
241             #
242             # Arguments:
243             # $mask: bit field that is compared to the setting.
244             #
245             # $caller_penalty: [optional] allows to provide a penalty used to
246             # determine the function features (via caller()) that is used to get
247             # the configuration setting. When not specified or 0, the call level
248             # right above the function that call the check_debug (2 steps from
249             # here) will be used.
250             #
251             # Returns:
252             # a boolean value.
253             #
254             sub check_debug {
255 12 50   12 0 68 return $_[0]->flag('debug', @_ == 3 ? ($_[2]+1) : 1) & $_[1];
256             }
257              
258             #
259             # ->check_trace
260             #
261             # return true when the given mask matches the flag setting for trace
262             # mode
263             #
264             # Arguments:
265             # $mask: bit field that is compared to the setting.
266             #
267             # $caller_penalty: [optional] allows to provide a penalty used to
268             # determine the function features (via caller()) that is used to get
269             # the configuration setting. When not specified or 0, the call level
270             # right above the function that call the check_trace (2 steps from
271             # here) will be used.
272             #
273             # Returns:
274             # a boolean value.
275             #
276             sub check_trace {
277 2 50   2 0 11 return $_[0]->flag('trace', @_ == 3 ? ($_[2]+1) : 1) & $_[1];
278             }
279              
280              
281             #
282             # ->flag
283             #
284             # Perform a walkthrough the different level of configuration setting
285             # and and gets a (additive | replacing) value for the result computation.
286             #
287             # When requesting the flag for 'debug' or 'trace', each stage value is
288             # added. For 'args' request, each value overwrites the previous one.
289             #
290             # The walkthrough is perfomed in the following order:
291             # - default
292             # - file
293             # - routine
294             # - routine for file
295             # - type
296             # - routine for type
297             #
298             # Arguments:
299             # $field: string that indicates the key that is used during the
300             # walkthrough. It is either 'debug', 'trace' or 'args'.
301             #
302             # $caller_penalty: [optional] allows to provide a penalty used to
303             # determine the function features (via caller()) that is used to get
304             # the configuration setting. When not specified or 0, the call level
305             # right above the function that call the check_trace (2 steps from
306             # here) will be used.
307             #
308             # Returns:
309             # a value that depends from the $field request:
310             # for 'debug' and 'trace': it represents a bit field.
311             # for 'args': it is an integer..
312             #
313             sub flag {
314 23     23 0 82 my $self = shift;
315 23         33 my ($field, $caller_penalty) = @_;
316              
317             # get debug caller (for filename location)
318 23 100       56 my $caller_level = defined $caller_penalty ? (1 + $caller_penalty) : 1;
319 23         110 my ($package, $filename, $line1) = caller($caller_level);
320              
321             # get debug caller (for routine name)
322             package DB;
323 5     5   33 use vars qw(@args); # ignore warning
  5         7  
  5         2651  
324 23         122 my ($package1, $filename1, $line, $subroutine,
325             $hasargs, $wantarray, $evaltext, $is_require) =
326             caller($caller_level + 1);
327             package Carp::Datum::Cfg;
328              
329             # the method that is gonna used to compute the different flag
330             # depends of what it is looked for:
331             # 'debug' or 'trace' -> flags are merged during the walkthrough
332             # 'args' -> value are overwritten during the walkthough
333 23         48 my $merge_routine = \&add_flag;
334 23 100       64 $merge_routine = \&add_args if $field eq 'args';
335              
336 23 100       55 $subroutine = '' unless defined $subroutine;
337 23         101 my ($func_name) = $subroutine =~ /.*::(\S+)/;
338 23         35 my $file_routine = undef;
339 23         30 my $type_routine = undef;
340              
341             # first get the default flag setting
342 23         57 my $result = &$merge_routine(0, $self->cfg_table->{default}->{$field});
343              
344             # update with cluster setting
345 23         37 my $cluster_cfg = $self->{cfg_cluster};
346 23 50       51 if (defined $cluster_cfg) {
347             # perhaps, the package gets directly an entry in the table
348 0 0       0 if (defined $cluster_cfg->{$package}) {
349 0         0 $result = &$merge_routine(
350             $result,
351             $cluster_cfg->{$package}->{flags}->{$field}
352             );
353             }
354             else {
355             # anyway, try to find a filter matching a part of the package name
356 0         0 my $tmp = $package;
357 0         0 while ($tmp =~ /(.*)::/) {
358 0         0 $tmp = $1;
359 0 0       0 if (defined $cluster_cfg->{$tmp}) {
360 0         0 $result = &$merge_routine(
361             $result,
362             $cluster_cfg->{$tmp}->{flags}->{$field}
363             );
364 0         0 last;
365             }
366              
367             };
368             }
369             }
370              
371             # update with file specific setting (if any), trying base name second
372 23         42 my $file_cfg = $self->{cfg_file}->{$filename};
373 23 50       43 if (defined $file_cfg) {
374 0         0 $result = &$merge_routine($result, $file_cfg->{flags}->{$field});
375 0         0 $file_routine = $file_cfg->{routine}->{$func_name};
376             }
377             else {
378 23         55 $file_cfg = $self->{cfg_file}->{basename($filename)};
379 23 50       57 if (defined $file_cfg) {
380 0         0 $result = &$merge_routine($result, $file_cfg->{flags}->{$field});
381 0         0 $file_routine = $file_cfg->{routine}->{$func_name};
382             }
383             }
384            
385             # update with routine specific setting (if any)
386 23         41 my $routine_cfg = $self->{cfg_routine}->{$func_name};
387 23         93 $result = &$merge_routine($result, $routine_cfg->{flags}->{$field});
388            
389             # update with routine specific setting from file specification (if any)
390 23         96 $result = &$merge_routine($result, $file_routine->{flags}->{$field});
391            
392             # update with dynamic type specific setting (if any)
393 23         39 my $dyna_type = '';
394 23 100       67 ($dyna_type) = $DB::args[0] =~ /(.*)=\w+\(.*\)/ if defined $DB::args[0];
395 23         45 my $dyna_cfg = $self->{cfg_type}->{$dyna_type};
396 23         79 $result = &$merge_routine($result, $dyna_cfg->{flags}->{$field});
397              
398             # update with routine specific setting from type specification (if any)
399 23         57 $type_routine = $dyna_cfg->{routine}->{$func_name};
400 23         93 $result = &$merge_routine($result, $type_routine->{flags}->{$field});
401              
402 23         235 return $result;
403             }
404              
405             1;
406              
407             =head1 NAME
408              
409             Carp::Datum::Cfg - Dynamic Debug Configuration Setting for Datum
410              
411             =head1 SYNOPSIS
412              
413             # In application's main
414             use Carp::Datum qw(:all on); # turns Datum "on" or "off"
415             DLOAD_CONFIG(-file => "./debug.cf", -config => "config string");
416              
417             =head1 DESCRIPTION
418              
419             By using the DLOAD_CONFIG function in an application's main file,
420             a debugging configuration can be dynamically loaded to define a particular
421             level of debug/trace flags for a specific sub-part of code.
422              
423             For instance, the tracing can be turned off when entering a routine
424             of a designated package. That is very useful for concentrating the
425             debugging onto the area that is presently developed and/or to filter
426             some verbose parts of code (recursive function call), when they don't
427             need to be monitored to fix the problem.
428              
429             =head1 EXAMPLE
430              
431             Before the obscure explaination of the grammar, here is an example of
432             what can be specified by dynamic configuration:
433              
434             /*
435             * flags definition: macro that can be used in further configuration
436             * settings
437             */
438             flags common {
439             all(yes);
440             trace(yes): all;
441             }
442              
443             flags silent {
444             all(yes);
445             flow(no);
446             trace(no);
447             return(no);
448             }
449              
450             /*
451             * default setting to use when there is no specific setting
452             * for the area
453             */
454             default common;
455              
456              
457             /*
458             * specific settings for specific areas
459             */
460             routine "context", "cleanup" { use silent; }
461             routine "validate", "is_num", "is_greater" { use silent; }
462              
463             file "Keyed_Tree.pm" { use silent; }
464             file "Color.pm" {
465             use silent;
466             trace(yes): emergency, alert, critical;
467             }
468              
469             cluster "CGI::MxScreen" {
470             use silent;
471             assert(no);
472             ensure(no);
473             }
474              
475             /*
476             * aliasing to reduce the trace output line length
477             */
478              
479             alias "/home/dehaudtc/usr/perl/lib/site_perl/5.6.0/CGI" => "";
480              
481             =head1 INTERFACE
482              
483             The only user interface is the C routine, which expects
484             the following optional named parameters:
485              
486             =over 4
487              
488             =item C<-config> => I
489              
490             Give an inlined configuration string that is appended to the one
491             defined by C<-file>, if any.
492              
493             =item C<-file> => I
494              
495             Specifies the configuration file to load to initialize the
496             debugging and tracing flags to be used for this run.
497              
498             =back
499              
500             =head1 CONFIGURATION DIRECTIVES
501              
502             =head2 Main Configuration Directives
503              
504             The following main directives can appear at a nesting level of 0. The
505             syntax unit known as I is a list of semi-colon terminated directives
506             held within curly braces.
507              
508             =over 4
509              
510             =item C I => I
511              
512             Defines an alias to be used during tracing. The I string
513             is replaced by the I in the logs.
514              
515             For instance, given:
516              
517             alias "/home/dehaudtc/lib/CGI" => "";
518              
519             then a trace for file C would be
520             traced as coming from file CCGIE/Carp.pm>, which is nicer to read.
521              
522             =item C I, I I
523              
524             The I defines the flags to be applied to all named clusters.
525             A cluster is a set of classes under a given name scope.
526             Cluster names are given by strings within double quotes, as in:
527              
528             cluster "CGI::MxScreen", "Net::MsgLink" { use silent; }
529              
530             This would apply to all classes under the "CGI::MxScreen" or "Net::MsgLink"
531             name scopes, i.e. C would be affected.
532              
533             An exact match is attempted first, i.e. saying:
534              
535             cluster "CGI::MxScreen" { use verbose; }
536             cluster "CGI::MxScreen::Screen" { use silent; }
537              
538             would apply the I flags for C but the I
539             ones to C.
540              
541             =item C I|I.
542              
543             Specifies the default flags that should apply. The default flags can be
544             given by providing the I of flags, defined by the C directive,
545             or by expansing them in the following I.
546              
547             For instance:
548              
549             default silent;
550              
551             would say that the flags to apply by default are the ones defined by an
552             earlier C directive. Not expanding defaults allows for
553             quick switching by replacing I with I. It is up to the
554             module user to define what is meant by that though.
555              
556             =item C I, I I
557              
558             The I defines the flags to be applied to all named files.
559             File names are given by strings withing double quotes, as in:
560              
561             file "foo.pm", "bar.pm" { use silent; }
562              
563             This would apply to all files named "foo.pm" or "bar.pm", whatever their
564             directory, i.e. it would apply to C as well as C<../bar.pm>.
565              
566             An exact match is attempted first, i.e. saying:
567              
568             file "foo.pm" { use verbose; }
569             file "/tmp/foo.pm" { use silent; }
570              
571             would apply the I flags for C but the I
572             ones to C<./foo.pm>.
573              
574             =item C I I
575              
576             Define a symbol I whose flags are described by the following I.
577             This I can then be used in C and C directives.
578              
579             For instance:
580              
581             flags common {
582             all(yes);
583             trace(yes): all;
584             }
585              
586             would define the flags known as I, which can then be re-used, as in:
587              
588             flags other {
589             use common; # reuses definiton of common flags
590             panic(no); # but switches off panic, enabled in common
591             }
592              
593             A flag symbol must be defined prior being used.
594              
595             =item C I, I I
596              
597             The I defines the flags to be applied to all named routines.
598             Routine names are given by strings within double quotes, as in:
599              
600             routine "foo", "bar" { use silent; }
601              
602             This would apply to all routines named "foo" or "bar", whatever their package,
603             for instance C and C.
604              
605             =head2 Debugging and Tracing Flags
606              
607             Debugging (and tracing) flags can be specified only within syntactic I
608             items, as expected by main directives such as C or C.
609              
610             Following is a list of debugging flags that can be specified in the
611             configuration. The order in which they are given in the file is significant:
612             the I/I settings are applied sequentially.
613              
614             =over 4
615              
616             =item C I
617              
618             Uses flags defined by a C directive under I. It acts as a
619             recursive macro expansion (since C can also be specified in C).
620             The symbol I must have been defined earlier.
621              
622             =item flow(yes|no)
623              
624             Whether to print out the entering/exiting of routines. That implies the
625             invocation of the C function in the routines.
626              
627             =item return(yes|no)
628              
629             Whether to print out the returned when using the return
630             C and C routines.
631              
632             =item trace(yes|no)
633              
634             Whether to print out traces specified by the C function. By
635             default all trace levels are affected. It may be followed by a list
636             of trace levels affected by the directive, as in.
637              
638             trace(yes): emergency, alert, critical;
639              
640             Trace levels are purely conventional, and have a strict one-to-one mapping
641             with C levels given at the C call. They are further
642             described in L below. There is one bit per defined trace
643             level, contrary to the convention established by syslog(), for better
644             tuning.
645              
646             =item require(yes|no)
647              
648             Whether to evaluate the pre-condition given by C. But see
649             L below.
650              
651             =item assert(yes|no)
652              
653             Whether to evaluate the assertion given by C. But see
654             L below.
655              
656             =item ensure(yes|no)
657              
658             Whether to evaluate the post-condition given by C. But see
659             L below.
660              
661             =item panic(yes|no)
662              
663             Whether to panic upon an assertion failure (pre/post condition or
664             assertion). If not enabled, a simple warning is issued, tracing the
665             assertion failure.
666              
667             =item stack(yes|no)
668              
669             Whether to print out a stack trace upon assertion failure.
670              
671             =item all(yes|no)
672              
673             Enable or disables B the previously described items.
674              
675             =back
676              
677             =head2 Assertion Evaluation Note
678              
679             When C is switched off, the assertions are always monitored,
680             and any failure is fatal. This is because a failing assertion is a Bad Thing
681             in production mode. Also, since C and friends are not
682             C macros but routines, the assertion expression is evaluated anyway, so
683             it might as well be tested.
684              
685             Therefore, a directive like:
686              
687             require(no);
688              
689             will only turn off monitoring of pre-conditions in debugging mode (e.g. because
690             the interface is not finalized, or the clients do not behave properly yet).
691              
692             =head2 Trace Levels
693              
694             Here is the list of trace flags that can be specified by the configuration:
695              
696             Configuration DTRACE flag
697             ------------- -------------
698             all TRC_ALL
699             emergency TRC_EMERGENCY
700             alert TRC_ALERT
701             critical TRC_CRITICAL
702             error TRC_ERROR
703             warning TRC_WARNING
704             notice TRC_NOTICE
705             info TRC_INFO
706             debug TRC_DEBUG
707              
708             A user could say something like:
709              
710             trace(no): all;
711             trace(yes): emergency, alert, critical, error;
712              
713             Since flags are applied in sequence, the first directive turns all tracing
714             flags to off, the second enables only the listed ones.
715              
716             =head1 BUGS
717              
718             Some things are not fully documented.
719              
720             =head1 AUTHORS
721              
722             Christophe Dehaudt and Raphael Manfredi are the original authors.
723              
724             Send bug reports, hints, tips, suggestions to Dave Hoover at .
725              
726             =head1 SEE ALSO
727              
728             Log::Agent(3).
729              
730             =cut
731              
732