76edd8257b93193bc18daa3f172ea79dcb300d37
[kivitendo-erp.git] / SL / LXDebug.pm
1 package LXDebug;
2
3 use constant NONE               => 0;
4 use constant INFO               => 1;
5 use constant DEBUG1             => 2;
6 use constant DEBUG2             => 4;
7 use constant QUERY              => 8;
8 use constant TRACE              => 16;
9 use constant BACKTRACE_ON_ERROR => 32;
10 use constant ALL                => 63;
11
12 use constant FILE_TARGET   => 0;
13 use constant STDERR_TARGET => 1;
14
15 use POSIX qw(strftime);
16
17 my $data_dumper_available;
18
19 our $global_level;
20 our $watch_form;
21
22 BEGIN {
23   eval("use Data::Dumper");
24   $data_dumper_available = $@ ? 0 : 1;
25
26   $global_level      = NONE;
27   $watch_form        = 0;
28 }
29
30 sub new {
31   my $type = shift;
32   my $self = {};
33
34   $self->{"calldepth"}  = 0;
35   $self->{"file"}       = "/tmp/lx-office-debug.log";
36   $self->{"target"}     = FILE_TARGET;
37   $self->{"level"}      = 0;
38
39   while ($_[0]) {
40     $self->{ $_[0] } = $_[1];
41     shift;
42     shift;
43   }
44
45   bless($self, $type);
46 }
47
48 sub set_target {
49   my ($self, $target, $file) = @_;
50
51   if ((FILE_TARGET == $target) && $file) {
52     $self->{"file"}   = $file;
53     $self->{"target"} = FILE_TARGET;
54
55   } elsif (STDERR_TARGET == $target) {
56     $self->{"target"} = STDERR_TARGET;
57   }
58 }
59
60 sub enter_sub {
61   my ($self, $level) = @_;
62   $level *= 1;
63
64   return 1 unless ($global_level & TRACE);          # ignore if traces aren't active
65   return 1 if $level && !($global_level & $level);  # ignore if level of trace isn't active
66
67   my ($package, $filename, $line, $subroutine) = caller(1);
68   my ($dummy1, $self_filename, $self_line) = caller(0);
69
70   my $indent = " " x $self->{"calldepth"}++;
71
72   if (!defined($package)) {
73     $self->_write('sub' . $level, $indent . "\\ top-level?\n");
74   } else {
75     $self->_write('sub' . $level, $indent
76                     . "\\ ${subroutine} in "
77                     . "${self_filename}:${self_line} called from "
78                     . "${filename}:${line}\n");
79   }
80   return 1;
81 }
82
83 sub leave_sub {
84   my ($self, $level) = @_;
85   $level *= 1;
86
87   return 1 unless ($global_level & TRACE);           # ignore if traces aren't active
88   return 1 if $level && !($global_level & $level);   # ignore if level of trace isn't active
89
90   my ($package, $filename, $line, $subroutine) = caller(1);
91   my ($dummy1, $self_filename, $self_line) = caller(0);
92
93   my $indent = " " x --$self->{"calldepth"};
94
95   if (!defined($package)) {
96     $self->_write('sub' . $level, $indent . "/ top-level?\n");
97   } else {
98     $self->_write('sub' . $level, $indent . "/ ${subroutine} in " . "${self_filename}:${self_line}\n");
99   }
100   return 1;
101 }
102
103 sub show_backtrace {
104   my ($self) = @_;
105
106   return 1 unless ($global_level & BACKTRACE_ON_ERROR);
107
108   $self->message(BACKTRACE_ON_ERROR, "Starting full caller dump:");
109   my $level = 0;
110   while (my ($dummy, $filename, $line, $subroutine) = caller $level) {
111     $self->message(BACKTRACE_ON_ERROR, "${subroutine} from ${filename}:${line}");
112     $level++;
113   }
114
115   return 1;
116 }
117
118 sub message {
119   my ($self, $level, $message) = @_;
120
121   $self->_write(level2string($level), $message) if (($self->{"level"} | $global_level) & $level || !$level);
122 }
123
124 sub dump {
125   my ($self, $level, $name, $variable) = @_;
126
127   if ($data_dumper_available) {
128     $self->message($level, "dumping ${name}:\n" . Dumper($variable));
129   } else {
130     $self->message($level,
131                    "dumping ${name}: Data::Dumper not available; "
132                      . "variable cannot be dumped");
133   }
134 }
135
136 sub enable_sub_tracing {
137   my ($self) = @_;
138   $self->{level} | TRACE;
139 }
140
141 sub disable_sub_tracing {
142   my ($self) = @_;
143   $self->{level} & ~ TRACE;
144 }
145
146 sub _write {
147   my ($self, $prefix, $message) = @_;
148   my $date = strftime("%Y-%m-%d %H:%M:%S $$ ${prefix}: ", localtime(time()));
149   local *FILE;
150
151   chomp($message);
152
153   if ((FILE_TARGET == $self->{"target"})
154       && open(FILE, ">>" . $self->{"file"})) {
155     print(FILE "${date}${message}\n");
156     close(FILE);
157
158   } elsif (STDERR_TARGET == $self->{"target"}) {
159     print(STDERR "${date}${message}\n");
160   }
161 }
162
163 sub level2string {
164   # use $_[0] as a bit mask and return levelstrings separated by /
165   join '/', qw(info debug1 debug2 query trace error_call_trace)[ grep { (reverse split //, sprintf "%05b", $_[0])[$_] } 0..5 ]
166 }
167
168 1;