1Appender::DBI(3) User Contributed Perl Documentation Appender::DBI(3)
2
3
4
6 Log::Log4perl::Appender::DBI - implements appending to a DB
7
9 my $config = <<'EOT';
10 log4j.category = WARN, DBAppndr
11 log4j.appender.DBAppndr = Log::Log4perl::Appender::DBI
12 log4j.appender.DBAppndr.datasource = DBI:CSV:f_dir=t/tmp
13 log4j.appender.DBAppndr.username = bobjones
14 log4j.appender.DBAppndr.password = 12345
15 log4j.appender.DBAppndr.sql = \
16 insert into log4perltest \
17 (loglevel, custid, category, message, ipaddr) \
18 values (?,?,?,?,?)
19 log4j.appender.DBAppndr.params.1 = %p
20 #2 is custid from the log() call
21 log4j.appender.DBAppndr.params.3 = %c
22 #4 is the message from log()
23 #5 is ipaddr from log()
24
25 log4j.appender.DBAppndr.usePreparedStmt = 1
26 #--or--
27 log4j.appender.DBAppndr.bufferSize = 2
28
29 #just pass through the array of message items in the log statement
30 log4j.appender.DBAppndr.layout = Log::Log4perl::Layout::NoopLayout
31 log4j.appender.DBAppndr.warp_message = 0
32
33 $logger->warn( $custid, 'big problem!!', $ip_addr );
34
36 This is a very young module and there are a lot of variations in setups
37 with different databases and connection methods, so make sure you test
38 thoroughly! Any feedback is welcome!
39
41 This is a specialized Log::Dispatch object customized to work with
42 log4perl and its abilities, originally based on Log::Dispatch::DBI by
43 Tatsuhiko Miyagawa but with heavy modifications.
44
45 It is an attempted compromise between what Log::Dispatch::DBI was doing
46 and what log4j's JDBCAppender does. Note the log4j docs say the JDB‐
47 CAppender "is very likely to be completely replaced in the future."
48
49 The simplest usage is this:
50
51 log4j.category = WARN, DBAppndr
52 log4j.appender.DBAppndr = Log::Log4perl::Appender::DBI
53 log4j.appender.DBAppndr.datasource = DBI:CSV:f_dir=t/tmp
54 log4j.appender.DBAppndr.username = bobjones
55 log4j.appender.DBAppndr.password = 12345
56 log4j.appender.DBAppndr.sql = \
57 INSERT INTO logtbl \
58 (loglevel, message) \
59 VALUES ('%c','%m')
60
61 log4j.appender.DBAppndr.layout = Log::Log4perl::Layout::PatternLayout
62
63 $logger->fatal('fatal message');
64 $logger->warn('warning message');
65
66 ===============================
67 ⎪FATAL⎪fatal message ⎪
68 ⎪WARN ⎪warning message ⎪
69 ===============================
70
71 But the downsides to that usage are:
72
73 · You'd better be darn sure there are not quotes in your log message,
74 or your insert could have unforseen consequences! This is a very
75 insecure way to handle database inserts, using place holders and
76 bind values is much better, keep reading. (Note that the log4j docs
77 warn "Be careful of quotes in your messages!") *.
78
79 · It's not terribly high-performance, a statement is created and exe‐
80 cuted for each log call.
81
82 · The only run-time parameter you get is the %m message, in reality
83 you probably want to log specific data in specific table columns.
84
85 So let's try using placeholders, and tell the logger to create a pre‐
86 pared statement handle at the beginning and just reuse it (just like
87 Log::Dispatch::DBI does)
88
89 log4j.appender.DBAppndr.sql = \
90 INSERT INTO logtbl \
91 (custid, loglevel, message) \
92 VALUES (?,?,?)
93
94 #---------------------------------------------------
95 #now the bind values:
96 #1 is the custid
97 log4j.appender.DBAppndr.params.2 = %p
98 #3 is the message
99 #---------------------------------------------------
100
101 log4j.appender.DBAppndr.layout = Log::Log4perl::Layout::NoopLayout
102 log4j.appender.DBAppndr.warp_message = 0
103
104 log4j.appender.DBAppndr.usePreparedStmt = 1
105
106 $logger->warn( 1234, 'warning message' );
107
108 Now see how we're using the '?' placeholders in our statement? This
109 means we don't have to worry about messages that look like
110
111 invalid input: 1234';drop table custid;
112
113 fubaring our database!
114
115 Normally a list of things in the logging statement gets concatenated
116 into a single string, but setting "warp_message" to 0 and using the
117 NoopLayout means that in
118
119 $logger->warn( 1234, 'warning message', 'bgates' );
120
121 the individual list values will still be available for the DBI appender
122 later on. (If "warp_message" is not set to 0, the default behavior is
123 to join the list elements into a single string. If PatternLayout or
124 SimpleLayout are used, their attempt to "render()" your layout will
125 result in something like "ARRAY(0x841d8dc)" in your logs. More infor‐
126 mation on "warp_message" is in Log::Log4perl::Appender.)
127
128 In your insert SQL you can mix up '?' placeholders with conversion
129 specifiers (%c, %p, etc) as you see fit--the logger will match the
130 question marks to params you've defined in the config file and populate
131 the rest with values from your list. If there are more '?' placehold‐
132 ers than there are values in your message, it will use undef for the
133 rest. For instance,
134
135 log4j.appender.DBAppndr.sql = \
136 insert into log4perltest \
137 (loglevel, message, datestr, subpoena_id)\
138 values (?,?,?,?)
139 log4j.appender.DBAppndr.params.1 = %p
140 log4j.appender.DBAppndr.params.3 = %d
141
142 log4j.appender.DBAppndr.warp_message=0
143
144 $logger->info('arrest him!', $subpoena_id);
145
146 results in the first '?' placholder being bound to %p, the second to
147 "arrest him!", the third to the date from "%d", and the fourth to your
148 $subpoenaid. If you forget the $subpoena_id and just log
149
150 $logger->info('arrest him!');
151
152 then you just get undef in the fourth column.
153
154 If the logger statement is also being handled by other non-DBI appen‐
155 ders, they will just join the list into a string, joined with
156 $Log::Log4perl::JOIN_MSG_ARRAY_CHAR (default is an empty string).
157
158 And see the "usePreparedStmt"? That creates a statement handle when
159 the logger object is created and just reuses it. That, however, may be
160 problematic for long-running processes like webservers, in which case
161 you can use this parameter instead
162
163 log4j.appender.DBAppndr.bufferSize=2
164
165 This copies log4j's JDBCAppender's behavior, it saves up that many log
166 statements and writes them all out at once. If your INSERT statement
167 uses only ? placeholders and no %x conversion specifiers it should be
168 quite efficient because the logger can re-use the same statement handle
169 for the inserts.
170
171 If the program ends while the buffer is only partly full, the DESTROY
172 block should flush the remaining statements, if the DESTROY block runs
173 of course.
174
175 * As I was writing this, Danko Mannhaupt was coming out with his
176 improved log4j JDBCAppender (http://www.mannhaupt.com/danko/projects/)
177 which overcomes many of the drawbacks of the original JDBCAppender.
178
180 Or another way to say the same thing:
181
182 The idea is that if you're logging to a database table, you probably
183 want specific parts of your log information in certain columns. To
184 this end, you pass an list to the log statement, like
185
186 $logger->warn('big problem!!',$userid,$subpoena_nr,$ip_addr);
187
188 and the array members drop into the positions defined by the placehold‐
189 ers in your SQL statement. You can also define information in the con‐
190 fig file like
191
192 log4j.appender.DBAppndr.params.2 = %p
193
194 in which case those numbered placeholders will be filled in with the
195 specified values, and the rest of the placeholders will be filled in
196 with the values from your log statement's array.
197
199 usePreparedStmt
200 See above.
201
202 warp_message
203 see Log::Log4perl::Appender
204
205 max_col_size
206 If you're used to just throwing debugging messages like huge stack‐
207 traces into your logger, some databases (Sybase's DBD!!) may
208 suprise you by choking on data size limitations. Normally, the
209 data would just be truncated to fit in the column, but Sybases's
210 DBD it turns out maxes out at 255 characters. Use this parameter
211 in such a situation to truncate long messages before they get to
212 the INSERT statement.
213
215 If you want to get your dbh from some place in particular, like maybe a
216 pool, subclass and override _init() and/or create_statement(), for
217 instance
218
219 sub _init {
220 ; #no-op, no pooling at this level
221 }
222 sub create_statement {
223 my ($self, $stmt) = @_;
224
225 $stmt ⎪⎪ croak "Log4perl: sql not set in ".__PACKAGE__;
226
227 return My::Connections->getConnection->prepare($stmt)
228 ⎪⎪ croak "Log4perl: DBI->prepare failed $DBI::errstr\n$stmt";
229 }
230
232 If you're using "log4j.appender.DBAppndr.usePreparedStmt" this module
233 creates an sth when it starts and keeps it for the life of the program.
234 For long-running processes (e.g. mod_perl), connections might go stale,
235 but if "Log::Log4perl::Appender::DBI" tries to write a message and fig‐
236 ures out that the DB connection is no longer working (using DBI's ping
237 method), it will reconnect.
238
239 The reconnection process can be controlled by two parameters, "recon‐
240 nect_attempts" and "reconnect_sleep". "reconnect_attempts" specifies
241 the number of reconnections attempts the DBI appender performs until it
242 gives up and dies. "reconnect_sleep" is the time between reconnection
243 attempts, measured in seconds. "reconnect_attempts" defaults to 1,
244 "reconnect_sleep" to 0.
245
246 Alternatively, use "Apache::DBI" or "Apache::DBI::Cache" and read
247 CHANGING DB CONNECTIONS above.
248
249 Note that "Log::Log4perl::Appender::DBI" holds one connection open for
250 every appender, which might be too many.
251
253 Kevin Goess <cpan@goess.org> December, 2002
254
256 Log::Dispatch::DBI
257
258 Log::Log4perl::JavaMap::JDBCAppender
259
260
261
262perl v5.8.8 2002-07-10 Appender::DBI(3)