package org.stagemonitor.jdbc;
import com.alibaba.druid.pool.DruidDataSource;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.Timer;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.p6spy.engine.spy.P6DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.stagemonitor.configuration.ConfigurationRegistry;
import org.stagemonitor.core.MeasurementSession;
import org.stagemonitor.core.Stagemonitor;
import org.stagemonitor.core.metrics.metrics2.Metric2Registry;
import org.stagemonitor.core.metrics.metrics2.MetricName;
import org.stagemonitor.tracing.MonitoredMethodRequest;
import org.stagemonitor.tracing.RequestMonitor;
import org.stagemonitor.tracing.SpanContextInformation;
import org.stagemonitor.tracing.TracingPlugin;
import org.stagemonitor.tracing.profiler.CallStackElement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Arrays;
import java.util.Map;
import javax.sql.DataSource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.stagemonitor.core.metrics.metrics2.MetricName.name;
@RunWith(Parameterized.class)
public class ConnectionMonitoringTransformerTest {
private static final String DRIVER_CLASS_NAME = "org.hsqldb.jdbcDriver";
private static final String URL = "jdbc:hsqldb:mem:test";
private ConfigurationRegistry configuration;
private DataSource dataSource;
private RequestMonitor requestMonitor;
private Metric2Registry metric2Registry;
private TestDao testDao;
@Parameterized.Parameters(name = "{index}: {1}")
public static Iterable<Object[]> data() throws Exception {
final PoolProperties poolProperties = new PoolProperties();
poolProperties.setDriverClassName(DRIVER_CLASS_NAME);
poolProperties.setUrl(URL);
final org.apache.tomcat.jdbc.pool.DataSource tomcatDataSource = new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(DRIVER_CLASS_NAME);
comboPooledDataSource.setJdbcUrl(URL);
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setDriverClassName(DRIVER_CLASS_NAME);
hikariDataSource.setJdbcUrl(URL);
org.apache.commons.dbcp.BasicDataSource dbcp = new org.apache.commons.dbcp.BasicDataSource();
dbcp.setDriverClassName(DRIVER_CLASS_NAME);
dbcp.setUrl(URL);
org.apache.commons.dbcp2.BasicDataSource dbcp2 = new org.apache.commons.dbcp2.BasicDataSource();
dbcp2.setDriverClassName(DRIVER_CLASS_NAME);
dbcp2.setUrl(URL);
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(DRIVER_CLASS_NAME);
druidDataSource.setUrl(URL);
druidDataSource.setTestWhileIdle(false);
return Arrays.asList(new Object[][]{
{tomcatDataSource, tomcatDataSource.getClass()},
{comboPooledDataSource, comboPooledDataSource.getClass()},
{hikariDataSource, hikariDataSource.getClass()},
{dbcp, dbcp.getClass()},
{dbcp2, dbcp2.getClass()},
{new P6DataSource(druidDataSource), druidDataSource.getClass()}
});
}
public ConnectionMonitoringTransformerTest(DataSource dataSource, Class<? extends DataSource> dataSourceClass) {
this.dataSource = dataSource;
}
@BeforeClass
public static void attachProfiler() throws Exception {
Stagemonitor.startMonitoring(new MeasurementSession("ConnectionMonitoringTransformerTest", "test", "test"));
}
@Before
public void setUp() throws Exception {
metric2Registry = Stagemonitor.getMetric2Registry();
metric2Registry.removeMatching(MetricFilter.ALL);
try (final Connection connection = dataSource.getConnection()) {
connection.prepareStatement("CREATE TABLE IF NOT EXISTS STAGEMONITOR (FOO INT)").execute();
connection.prepareStatement("INSERT INTO STAGEMONITOR (FOO) VALUES (1)").execute();
}
requestMonitor = Stagemonitor.getPlugin(TracingPlugin.class).getRequestMonitor();
configuration = Stagemonitor.getConfiguration();
testDao = new TestDao(dataSource);
}
@AfterClass
public static void cleanUp() {
Stagemonitor.reset();
Stagemonitor.getMetric2Registry().removeMatching(MetricFilter.ALL);
}
@Test
public void monitorGetConnection() throws Exception {
requestMonitor
.monitor(new MonitoredMethodRequest(configuration, "monitorGetConnectionUsernamePassword()", () -> {
dataSource.getConnection().close();
}));
final Map<MetricName, Timer> timers = metric2Registry.getTimers();
assertNotNull(timers.keySet().toString(), timers.get(name("get_jdbc_connection").tag("url", "SA@jdbc:hsqldb:mem:test").build()));
}
@Test
public void monitorGetConnectionUsernamePassword() throws Exception {
try {
dataSource.getConnection("sa", "").close();
} catch (SQLFeatureNotSupportedException | UnsupportedOperationException e) {
// ignore
return;
}
requestMonitor
.monitor(new MonitoredMethodRequest(configuration, "monitorGetConnectionUsernamePassword()", () -> {
dataSource.getConnection("sa", "").close();
}));
final Map<MetricName, Timer> timers = metric2Registry.getTimers();
assertNotNull(timers.keySet().toString(), timers.get(name("get_jdbc_connection").tag("url", "SA@jdbc:hsqldb:mem:test").build()));
}
@Test
public void testRecordSqlPreparedStatement() throws Exception {
final SpanContextInformation spanContext = requestMonitor
.monitor(new MonitoredMethodRequest(configuration, "testRecordSqlPreparedStatement", () -> testDao.executePreparedStatement()));
final Map<MetricName, Timer> timers = metric2Registry.getTimers();
assertTrue(timers.keySet().toString(), timers.size() > 1);
assertNotNull(timers.keySet().toString(), timers.get(name("external_request_response_time").type("jdbc").tag("signature", "All").tag("method", "SELECT").build()));
assertNotNull(timers.keySet().toString(), timers.get(name("external_request_response_time").type("jdbc").tag("signature", "ConnectionMonitoringTransformerTest$TestDao#executePreparedStatement").tag("method", "SELECT").build()));
final Map<MetricName, Meter> meters = metric2Registry.getMeters();
assertNotNull(meters.keySet().toString(), meters.get(name("external_requests_rate").tag("request_name", "testRecordSqlPreparedStatement").tag("type", "jdbc").build()));
final CallStackElement callTree = spanContext.getCallTree();
assertEquals("testRecordSqlPreparedStatement", callTree.getSignature());
assertEquals(callTree.toString(), 1, callTree.getChildren().size());
assertEquals("void org.stagemonitor.jdbc.ConnectionMonitoringTransformerTest$TestDao.executePreparedStatement()",
callTree.getChildren().get(0).getSignature());
assertEquals(callTree.toString(), "SELECT * from STAGEMONITOR ", callTree.getChildren().get(0).getChildren().get(0).getSignature());
}
@Test
public void testRecordSqlStatement() throws Exception {
final SpanContextInformation spanContext = requestMonitor
.monitor(new MonitoredMethodRequest(configuration, "testRecordSqlStatement", () -> {
testDao.executeStatement();
}));
final Map<MetricName, Timer> timers = metric2Registry.getTimers();
final String message = timers.keySet().toString();
assertTrue(message, timers.size() > 1);
assertEquals(message, 1, timers.get(name("external_request_response_time").type("jdbc").tag("signature", "ConnectionMonitoringTransformerTest$TestDao#executeStatement").tag("method", "SELECT").build()).getCount());
assertEquals(message, 1, timers.get(name("external_request_response_time").type("jdbc").tag("signature", "All").tag("method", "SELECT").build()).getCount());
final CallStackElement callStack = spanContext.getCallTree();
assertEquals("testRecordSqlStatement", callStack.getSignature());
assertEquals("void org.stagemonitor.jdbc.ConnectionMonitoringTransformerTest$TestDao.executeStatement()",
callStack.getChildren().get(0).getSignature());
assertEquals("SELECT * from STAGEMONITOR ", callStack.getChildren().get(0).getChildren().get(0).getSignature());
}
public static class TestDao {
private final DataSource dataSource;
public TestDao(DataSource dataSource) {
this.dataSource = dataSource;
}
private void executePreparedStatement() throws SQLException {
try (final Connection connection = dataSource.getConnection()) {
final PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from STAGEMONITOR");
preparedStatement.execute();
final ResultSet resultSet = preparedStatement.getResultSet();
resultSet.next();
}
}
private void executeStatement() throws SQLException {
try (final Connection connection = dataSource.getConnection()) {
connection.createStatement().execute("SELECT * from STAGEMONITOR");
}
}
}
}