]>
Commit | Line | Data |
---|---|---|
c925a6af TC |
1 | package BSE::TB::AuditLog; |
2 | use strict; | |
3 | use Squirrel::Table; | |
4 | use vars qw(@ISA $VERSION); | |
5 | @ISA = qw(Squirrel::Table); | |
6 | use BSE::TB::AuditEntry; | |
13a986ee | 7 | use Scalar::Util qw(blessed); |
c925a6af TC |
8 | |
9 | sub rowClass { | |
10 | return 'BSE::TB::AuditEntry'; | |
11 | } | |
12 | ||
13 | =item log | |
14 | ||
15 | Log a message to the audit log. | |
16 | ||
17 | Required parameters: | |
18 | ||
19 | =over | |
20 | ||
21 | =item * | |
22 | ||
23 | component - either a simple component name like "shop", or colon | |
24 | separated component, module and function. | |
25 | ||
26 | =item * | |
27 | ||
13a986ee TC |
28 | level - level of event, one of emerg, alert, crit, error, warning, |
29 | notice, info, debug. | |
30 | ||
31 | =item * | |
32 | ||
33 | actor - the entity performing the actor, one of a SiteUser object, an | |
34 | AdminUser object, "S" for system, "U" for an unknown public user. | |
35 | ||
36 | =item * | |
37 | ||
38 | msg - a brief message | |
39 | ||
40 | =item * | |
41 | ||
42 | ip_address - the actor's IP address (optional, loaded from $REMOTE_ADDR). | |
43 | ||
44 | =item * | |
45 | ||
46 | object - an optional object being acted upon. | |
47 | ||
48 | =item * | |
49 | ||
50 | dump - an optional dump of debugging data | |
c925a6af TC |
51 | |
52 | =back | |
53 | ||
54 | =cut | |
55 | ||
56 | sub log { | |
57 | my ($class, %opts) = @_; | |
58 | ||
59 | my %entry = | |
60 | ( | |
61 | when_at => BSE::Util::SQL::now_datetime(), | |
62 | ); | |
63 | ||
64 | my $facility = delete $opts{facility} || "bse"; | |
65 | $entry{facility} = $facility; | |
66 | ||
67 | my $component = delete $opts{component} | |
68 | or $class->crash("Missing component"); | |
69 | if ($component =~ /^(\w+):(\w*):(.+)$/) { | |
70 | @entry{qw/component module function/} = ( $1, $2, $3 ); | |
71 | } | |
13a986ee TC |
72 | elsif ($component =~ /^(\w+):(\w*)$/) { |
73 | @entry{qw/component module/} = ( $1, $2 ); | |
74 | $entry{function} = delete $opts{function} || delete $opts{action} | |
75 | or $class->crash("Missing function parameter"); | |
76 | } | |
c925a6af TC |
77 | else { |
78 | $entry{component} = $component; | |
79 | $entry{module} = delete $opts{module} || ''; | |
80 | $entry{function} = delete $opts{function} || delete $opts{action} | |
81 | or $class->crash("Missing function parameter") | |
82 | } | |
83 | ||
84 | my $object = delete $opts{object}; | |
85 | if ($object) { | |
86 | $entry{object_type} = blessed $object; | |
87 | $entry{object_id} = $object->id; | |
88 | } | |
89 | else { | |
90 | $entry{object_type} = undef; | |
91 | $entry{object_id} = undef; | |
92 | } | |
93 | ||
94 | $entry{ip_address} = delete $opts{ip_address} || $ENV{REMOTE_ADDR} || ''; | |
95 | my $level_name = delete $opts{level} || "emerg"; | |
96 | $entry{level} = _level_name_to_id($level_name); | |
97 | ||
98 | my $actor = delete $opts{actor} | |
99 | or $class->crash("No actor supplied"); | |
100 | ||
101 | if (ref $actor) { | |
102 | if ($actor->isa("BSE::TB::AdminUser")) { | |
103 | $entry{actor_type} = "A"; | |
104 | } | |
105 | else { | |
13a986ee | 106 | $entry{actor_type} = "M"; |
c925a6af TC |
107 | } |
108 | $entry{actor_id} = $actor->id; | |
109 | } | |
110 | else { | |
13a986ee TC |
111 | if ($actor =~ /^[US]$/) { |
112 | $entry{actor_type} = $actor; | |
c925a6af TC |
113 | } |
114 | else { | |
13a986ee | 115 | $entry{actor_type} = "E"; |
c925a6af TC |
116 | } |
117 | $entry{actor_id} = undef; | |
118 | } | |
119 | ||
120 | $entry{msg} = delete $opts{msg} | |
121 | or $class->crash("No msg"); | |
122 | $entry{dump} = delete $opts{dump}; | |
123 | ||
124 | my $cfg = BSE::Cfg->single; | |
125 | ||
126 | my $section = "audit log $facility"; | |
127 | unless ($cfg->entry | |
128 | ( | |
129 | $section, join(":", @entry{qw/component module function/}), | |
130 | $cfg->entry | |
131 | ( | |
132 | $section, join(":", @entry{qw/component module/}), | |
133 | $cfg->entry | |
134 | ( | |
135 | $section, $entry{component}, 1 | |
136 | ) | |
137 | ) | |
138 | ) | |
139 | ) { | |
140 | return; | |
141 | } | |
142 | ||
143 | require BSE::Util::SQL; | |
144 | require BSE::TB::AuditLog; | |
145 | BSE::TB::AuditLog->make(%entry); | |
146 | keys %opts | |
147 | and $class->crash("Unknown parameters ", join(",", keys %opts), " to log()"); | |
148 | } | |
149 | ||
150 | sub crash { | |
151 | my ($class, @msg) = @_; | |
152 | ||
153 | @msg or push @msg, "Unknown"; | |
154 | my $longmsg = Carp::longmess(@msg); | |
155 | $class->log | |
156 | ( | |
157 | component => "unknown", | |
158 | module => "unknown", | |
159 | function => "unknown", | |
160 | level => "crit", | |
161 | actor => "S", | |
162 | msg => join("", @msg) || "Unknown", | |
163 | dump => $longmsg, | |
164 | ); | |
165 | die $longmsg; | |
166 | } | |
167 | ||
168 | my @level_names = qw(emerg alert crit error warning notice info debug); | |
169 | my %level_name_to_id; | |
170 | @level_name_to_id{@level_names} = 0 .. $#level_names; | |
171 | my %level_id_to_name; | |
172 | @level_id_to_name{0 .. $#level_names} = @level_names; | |
173 | ||
174 | sub _level_name_to_id { | |
175 | my ($name) = @_; | |
176 | ||
177 | # default to 0 (emerg) | |
178 | return $level_name_to_id{$name} || 0; | |
179 | } | |
180 | ||
181 | sub level_id_to_name { | |
182 | my ($class, $id) = @_; | |
183 | ||
184 | return $level_id_to_name{$id} || sprintf("unknown-%d", $id); | |
185 | } | |
186 | ||
187 | 1; |