/* * Copyright 1999-2017 Alibaba Group Holding Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.druid.stat; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.management.JMException; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import com.alibaba.druid.util.Histogram; import com.alibaba.druid.util.JMXUtils; /** * @author wenshao [szujobs@hotmail.com] */ public class JdbcConnectionStat implements JdbcConnectionStatMBean { private final AtomicInteger activeCount = new AtomicInteger(); private final AtomicInteger activeCountMax = new AtomicInteger(); private final AtomicInteger connectingCount = new AtomicInteger(); private final AtomicInteger connectingMax = new AtomicInteger(); private final AtomicLong connectCount = new AtomicLong(); private final AtomicLong connectErrorCount = new AtomicLong(); private Throwable connectErrorLast; private final AtomicLong connectNanoTotal = new AtomicLong(0); // 连接建立消耗时间总和(纳秒) private final AtomicLong connectNanoMax = new AtomicLong(0); // 连接建立消耗最大时间(纳秒) private final AtomicLong errorCount = new AtomicLong(); private final AtomicLong aliveNanoTotal = new AtomicLong(); private Throwable lastError; private long lastErrorTime; private long connectLastTime = 0; private final AtomicLong closeCount = new AtomicLong(0); // 执行Connection.close的计数 private final AtomicLong transactionStartCount = new AtomicLong(0); private final AtomicLong commitCount = new AtomicLong(0); // 执行commit的计数 private final AtomicLong rollbackCount = new AtomicLong(0); // 执行rollback的计数 private final AtomicLong aliveNanoMin = new AtomicLong(); private final AtomicLong aliveNanoMax = new AtomicLong(); private final Histogram histogram = new Histogram(TimeUnit.SECONDS, new long[] { // 1, 5, 15, 60, 300, 1800 }); public JdbcConnectionStat(){ } public void reset() { connectingMax.set(0); connectErrorCount.set(0); errorCount.set(0); aliveNanoTotal.set(0); aliveNanoMin.set(0); aliveNanoMax.set(0); lastError = null; lastErrorTime = 0; connectLastTime = 0; connectCount.set(0); closeCount.set(0); transactionStartCount.set(0); commitCount.set(0); rollbackCount.set(0); connectNanoTotal.set(0); connectNanoMax.set(0); histogram.reset(); } public void beforeConnect() { int invoking = connectingCount.incrementAndGet(); for (;;) { int max = connectingMax.get(); if (invoking > max) { if (connectingMax.compareAndSet(max, invoking)) { break; } else { continue; } } else { break; } } connectCount.incrementAndGet(); connectLastTime = System.currentTimeMillis(); } public void afterConnected(long delta) { connectingCount.decrementAndGet(); connectNanoTotal.addAndGet(delta); for (;;) { // connectNanoMax long max = connectNanoMax.get(); if (delta > max) { if (connectNanoMax.compareAndSet(max, delta)) { break; } else { continue; } } else { break; } } activeCount.incrementAndGet(); } public long getConnectNanoMax() { return this.connectNanoMax.get(); } public long getConnectMillisMax() { return this.connectNanoMax.get() / (1000 * 1000); } public void setActiveCount(int activeCount) { this.activeCount.set(activeCount); for (;;) { int max = activeCountMax.get(); if (activeCount > max) { if (activeCountMax.compareAndSet(max, activeCount)) { break; } else { continue; } } else { break; } } } public int getActiveCount() { return activeCount.get(); } public int getAtiveCountMax() { return this.activeCount.get(); } public long getErrorCount() { return errorCount.get(); } public int getConnectingCount() { return connectingCount.get(); } public int getConnectingMax() { return connectingMax.get(); } public long getAliveTotal() { return aliveNanoTotal.get(); } public long getAliveNanoMin() { return aliveNanoMin.get(); } public long getAliveMillisMin() { return aliveNanoMin.get() / (1000 * 1000); } public long getAliveNanoMax() { return aliveNanoMax.get(); } public long getAliveMillisMax() { return aliveNanoMax.get() / (1000 * 1000); } public void afterClose(long aliveNano) { activeCount.decrementAndGet(); aliveNanoTotal.addAndGet(aliveNano); for (;;) { long max = aliveNanoMax.get(); if (aliveNano > max) { if (aliveNanoMax.compareAndSet(max, aliveNano)) { break; } else { continue; } } else { break; } } for (;;) { long min = aliveNanoMin.get(); if (min == 0 || aliveNano < min) { if (aliveNanoMin.compareAndSet(min, aliveNano)) { break; } else { continue; } } else { break; } } long aliveMillis = aliveNano / (1000 * 1000); histogram.record(aliveMillis); } public Throwable getErrorLast() { return lastError; } public Throwable getConnectErrorLast() { return this.connectErrorLast; } public Date getErrorLastTime() { if (lastErrorTime <= 0) { return null; } return new Date(lastErrorTime); } public void connectError(Throwable error) { connectErrorCount.incrementAndGet(); connectErrorLast = error; errorCount.incrementAndGet(); lastError = error; lastErrorTime = System.currentTimeMillis(); } public void error(Throwable error) { errorCount.incrementAndGet(); lastError = error; lastErrorTime = System.currentTimeMillis(); } @Override public long getCloseCount() { return this.closeCount.get(); } @Override public long getCommitCount() { return this.commitCount.get(); } @Override public long getConnectCount() { return connectCount.get(); } @Override public long getConnectMillis() { return connectNanoTotal.get() / (1000 * 1000); } @Override public int getActiveMax() { return this.activeCountMax.get(); } @Override public long getRollbackCount() { return rollbackCount.get(); } @Override public long getConnectErrorCount() { return connectErrorCount.get(); } @Override public Date getConnectLastTime() { if (connectLastTime == 0) { return null; } return new Date(connectLastTime); } public void incrementConnectionCloseCount() { closeCount.incrementAndGet(); } public void incrementConnectionCommitCount() { commitCount.incrementAndGet(); } public void incrementConnectionRollbackCount() { rollbackCount.incrementAndGet(); } public void incrementTransactionStartCount() { transactionStartCount.incrementAndGet(); } public long getTransactionStartCount() { return transactionStartCount.get(); } public static class Entry implements EntryMBean { private long id; private long establishTime; private long establishNano; private Date connectTime; private long connectTimespanNano; private Exception connectStackTraceException; private String lastSql; private Exception lastStatementStatckTraceException; protected Throwable lastError; protected long lastErrorTime; private final String dataSource; public Entry(String dataSource, long connectionId){ this.id = connectionId; this.dataSource = dataSource; } public void reset() { this.lastSql = null; this.lastStatementStatckTraceException = null; this.lastError = null; this.lastErrorTime = 0; } public Date getEstablishTime() { if (establishTime <= 0) { return null; } return new Date(establishTime); } public void setEstablishTime(long establishTime) { this.establishTime = establishTime; } public long getEstablishNano() { return establishNano; } public void setEstablishNano(long establishNano) { this.establishNano = establishNano; } public Date getConnectTime() { return connectTime; } public void setConnectTime(Date connectTime) { this.connectTime = connectTime; } public long getConnectTimespanNano() { return connectTimespanNano; } public void setConnectTimespanNano(long connectTimespanNano) { this.connectTimespanNano = connectTimespanNano; } public String getLastSql() { return lastSql; } public void setLastSql(String lastSql) { this.lastSql = lastSql; } public String getConnectStackTrace() { if (connectStackTraceException == null) { return null; } StringWriter buf = new StringWriter(); connectStackTraceException.printStackTrace(new PrintWriter(buf)); return buf.toString(); } public void setConnectStackTrace(Exception connectStackTraceException) { this.connectStackTraceException = connectStackTraceException; } public String getLastStatementStatckTrace() { if (lastStatementStatckTraceException == null) { return null; } StringWriter buf = new StringWriter(); lastStatementStatckTraceException.printStackTrace(new PrintWriter(buf)); return buf.toString(); } public void setLastStatementStatckTrace(Exception lastStatementStatckTrace) { this.lastStatementStatckTraceException = lastStatementStatckTrace; } public void error(Throwable lastError) { this.lastError = lastError; this.lastErrorTime = System.currentTimeMillis(); } public Date getLastErrorTime() { if (lastErrorTime <= 0) { return null; } return new Date(lastErrorTime); } private static String[] indexNames = { "ID", "ConnectTime", "ConnectTimespan", "EstablishTime", "AliveTimespan", "LastSql", "LastError", "LastErrorTime", "ConnectStatckTrace", "LastStatementStackTrace", "DataSource" }; private static String[] indexDescriptions = indexNames; public static CompositeType getCompositeType() throws JMException { OpenType<?>[] indexTypes = new OpenType<?>[] { SimpleType.LONG, SimpleType.DATE, SimpleType.LONG, SimpleType.DATE, SimpleType.LONG, SimpleType.STRING, JMXUtils.getThrowableCompositeType(), SimpleType.DATE, SimpleType.STRING, SimpleType.STRING, SimpleType.STRING }; return new CompositeType("ConnectionStatistic", "Connection Statistic", indexNames, indexDescriptions, indexTypes); } public String getDataSource() { return this.dataSource; } public CompositeDataSupport getCompositeData() throws JMException { Map<String, Object> map = new HashMap<String, Object>(); map.put("ID", id); map.put("ConnectTime", getConnectTime()); map.put("ConnectTimespan", getConnectTimespanNano() / (1000 * 1000)); map.put("EstablishTime", getEstablishTime()); map.put("AliveTimespan", (System.nanoTime() - getEstablishNano()) / (1000 * 1000)); map.put("LastSql", getLastSql()); map.put("LastError", JMXUtils.getErrorCompositeData(this.lastError)); map.put("LastErrorTime", getLastErrorTime()); map.put("ConnectStatckTrace", getConnectStackTrace()); map.put("LastStatementStackTrace", getLastStatementStatckTrace()); map.put("DataSource", this.getDataSource()); return new CompositeDataSupport(getCompositeType(), map); } } public interface EntryMBean { Date getEstablishTime(); long getEstablishNano(); Date getConnectTime(); long getConnectTimespanNano(); String getLastSql(); String getConnectStackTrace(); String getLastStatementStatckTrace(); Date getLastErrorTime(); void reset(); } public long[] getHistorgramValues() { return this.histogram.toArray(); } public long[] getHistogramRanges() { return this.histogram.getRanges(); } }