001/**
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *   or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014package ch.qos.logback.access.db;
015
016import java.lang.reflect.Method;
017import java.sql.Connection;
018import java.sql.PreparedStatement;
019import java.sql.SQLException;
020import java.util.Enumeration;
021
022import ch.qos.logback.access.spi.IAccessEvent;
023import ch.qos.logback.core.db.DBAppenderBase;
024
025/**
026 * The DBAppender inserts access events into three database tables in a format
027 * independent of the Java programming language. 
028 * 
029 * For more information about this appender, please refer to the online manual at
030 * http://logback.qos.ch/manual/appenders.html#AccessDBAppender
031 * 
032 * @author Ceki Gülcü
033 * @author Ray DeCampo
034 * @author Sébastien Pennec
035 */
036public class DBAppender extends DBAppenderBase<IAccessEvent> {
037    protected static final String insertSQL;
038    protected final String insertHeaderSQL = "INSERT INTO  access_event_header (event_id, header_key, header_value) VALUES (?, ?, ?)";
039    protected static final Method GET_GENERATED_KEYS_METHOD;
040
041    private boolean insertHeaders = false;
042
043    static {
044        StringBuilder sql = new StringBuilder();
045        sql.append("INSERT INTO access_event (");
046        sql.append("timestmp, ");
047        sql.append("requestURI, ");
048        sql.append("requestURL, ");
049        sql.append("remoteHost, ");
050        sql.append("remoteUser, ");
051        sql.append("remoteAddr, ");
052        sql.append("protocol, ");
053        sql.append("method, ");
054        sql.append("serverName, ");
055        sql.append("postContent) ");
056        sql.append(" VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?)");
057        insertSQL = sql.toString();
058
059        Method getGeneratedKeysMethod;
060        try {
061            getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[]) null);
062        } catch (Exception ex) {
063            getGeneratedKeysMethod = null;
064        }
065        GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
066    }
067
068    @Override
069    protected void subAppend(IAccessEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {
070
071        addAccessEvent(insertStatement, event);
072
073        int updateCount = insertStatement.executeUpdate();
074        if (updateCount != 1) {
075            addWarn("Failed to insert access event");
076        }
077    }
078
079    @Override
080    protected void secondarySubAppend(IAccessEvent event, Connection connection, long eventId) throws Throwable {
081        if (insertHeaders) {
082            addRequestHeaders(event, connection, eventId);
083        }
084    }
085
086    void addAccessEvent(PreparedStatement stmt, IAccessEvent event) throws SQLException {
087        stmt.setLong(1, event.getTimeStamp());
088        stmt.setString(2, event.getRequestURI());
089        stmt.setString(3, event.getRequestURL());
090        stmt.setString(4, event.getRemoteHost());
091        stmt.setString(5, event.getRemoteUser());
092        stmt.setString(6, event.getRemoteAddr());
093        stmt.setString(7, event.getProtocol());
094        stmt.setString(8, event.getMethod());
095        stmt.setString(9, event.getServerName());
096        stmt.setString(10, event.getRequestContent());
097    }
098
099    void addRequestHeaders(IAccessEvent event, Connection connection, long eventId) throws SQLException {
100        Enumeration<String> names = event.getRequestHeaderNames();
101        if (names.hasMoreElements()) {
102            PreparedStatement insertHeaderStatement = connection.prepareStatement(insertHeaderSQL);
103
104            while (names.hasMoreElements()) {
105                String key = (String) names.nextElement();
106                String value = (String) event.getRequestHeader(key);
107
108                insertHeaderStatement.setLong(1, eventId);
109                insertHeaderStatement.setString(2, key);
110                insertHeaderStatement.setString(3, value);
111
112                if (cnxSupportsBatchUpdates) {
113                    insertHeaderStatement.addBatch();
114                } else {
115                    insertHeaderStatement.execute();
116                }
117            }
118
119            if (cnxSupportsBatchUpdates) {
120                insertHeaderStatement.executeBatch();
121            }
122
123            insertHeaderStatement.close();
124        }
125    }
126
127    @Override
128    protected Method getGeneratedKeysMethod() {
129        return GET_GENERATED_KEYS_METHOD;
130    }
131
132    @Override
133    protected String getInsertSQL() {
134        return insertSQL;
135    }
136
137    public void setInsertHeaders(boolean insertHeaders) {
138        this.insertHeaders = insertHeaders;
139    }
140}