View Javadoc

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