Newer
Older
simple-jdbc-stats / src / nl / astraeus / jdbc / JdbcLogger.java
package nl.astraeus.jdbc;

import nl.astraeus.jdbc.util.Util;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * User: riennentjes
 * Date: Jul 12, 2008
 * Time: 9:15:21 AM
 */
public class JdbcLogger {
    private final static Map<Integer, JdbcLogger> instances = new ConcurrentHashMap<Integer, JdbcLogger>();

    public static JdbcLogger get(int port) {
        JdbcLogger result = instances.get(port);

        if (result == null) {
            result = new JdbcLogger(port);
            instances.put(port, result);
        }

        return result;
    }

    public final static class LogEntry {
        private long threadId;
        private QueryType type;
        private String sql;
        private long timeStamp;
        private long nanoTimeStamp;
        private long milli;
        private long nano;
        private int hash;
        private int count;
        private boolean autoCommit;
        private boolean formattedQueries;
        private StackTraceElement[] stackTrace = null;
        private final Map<Integer, Parameter> parameters;

        public LogEntry(
                int hash,
                QueryType type,
                String sql,
                long milli,
                long nano,
                boolean isAutoCommit,
                boolean formattedQueries,
                Map<Integer, Parameter> parameters
        ) {
            this.threadId = Thread.currentThread().getId();
            this.timeStamp = System.currentTimeMillis();
            this.nanoTimeStamp = System.nanoTime();
            this.hash = hash;
            this.type = type;
            this.sql = sql;
            this.milli = milli;
            this.nano = nano;
            this.autoCommit = isAutoCommit;
            this.count = 1;
            this.formattedQueries = formattedQueries;
            this.parameters = parameters;
        }

        public LogEntry(LogEntry le) {
            this.timeStamp = System.currentTimeMillis();
            this.nanoTimeStamp = System.nanoTime();
            this.hash = le.hash;
            this.type = le.type;
            this.sql = le.sql;
            this.milli = le.milli;
            this.nano = le.nano;
            this.count = le.count;
            this.autoCommit = le.autoCommit;
            this.formattedQueries = le.formattedQueries;
            this.parameters = le.parameters;
        }

        public QueryType getType() {
            return type;
        }

        public long getThreadId() {
            return threadId;
        }

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

        public int getHash() {
            return hash;
        }

        public long getTimestamp() {
            return timeStamp;
        }

        public long getNanoTimeStamp() {
            return nanoTimeStamp;
        }

        public String getFormattedTimestamp() {
            return Util.formatTimestamp(timeStamp);
        }

        public long getMilli() {
            return milli;
        }

        public long getNano() {
            return nano;
        }

        public String getFormattedMilli() {
            return Util.formatNano(milli * 1000000 / count);
        }

        public String getFormattedNano() {
            return Util.formatNano(nano / count);
        }

        public String getTotal() {
            return Util.formatNano(nano);
        }

        public String getSql() {
            if (formattedQueries) {
                return SqlFormatter.getHTMLFormattedSQL(sql);
            } else {
                return sql;
            }
        }

        public String getParameters() {
            StringBuilder result = new StringBuilder();

            if (parameters != null && !parameters.isEmpty()) {
                for (Map.Entry<Integer, Parameter> parameterEntry : parameters.entrySet()) {
                    Parameter parameter = parameterEntry.getValue();
                    result.append(String.format(
                            "%4s, %12s, %24s\n",
                            parameter.getIndex(),
                            parameter.getType(),
                            parameter.getDisplayValue()
                    ));
                }
            }

            return result.toString();
        }

        public boolean isEndOfTransaction() {
            return sql.toLowerCase().trim().equals("commit")
                    || sql.toLowerCase().trim().equals("rollback")
                    || sql.toLowerCase().trim().equals("close")
                    || isAutoCommit();
        }

        public void addCount(LogEntry le) {
            count++;
            this.milli += le.getMilli();
            this.nano += le.getNano();
            this.timeStamp = 0;
        }

        public boolean hasStackTrace() {
            return stackTrace != null;
        }

        public void setStackTrace(StackTraceElement[] stackTrace) {
            this.stackTrace = stackTrace;
        }

        public StackTraceElement[] getStackTrace() {
            return stackTrace;
        }

        public boolean isAutoCommit() {
            return autoCommit;
        }
    }


    private final List<LogEntry> queries;
    private final List<LogEntry> last100;
    private long startTime;
    private int port;

    public JdbcLogger(int port) {
        this.port = port;
        queries = new LinkedList<LogEntry>();
        last100 = new LinkedList<LogEntry>();
        startTime = System.currentTimeMillis();
    }

    public void clear() {
        queries.clear();
        last100.clear();
    }

    public void logEntry(
            QueryType type,
            String sql,
            long milli,
            long nano,
            boolean isAutoCommit
    ) {
        logEntry(type, sql, milli, nano, isAutoCommit, new TreeMap<Integer, Parameter>());
    }

    public void logEntry(
            QueryType type,
            String sql,
            long milli,
            long nano,
            boolean isAutoCommit,
            Map <Integer, Parameter> parameters
    ) {
        int hash = sql.hashCode();
        Driver.StatsLogger logger = Driver.get(port);

        LogEntry entry = new LogEntry(
                hash,
                type,
                sql,
                milli,
                nano,
                isAutoCommit,
                logger.getSettings().isFormattedQueries(),
                parameters
        );

        if (logger.getSettings().isRecordingStacktraces()) {
            try {
                throw new IllegalStateException();
            } catch (IllegalStateException e) {
                entry.setStackTrace(e.getStackTrace());
            }
        }

        synchronized (queries) {
            queries.add(entry);
            last100.add(entry);

            while (queries.size() > logger.getSettings().getNumberOfQueries()) {
                entry = queries.remove(0);
                startTime = entry.getMilli();
            }
            while (last100.size() > 100) {
                last100.remove(0);
            }
        }
    }

    public List<LogEntry> getEntries() {
        synchronized (queries) {
            return new LinkedList<LogEntry>(queries);
        }
    }

    public List<LogEntry> getLast100() {
        synchronized (queries) {
            return new LinkedList<LogEntry>(last100);
        }
    }
}