KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > access > QueryLogger


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne.access;
57
58 import java.sql.SQLException JavaDoc;
59 import java.util.List JavaDoc;
60
61 import org.apache.log4j.Level;
62 import org.apache.log4j.Logger;
63 import org.objectstyle.cayenne.access.jdbc.ParameterBinding;
64 import org.objectstyle.cayenne.conn.DataSourceInfo;
65 import org.objectstyle.cayenne.query.Query;
66 import org.objectstyle.cayenne.util.Util;
67
68 /**
69  * A QueryLogger is intended to log special events during query executions.
70  * This includes generated SQL statements, result counts, connection events etc.
71  * It is a single consistent place for that kind of logging and should be used
72  * by all Cayenne classes that work with the database directly.
73  *
74  * <p>In many cases it is important to use this class as opposed to logging
75  * from the class that performs a particular operation, since QueryLogger
76  * will generate consistently formatted logs that are easy to analyze
77  * and turn on/off.</p>
78  *
79  * <p><i>For more information see <a HREF="../../../../../../userguide/index.html"
80  * target="_top">Cayenne User Guide.</a></i></p>
81  *
82  * @author Andrei Adamchik
83  */

84 public class QueryLogger {
85     private static final Logger logObj = Logger.getLogger(QueryLogger.class);
86
87     public static final Level DEFAULT_LOG_LEVEL = Query.DEFAULT_LOG_LEVEL;
88     public static final int TRIM_VALUES_THRESHOLD = 300;
89
90     /**
91      * Utility method that appends SQL literal for the specified object to the buffer.
92     *
93     * <p>Note: this method is not intended to build SQL queries, rather this is used
94     * in logging routines only. In particular it will trim large values to avoid
95     * flooding the logs.</p>
96     *
97     * @param buf buffer to append value
98     * @param anObject object to be transformed to SQL literal.
99     */

100     public static void sqlLiteralForObject(StringBuffer JavaDoc buf, Object JavaDoc anObject) {
101         // 0. Null
102
if (anObject == null) {
103             buf.append("NULL");
104         }
105         // 1. String literal
106
else if (anObject instanceof String JavaDoc) {
107             buf.append('\'');
108             // lets escape quotes
109
String JavaDoc literal = (String JavaDoc) anObject;
110             if (literal.length() > TRIM_VALUES_THRESHOLD) {
111                 literal = literal.substring(0, TRIM_VALUES_THRESHOLD) + "...";
112             }
113
114             int curPos = 0;
115             int endPos = 0;
116
117             while ((endPos = literal.indexOf('\'', curPos)) >= 0) {
118                 buf.append(literal.substring(curPos, endPos + 1)).append('\'');
119                 curPos = endPos + 1;
120             }
121
122             if (curPos < literal.length())
123                 buf.append(literal.substring(curPos));
124
125             buf.append('\'');
126         }
127         // 2. Numeric literal
128
else if (anObject instanceof Number JavaDoc) {
129             // process numeric value (do something smart in the future)
130
buf.append(anObject);
131         }
132         // 3. Date
133
else if (anObject instanceof java.sql.Date JavaDoc) {
134             buf.append('\'').append(anObject).append('\'');
135         }
136         // 4. Date
137
else if (anObject instanceof java.sql.Time JavaDoc) {
138             buf.append('\'').append(anObject).append('\'');
139         }
140         // 5 Date
141
else if (anObject instanceof java.util.Date JavaDoc) {
142             long time = ((java.util.Date JavaDoc) anObject).getTime();
143             buf.append('\'').append(new java.sql.Timestamp JavaDoc(time)).append('\'');
144         }
145         // 6. byte[]
146
else if (anObject instanceof byte[]) {
147             buf.append("< ");
148             byte[] b = (byte[]) anObject;
149
150             int len = b.length;
151             boolean trimming = false;
152             if (len > TRIM_VALUES_THRESHOLD) {
153                 len = TRIM_VALUES_THRESHOLD;
154                 trimming = true;
155             }
156
157             for (int i = 0; i < len; i++) {
158                 appendFormattedByte(buf, b[i]);
159                 buf.append(' ');
160             }
161
162             if (trimming) {
163                 buf.append("...");
164             }
165             buf.append('>');
166         }
167         else if (anObject instanceof Boolean JavaDoc) {
168             buf.append('\'').append(anObject).append('\'');
169
170         }
171         else if (anObject instanceof ParameterBinding) {
172             sqlLiteralForObject(buf, ((ParameterBinding) anObject).getValue());
173         }
174         else {
175             // unknown
176
buf
177                 .append("[")
178                 .append(anObject.getClass().getName())
179                 .append(": ")
180                 .append(anObject)
181                 .append("]");
182         }
183     }
184
185     /**
186      * Prints a byte value to a StringBuffer as a double digit hex value.
187      */

188     protected static void appendFormattedByte(StringBuffer JavaDoc buffer, byte byteValue) {
189         final String JavaDoc digits = "0123456789ABCDEF";
190
191         buffer.append(digits.charAt((byteValue >>> 4) & 0xF));
192         buffer.append(digits.charAt(byteValue & 0xF));
193     }
194
195     /**
196      * Returns current logging level.
197      */

198     public static Level getLoggingLevel() {
199         Level level = logObj.getLevel();
200         return (level != null) ? level : DEFAULT_LOG_LEVEL;
201     }
202
203     /**
204      * Sets logging level.
205      */

206     public static void setLoggingLevel(Level level) {
207         logObj.setLevel(level);
208     }
209
210     /**
211      * Logs database connection event using container data source.
212      */

213     public static void logConnect(Level logLevel, String JavaDoc dataSource) {
214         if (isLoggable(logLevel)) {
215             logObj.log(logLevel, "Connecting. JNDI path: " + dataSource);
216         }
217     }
218
219     public static void logConnect(
220         Level logLevel,
221         String JavaDoc url,
222         String JavaDoc userName,
223         String JavaDoc password) {
224         if (isLoggable(logLevel)) {
225             StringBuffer JavaDoc buf = new StringBuffer JavaDoc("Opening connection: ");
226
227             // append URL on the same line to make log somewhat grep-friendly
228
buf.append(url);
229             buf.append("\n\tLogin: ").append(userName);
230             buf.append("\n\tPassword: *******");
231
232             logObj.log(logLevel, buf.toString());
233         }
234     }
235
236     /**
237      * Logs database connection event.
238      */

239     public static void logPoolCreated(Level logLevel, DataSourceInfo dsi) {
240         if (isLoggable(logLevel)) {
241             StringBuffer JavaDoc buf = new StringBuffer JavaDoc("Created connection pool: ");
242
243             if (dsi != null) {
244                 // append URL on the same line to make log somewhat grep-friendly
245
buf.append(dsi.getDataSourceUrl());
246
247                 if (dsi.getAdapterClassName() != null) {
248                     buf.append("\n\tCayenne DbAdapter: ").append(
249                         dsi.getAdapterClassName());
250                 }
251
252                 buf.append("\n\tDriver class: ").append(dsi.getJdbcDriver());
253
254                 if (dsi.getMinConnections() >= 0) {
255                     buf.append("\n\tMin. connections in the pool: ").append(
256                         dsi.getMinConnections());
257                 }
258                 if (dsi.getMaxConnections() >= 0) {
259                     buf.append("\n\tMax. connections in the pool: ").append(
260                         dsi.getMaxConnections());
261                 }
262             }
263             else {
264                 buf.append(" pool information unavailable");
265             }
266
267             logObj.log(logLevel, buf.toString());
268         }
269     }
270
271     public static void logConnectSuccess(Level logLevel) {
272         logObj.log(logLevel, "+++ Connecting: SUCCESS.");
273     }
274
275     public static void logConnectFailure(Level logLevel, Throwable JavaDoc th) {
276         logObj.log(logLevel, "*** Connecting: FAILURE.", th);
277     }
278
279     public static void logQuery(Level logLevel, String JavaDoc queryStr, List JavaDoc params) {
280         logQuery(logLevel, queryStr, params, -1);
281     }
282
283     /**
284      * Log query content using Log4J Category with "INFO" priority.
285      *
286      * @param queryStr Query SQL string
287      * @param params optional list of query parameters that are used when
288      * executing query in prepared statement.
289      */

290     public static void logQuery(
291         Level logLevel,
292         String JavaDoc queryStr,
293         List JavaDoc params,
294         long time) {
295         if (isLoggable(logLevel)) {
296             StringBuffer JavaDoc buf = new StringBuffer JavaDoc(queryStr);
297             if (params != null && params.size() > 0) {
298                 buf.append(" [bind: ");
299                 sqlLiteralForObject(buf, params.get(0));
300
301                 int len = params.size();
302                 for (int i = 1; i < len; i++) {
303                     buf.append(", ");
304                     sqlLiteralForObject(buf, params.get(i));
305                 }
306
307                 buf.append(']');
308             }
309
310             // log preparation time only if it is something significant
311
if (time > 5) {
312                 buf.append(" - prepared in ").append(time).append(" ms.");
313             }
314
315             logObj.log(logLevel, buf.toString());
316         }
317     }
318
319     public static void logQueryParameters(
320         Level logLevel,
321         String JavaDoc label,
322         List JavaDoc parameters) {
323
324         if (isLoggable(logLevel) && parameters.size() > 0) {
325             int len = parameters.size();
326             StringBuffer JavaDoc buf = new StringBuffer JavaDoc("[");
327             buf.append(label).append(": ");
328
329             sqlLiteralForObject(buf, parameters.get(0));
330
331             for (int i = 1; i < len; i++) {
332                 buf.append(", ");
333                 sqlLiteralForObject(buf, parameters.get(i));
334             }
335
336             buf.append(']');
337             logObj.log(logLevel, buf.toString());
338         }
339     }
340
341     public static void logSelectCount(Level logLevel, int count) {
342         logSelectCount(logLevel, count, -1);
343     }
344
345     public static void logSelectCount(Level logLevel, int count, long time) {
346         if (isLoggable(logLevel)) {
347             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
348
349             if (count == 1) {
350                 buf.append("=== returned 1 row.");
351             }
352             else {
353                 buf.append("=== returned ").append(count).append(" rows.");
354             }
355
356             if (time >= 0) {
357                 buf.append(" - took ").append(time).append(" ms.");
358             }
359
360             logObj.log(logLevel, buf.toString());
361         }
362     }
363
364     public static void logUpdateCount(Level logLevel, int count) {
365         if (isLoggable(logLevel)) {
366             String JavaDoc countStr =
367                 (count == 1) ? "=== updated 1 row." : "=== updated " + count + " rows.";
368             logObj.log(logLevel, countStr);
369         }
370     }
371
372     /**
373      * @since 1.1
374      */

375     public static void logBeginTransaction(Level logLevel, String JavaDoc transactionLabel) {
376         if (isLoggable(logLevel)) {
377             logObj.log(logLevel, "--- " + transactionLabel);
378         }
379     }
380
381     /**
382      * @since 1.1
383      */

384     public static void logCommitTransaction(Level logLevel, String JavaDoc transactionLabel) {
385         if (isLoggable(logLevel)) {
386             logObj.log(logLevel, "+++ " + transactionLabel);
387         }
388     }
389
390     /**
391      * @since 1.1
392      */

393     public static void logRollbackTransaction(Level logLevel, String JavaDoc transactionLabel) {
394         if (isLoggable(logLevel)) {
395             logObj.log(logLevel, "*** " + transactionLabel);
396         }
397     }
398
399     public static void logQueryError(Level logLevel, Throwable JavaDoc th) {
400         if (th != null) {
401             th = Util.unwindException(th);
402         }
403
404         logObj.log(logLevel, "*** error.", th);
405
406         if (th instanceof SQLException JavaDoc) {
407             SQLException JavaDoc sqlException = ((SQLException JavaDoc) th).getNextException();
408             while (sqlException != null) {
409                 logObj.log(logLevel, "*** nested SQL error.", sqlException);
410                 sqlException = sqlException.getNextException();
411             }
412         }
413     }
414
415     public static void logQueryStart(Level logLevel, int count) {
416         if (isLoggable(logLevel)) {
417             String JavaDoc countStr =
418                 (count == 1)
419                     ? "--- will run 1 query."
420                     : "--- will run " + count + " queries.";
421             logObj.log(logLevel, countStr);
422         }
423     }
424
425     public static boolean isLoggable(Level logLevel) {
426         return logObj.isEnabledFor(logLevel);
427     }
428
429 }
430
Popular Tags