View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2009, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.db;
15  
16  import java.lang.reflect.Method;
17  import java.sql.Connection;
18  import java.sql.PreparedStatement;
19  import java.sql.SQLException;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import ch.qos.logback.classic.spi.ILoggingEvent;
26  import ch.qos.logback.classic.spi.StackTraceElementProxy;
27  import ch.qos.logback.core.db.DBAppenderBase;
28  
29  /**
30   * The DBAppender inserts logging events into three database tables in a format
31   * independent of the Java programming language. 
32   * 
33   * For more information about this appender, please refer to the online manual at
34   * http://logback.qos.ch/manual/appenders.html#DBAppender
35   * 
36   * @author Ceki Gülcü
37   * @author Ray DeCampo
38   * @author Sébastien Pennec
39   */
40  public class DBAppender extends DBAppenderBase<ILoggingEvent> {
41    protected final String insertPropertiesSQL = "INSERT INTO  logging_event_property (event_id, mapped_key, mapped_value) VALUES (?, ?, ?)";
42    protected final String insertExceptionSQL = "INSERT INTO  logging_event_exception (event_id, i, trace_line) VALUES (?, ?, ?)";
43    protected static final String insertSQL;
44    protected static final Method GET_GENERATED_KEYS_METHOD;
45  
46    static {
47      StringBuffer sql = new StringBuffer();
48      sql.append("INSERT INTO logging_event (");
49      sql.append("timestmp, ");
50      sql.append("formatted_message, ");
51      sql.append("logger_name, ");
52      sql.append("level_string, ");
53      sql.append("thread_name, ");
54      sql.append("reference_flag, ");
55      sql.append("caller_filename, ");
56      sql.append("caller_class, ");
57      sql.append("caller_method, ");
58      sql.append("caller_line) ");
59      sql.append(" VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?,?)");
60      insertSQL = sql.toString();
61      
62      // PreparedStatement.getGeneratedKeys() method was added in JDK 1.4
63      Method getGeneratedKeysMethod;
64      try {
65        // the 
66        getGeneratedKeysMethod = PreparedStatement.class.getMethod(
67            "getGeneratedKeys", (Class[]) null);
68      } catch (Exception ex) {
69        getGeneratedKeysMethod = null;
70      }
71      GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
72    }
73    
74    public DBAppender() {
75    }
76  
77    @Override
78    protected void subAppend(Object eventObject, Connection connection,
79        PreparedStatement insertStatement) throws Throwable {
80      ILoggingEvent event = (ILoggingEvent) eventObject;
81  
82      bindLoggingEventWithInsertStatement(insertStatement, event);
83      // This is expensive... should we do it every time?
84      bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData());
85  
86      int updateCount = insertStatement.executeUpdate();
87      if (updateCount != 1) {
88        addWarn("Failed to insert loggingEvent");
89      }
90  
91      int eventId = selectEventId(insertStatement, connection);
92  
93      Map<String, String> mergedMap = mergePropertyMaps(event);
94      insertProperties(mergedMap, connection, eventId);
95  
96      if (event.getThrowableProxy() != null) {
97        insertThrowable(event.getThrowableProxy().getStackTraceElementProxyArray(), connection, eventId);
98      }
99    }
100 
101   void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event)
102       throws SQLException {
103     stmt.setLong(1, event.getTimeStamp());
104     stmt.setString(2, event.getFormattedMessage());
105     stmt.setString(3, event.getLoggerName());
106     stmt.setString(4, event.getLevel().toString());
107     stmt.setString(5, event.getThreadName());
108     stmt.setShort(6, DBHelper.computeReferenceMask(event));
109   }
110 
111   void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray)
112       throws SQLException {
113     StackTraceElement callerData = callerDataArray[0];
114     if (callerData != null) {
115       stmt.setString(7, callerData.getFileName());
116       stmt.setString(8, callerData.getClassName());
117       stmt.setString(9, callerData.getMethodName());
118       stmt.setString(10, Integer.toString(callerData.getLineNumber()));
119     }
120   }
121 
122   Map<String, String> mergePropertyMaps(ILoggingEvent event) {
123     Map<String, String> mergedMap = new HashMap<String, String>();
124     // we add the context properties first, then the event properties, since
125     // we consider that event-specific properties should have priority over
126     // context-wide
127     // properties.
128     Map<String, String> loggerContextMap = event.getLoggerContextVO().getPropertyMap();
129     Map<String, String> mdcMap = event.getMDCPropertyMap();
130     if (loggerContextMap != null) {
131       mergedMap.putAll(loggerContextMap);
132     }
133     if (mdcMap != null) {
134       mergedMap.putAll(mdcMap);
135     }
136 
137     return mergedMap;
138   }
139 
140   @Override
141   protected Method getGeneratedKeysMethod() {
142     return GET_GENERATED_KEYS_METHOD;
143   }
144 
145   @Override
146   protected String getInsertSQL() {
147     return insertSQL;
148   }
149   
150   protected void insertProperties(Map<String, String> mergedMap,
151       Connection connection, int eventId) throws SQLException {
152     Set propertiesKeys = mergedMap.keySet();
153     if (propertiesKeys.size() > 0) {
154       PreparedStatement insertPropertiesStatement = connection
155           .prepareStatement(insertPropertiesSQL);
156 
157       for (Iterator i = propertiesKeys.iterator(); i.hasNext();) {
158         String key = (String) i.next();
159         String value = (String) mergedMap.get(key);
160 
161         insertPropertiesStatement.setInt(1, eventId);
162         insertPropertiesStatement.setString(2, key);
163         insertPropertiesStatement.setString(3, value);
164 
165         if (cnxSupportsBatchUpdates) {
166           insertPropertiesStatement.addBatch();
167         } else {
168           insertPropertiesStatement.execute();
169         }
170       }
171 
172       if (cnxSupportsBatchUpdates) {
173         insertPropertiesStatement.executeBatch();
174       }
175 
176       insertPropertiesStatement.close();
177       insertPropertiesStatement = null;
178     }
179   }
180   
181   protected void insertThrowable(StackTraceElementProxy[] stepArray, Connection connection,
182       int eventId) throws SQLException {
183 
184     PreparedStatement insertExceptionStatement = connection
185         .prepareStatement(insertExceptionSQL);
186 
187     for (short i = 0; i < stepArray.length; i++) {
188       insertExceptionStatement.setInt(1, eventId);
189       insertExceptionStatement.setShort(2, i);
190       insertExceptionStatement.setString(3, stepArray[i].toString());
191       if (cnxSupportsBatchUpdates) {
192         insertExceptionStatement.addBatch();
193       } else {
194         insertExceptionStatement.execute();
195       }
196     }
197     if (cnxSupportsBatchUpdates) {
198       insertExceptionStatement.executeBatch();
199     }
200     insertExceptionStatement.close();
201     insertExceptionStatement = null;
202 
203   }
204 }