mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
improve stat
This commit is contained in:
@@ -116,7 +116,7 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean {
|
||||
public void setMergeSql(boolean mergeSql) {
|
||||
this.mergeSql = mergeSql;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public String mergeSql(String sql) {
|
||||
return this.mergeSql(sql, dbType);
|
||||
@@ -563,17 +563,19 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean {
|
||||
dataSourceStat.getResultSetStat().beforeOpen();
|
||||
|
||||
resultSet.setConstructNano();
|
||||
|
||||
StatFilterContext.getInstance().resultSet_open();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resultSet_close(FilterChain chain, ResultSetProxy resultSet) throws SQLException {
|
||||
|
||||
long nanoSpan = System.nanoTime() - resultSet.getConstructNano();
|
||||
long nanos = System.nanoTime() - resultSet.getConstructNano();
|
||||
|
||||
int fetchRowCount = resultSet.getFetchRowCount();
|
||||
|
||||
JdbcDataSourceStat dataSourceStat = chain.getDataSource().getDataSourceStat();
|
||||
dataSourceStat.getResultSetStat().afterClose(nanoSpan);
|
||||
dataSourceStat.getResultSetStat().afterClose(nanos);
|
||||
dataSourceStat.getResultSetStat().addFetchRowCount(fetchRowCount);
|
||||
dataSourceStat.getResultSetStat().incrementCloseCounter();
|
||||
|
||||
@@ -585,11 +587,13 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean {
|
||||
if (sqlStat != null && resultSet.getCloseCount() == 0) {
|
||||
sqlStat.addFetchRowCount(fetchRowCount);
|
||||
long stmtExecuteNano = resultSet.getStatementProxy().getLastExecuteTimeNano();
|
||||
sqlStat.addResultSetHoldTimeNano(stmtExecuteNano, nanoSpan);
|
||||
sqlStat.addResultSetHoldTimeNano(stmtExecuteNano, nanos);
|
||||
}
|
||||
}
|
||||
|
||||
chain.resultSet_close(resultSet);
|
||||
|
||||
StatFilterContext.getInstance().resultSet_close(nanos);
|
||||
}
|
||||
|
||||
public JdbcConnectionStat.Entry getConnectionInfo(ConnectionProxy connection) {
|
||||
@@ -626,11 +630,11 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean {
|
||||
return dataSourceStat.createSqlStat(contextSql);
|
||||
} else {
|
||||
String dbType = this.dbType;
|
||||
|
||||
|
||||
if (dbType == null) {
|
||||
dbType = dataSource.getDbType();
|
||||
}
|
||||
|
||||
|
||||
sql = mergeSql(sql, dbType);
|
||||
return dataSourceStat.createSqlStat(sql);
|
||||
}
|
||||
@@ -654,6 +658,8 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean {
|
||||
long millis = nanos / (1000L * 1000L);
|
||||
JdbcDataSourceStat dataSourceStat = chain.getDataSource().getDataSourceStat();
|
||||
dataSourceStat.getConnectionHoldHistogram().record(millis);
|
||||
|
||||
StatFilterContext.getInstance().pool_connection_close(nanos);
|
||||
}
|
||||
|
||||
public DruidPooledConnection dataSource_getConnection(FilterChain chain, DruidDataSource dataSource,
|
||||
@@ -662,6 +668,8 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean {
|
||||
|
||||
if (conn != null) {
|
||||
conn.setConnectedTimeNano(System.nanoTime());
|
||||
|
||||
StatFilterContext.getInstance().pool_connection_open();
|
||||
}
|
||||
|
||||
return conn;
|
||||
|
||||
@@ -54,10 +54,58 @@ public class StatFilterContext {
|
||||
}
|
||||
|
||||
public void commit() {
|
||||
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void rollback() {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
public void pool_connection_open() {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.pool_connect();
|
||||
}
|
||||
}
|
||||
|
||||
public void pool_connection_close(long nanos) {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.pool_close(nanos);
|
||||
}
|
||||
}
|
||||
|
||||
public void physical_connection_connect() {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.physical_connection_connect();
|
||||
}
|
||||
}
|
||||
|
||||
public void physical_connection_close(long nanos) {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.physical_connection_close(nanos);
|
||||
}
|
||||
}
|
||||
|
||||
public void resultSet_open() {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.resultSet_open();
|
||||
}
|
||||
}
|
||||
|
||||
public void resultSet_close(long nanos) {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
StatFilterContextListener listener = listeners.get(i);
|
||||
listener.resultSet_close(nanos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,16 @@ public interface StatFilterContextListener {
|
||||
void commit();
|
||||
|
||||
void rollback();
|
||||
|
||||
void pool_connect();
|
||||
|
||||
void pool_close(long nanos);
|
||||
|
||||
void physical_connection_connect();
|
||||
|
||||
void physical_connection_close(long nanos);
|
||||
|
||||
void resultSet_open();
|
||||
|
||||
void resultSet_close(long nanos);
|
||||
}
|
||||
|
||||
@@ -32,4 +32,35 @@ public class StatFilterContextListenerAdapter implements StatFilterContextListen
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pool_connect() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pool_close(long nanos) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void physical_connection_connect() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void physical_connection_close(long nanos) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resultSet_open() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resultSet_close(long nanos) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -452,6 +452,48 @@ public class WebStatFilter implements Filter {
|
||||
springMethodStat.incrementJdbcRollbackCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pool_connect() {
|
||||
WebRequestStat reqStat = WebRequestStat.current();
|
||||
if (reqStat != null) {
|
||||
reqStat.incrementJdbcPoolConnectCount();
|
||||
}
|
||||
|
||||
SpringMethodStat springMethodStat = SpringMethodStat.current();
|
||||
if (springMethodStat != null) {
|
||||
springMethodStat.incrementJdbcPoolConnectionOpenCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pool_close(long nanos) {
|
||||
WebRequestStat reqStat = WebRequestStat.current();
|
||||
if (reqStat != null) {
|
||||
reqStat.incrementJdbcPoolCloseCount();
|
||||
}
|
||||
|
||||
SpringMethodStat springMethodStat = SpringMethodStat.current();
|
||||
if (springMethodStat != null) {
|
||||
springMethodStat.incrementJdbcPoolConnectionOpenCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resultSet_open() {
|
||||
WebRequestStat reqStat = WebRequestStat.current();
|
||||
if (reqStat != null) {
|
||||
reqStat.incrementJdbcResultSetOpenCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resultSet_close(long nanos) {
|
||||
WebRequestStat reqStat = WebRequestStat.current();
|
||||
if (reqStat != null) {
|
||||
reqStat.incrementJdbcResultSetCloseCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final static class StatHttpServletResponseWrapper extends HttpServletResponseWrapper implements HttpServletResponse {
|
||||
|
||||
@@ -13,6 +13,12 @@ public class WebRequestStat {
|
||||
private long jdbcExecuteNano;
|
||||
private long jdbcExecuteErrorCount;
|
||||
|
||||
private long jdbcPoolConnectCount;
|
||||
private long jdbcPoolCloseCount;
|
||||
|
||||
private long jdbcResultSetOpenCount;
|
||||
private long jdbcResultSetCloseCount;
|
||||
|
||||
public WebRequestStat(){
|
||||
|
||||
}
|
||||
@@ -115,4 +121,37 @@ public class WebRequestStat {
|
||||
public void incrementJdbcExecuteErrorCount() {
|
||||
jdbcExecuteErrorCount++;
|
||||
}
|
||||
|
||||
public long getJdbcPoolConnectionOpenCount() {
|
||||
return jdbcPoolConnectCount;
|
||||
}
|
||||
|
||||
public void incrementJdbcPoolConnectCount() {
|
||||
this.jdbcPoolConnectCount++;
|
||||
}
|
||||
|
||||
public long getJdbcPoolConnectionCloseCount() {
|
||||
return jdbcPoolCloseCount;
|
||||
}
|
||||
|
||||
public void incrementJdbcPoolCloseCount() {
|
||||
this.jdbcPoolCloseCount++;
|
||||
}
|
||||
|
||||
public long getJdbcResultSetOpenCount() {
|
||||
return jdbcResultSetOpenCount;
|
||||
}
|
||||
|
||||
public void incrementJdbcResultSetOpenCount() {
|
||||
this.jdbcResultSetOpenCount++;
|
||||
}
|
||||
|
||||
public long getJdbcResultSetCloseCount() {
|
||||
return jdbcResultSetCloseCount;
|
||||
}
|
||||
|
||||
public void incrementJdbcResultSetCloseCount() {
|
||||
this.jdbcResultSetCloseCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,30 +10,36 @@ public class WebURIStat {
|
||||
|
||||
private final String uri;
|
||||
|
||||
private final AtomicInteger runningCount = new AtomicInteger();
|
||||
private final AtomicInteger concurrentMax = new AtomicInteger();
|
||||
private final AtomicLong requestCount = new AtomicLong(0);
|
||||
private final AtomicLong requestTimeNano = new AtomicLong();
|
||||
private final AtomicInteger runningCount = new AtomicInteger();
|
||||
private final AtomicInteger concurrentMax = new AtomicInteger();
|
||||
private final AtomicLong requestCount = new AtomicLong(0);
|
||||
private final AtomicLong requestTimeNano = new AtomicLong();
|
||||
|
||||
private final AtomicLong jdbcFetchRowCount = new AtomicLong();
|
||||
private final AtomicLong jdbcFetchRowPeak = new AtomicLong(); // 单次请求读取行数的峰值
|
||||
private final AtomicLong jdbcFetchRowCount = new AtomicLong();
|
||||
private final AtomicLong jdbcFetchRowPeak = new AtomicLong(); // 单次请求读取行数的峰值
|
||||
|
||||
private final AtomicLong jdbcUpdateCount = new AtomicLong();
|
||||
private final AtomicLong jdbcUpdatePeak = new AtomicLong(); // 单次请求更新行数的峰值
|
||||
private final AtomicLong jdbcUpdateCount = new AtomicLong();
|
||||
private final AtomicLong jdbcUpdatePeak = new AtomicLong(); // 单次请求更新行数的峰值
|
||||
|
||||
private final AtomicLong jdbcExecuteCount = new AtomicLong();
|
||||
private final AtomicLong jdbcExecuteErrorCount = new AtomicLong();
|
||||
private final AtomicLong jdbcExecutePeak = new AtomicLong(); // 单次请求执行SQL次数的峰值
|
||||
private final AtomicLong jdbcExecuteTimeNano = new AtomicLong();
|
||||
private final AtomicLong jdbcExecuteCount = new AtomicLong();
|
||||
private final AtomicLong jdbcExecuteErrorCount = new AtomicLong();
|
||||
private final AtomicLong jdbcExecutePeak = new AtomicLong(); // 单次请求执行SQL次数的峰值
|
||||
private final AtomicLong jdbcExecuteTimeNano = new AtomicLong();
|
||||
|
||||
private final AtomicLong jdbcCommitCount = new AtomicLong();
|
||||
private final AtomicLong jdbcRollbackCount = new AtomicLong();
|
||||
private final AtomicLong jdbcCommitCount = new AtomicLong();
|
||||
private final AtomicLong jdbcRollbackCount = new AtomicLong();
|
||||
|
||||
private final AtomicLong errorCount = new AtomicLong();
|
||||
private final AtomicLong jdbcPoolConnectionOpenCount = new AtomicLong();
|
||||
private final AtomicLong jdbcPoolConnectionCloseCount = new AtomicLong();
|
||||
|
||||
private volatile long lastAccessTimeMillis = -1L;
|
||||
private final AtomicLong jdbcResultSetOpenCount = new AtomicLong();
|
||||
private final AtomicLong jdbcResultSetCloseCount = new AtomicLong();
|
||||
|
||||
private final static ThreadLocal<WebURIStat> currentLocal = new ThreadLocal<WebURIStat>();
|
||||
private final AtomicLong errorCount = new AtomicLong();
|
||||
|
||||
private volatile long lastAccessTimeMillis = -1L;
|
||||
|
||||
private final static ThreadLocal<WebURIStat> currentLocal = new ThreadLocal<WebURIStat>();
|
||||
|
||||
public WebURIStat(String uri){
|
||||
super();
|
||||
@@ -131,12 +137,13 @@ public class WebURIStat {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
long jdbcExecuteErrorCount = localStat.getJdbcExecuteErrorCount();
|
||||
if (jdbcExecuteErrorCount > 0) {
|
||||
this.jdbcExecuteErrorCount.addAndGet(jdbcExecuteErrorCount);
|
||||
}
|
||||
}
|
||||
this.jdbcExecuteErrorCount.addAndGet(localStat.getJdbcExecuteErrorCount());
|
||||
|
||||
this.addJdbcPoolConnectionOpenCount(localStat.getJdbcPoolConnectionOpenCount());
|
||||
this.addJdbcPoolConnectionCloseCount(localStat.getJdbcPoolConnectionCloseCount());
|
||||
|
||||
this.addJdbcResultSetOpenCount(localStat.getJdbcResultSetOpenCount());
|
||||
this.addJdbcResultSetCloseCount(localStat.getJdbcResultSetCloseCount());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +258,46 @@ public class WebURIStat {
|
||||
return errorCount.get();
|
||||
}
|
||||
|
||||
public long getJdbcPoolConnectionOpenCount() {
|
||||
return jdbcPoolConnectionOpenCount.get();
|
||||
}
|
||||
|
||||
public void addJdbcPoolConnectionOpenCount(long delta) {
|
||||
jdbcPoolConnectionOpenCount.addAndGet(delta);
|
||||
}
|
||||
|
||||
public void incrementJdbcPoolConnectionOpenCount() {
|
||||
jdbcPoolConnectionOpenCount.incrementAndGet();
|
||||
}
|
||||
|
||||
public long getJdbcPoolConnectionCloseCount() {
|
||||
return jdbcPoolConnectionCloseCount.get();
|
||||
}
|
||||
|
||||
public void addJdbcPoolConnectionCloseCount(long delta) {
|
||||
jdbcPoolConnectionCloseCount.addAndGet(delta);
|
||||
}
|
||||
|
||||
public void incrementJdbcPoolConnectionCloseCount() {
|
||||
jdbcPoolConnectionCloseCount.incrementAndGet();
|
||||
}
|
||||
|
||||
public long getJdbcResultSetOpenCount() {
|
||||
return jdbcResultSetOpenCount.get();
|
||||
}
|
||||
|
||||
public void addJdbcResultSetOpenCount(long delta) {
|
||||
jdbcResultSetOpenCount.addAndGet(delta);
|
||||
}
|
||||
|
||||
public long getJdbcResultSetCloseCount() {
|
||||
return jdbcResultSetCloseCount.get();
|
||||
}
|
||||
|
||||
public void addJdbcResultSetCloseCount(long delta) {
|
||||
jdbcResultSetCloseCount.addAndGet(delta);
|
||||
}
|
||||
|
||||
public Map<String, Object> getStatData() {
|
||||
Map<String, Object> data = new LinkedHashMap<String, Object>();
|
||||
|
||||
@@ -276,6 +323,12 @@ public class WebURIStat {
|
||||
data.put("JdbcUpdateCount", this.getJdbcUpdateCount());
|
||||
data.put("JdbcUpdatePeak", this.getJdbcUpdatePeak());
|
||||
|
||||
data.put("JdbcPoolConnectionOpenCount", this.getJdbcPoolConnectionOpenCount());
|
||||
data.put("JdbcPoolConnectionCloseCount", this.getJdbcPoolConnectionCloseCount());
|
||||
|
||||
data.put("JdbcResultSetOpenCount", this.getJdbcResultSetOpenCount());
|
||||
data.put("JdbcResultSetCloseCount", this.getJdbcResultSetCloseCount());
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.alibaba.druid.support.spring.stat;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -26,6 +27,12 @@ public class SpringMethodStat {
|
||||
private final AtomicLong jdbcCommitCount = new AtomicLong();
|
||||
private final AtomicLong jdbcRollbackCount = new AtomicLong();
|
||||
|
||||
private final AtomicLong jdbcPoolConnectionOpenCount = new AtomicLong();
|
||||
private final AtomicLong jdbcPoolConnectionCloseCount = new AtomicLong();
|
||||
|
||||
private volatile Throwable lastError;
|
||||
private volatile long lastErrorTimeMillis;
|
||||
|
||||
public SpringMethodStat(SpringMethodInfo methodInfo){
|
||||
this.methodInfo = methodInfo;
|
||||
}
|
||||
@@ -39,10 +46,17 @@ public class SpringMethodStat {
|
||||
jdbcFetchRowCount.set(0);
|
||||
jdbcUpdateCount.set(0);
|
||||
jdbcExecuteCount.set(0);
|
||||
jdbcExecuteErrorCount.set(0);
|
||||
jdbcExecuteTimeNano.set(0);
|
||||
|
||||
jdbcCommitCount.set(0);
|
||||
jdbcRollbackCount.set(0);
|
||||
|
||||
jdbcPoolConnectionOpenCount.set(0);
|
||||
jdbcPoolConnectionCloseCount.set(0);
|
||||
|
||||
lastError = null;
|
||||
lastErrorTimeMillis = 0;
|
||||
}
|
||||
|
||||
public SpringMethodInfo getMethodInfo() {
|
||||
@@ -84,9 +98,27 @@ public class SpringMethodStat {
|
||||
|
||||
if (error != null) {
|
||||
executeErrorCount.incrementAndGet();
|
||||
lastError = error;
|
||||
lastErrorTimeMillis = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
public Throwable getLastError() {
|
||||
return lastError;
|
||||
}
|
||||
|
||||
public Date getLastErrorTime() {
|
||||
if (lastErrorTimeMillis <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Date(lastErrorTimeMillis);
|
||||
}
|
||||
|
||||
public long getLastErrorTimeMillis() {
|
||||
return lastErrorTimeMillis;
|
||||
}
|
||||
|
||||
public int getRunningCount() {
|
||||
return this.runningCount.get();
|
||||
}
|
||||
@@ -138,15 +170,15 @@ public class SpringMethodStat {
|
||||
public long getJdbcExecuteCount() {
|
||||
return jdbcExecuteCount.get();
|
||||
}
|
||||
|
||||
|
||||
public long getJdbcExecuteErrorCount() {
|
||||
return jdbcExecuteErrorCount.get();
|
||||
}
|
||||
|
||||
|
||||
public void addJdbcExecuteErrorCount(long executeCount) {
|
||||
jdbcExecuteErrorCount.addAndGet(executeCount);
|
||||
}
|
||||
|
||||
|
||||
public void incrementJdbcExecuteErrorCount() {
|
||||
jdbcExecuteErrorCount.incrementAndGet();
|
||||
}
|
||||
@@ -187,6 +219,30 @@ public class SpringMethodStat {
|
||||
this.jdbcRollbackCount.addAndGet(rollbackCount);
|
||||
}
|
||||
|
||||
public long getJdbcPoolConnectionOpenCount() {
|
||||
return jdbcPoolConnectionOpenCount.get();
|
||||
}
|
||||
|
||||
public void addJdbcPoolConnectionOpenCount(long delta) {
|
||||
jdbcPoolConnectionOpenCount.addAndGet(delta);
|
||||
}
|
||||
|
||||
public void incrementJdbcPoolConnectionOpenCount() {
|
||||
jdbcPoolConnectionOpenCount.incrementAndGet();
|
||||
}
|
||||
|
||||
public long getJdbcPoolConnectionCloseCount() {
|
||||
return jdbcPoolConnectionCloseCount.get();
|
||||
}
|
||||
|
||||
public void addJdbcPoolConnectionCloseCount(long delta) {
|
||||
jdbcPoolConnectionCloseCount.addAndGet(delta);
|
||||
}
|
||||
|
||||
public void incrementJdbcPoolConnectionCloseCount() {
|
||||
jdbcPoolConnectionCloseCount.incrementAndGet();
|
||||
}
|
||||
|
||||
public Map<String, Object> getStatData() {
|
||||
Map<String, Object> data = new LinkedHashMap<String, Object>();
|
||||
|
||||
@@ -202,12 +258,18 @@ public class SpringMethodStat {
|
||||
data.put("JdbcCommitCount", this.getJdbcCommitCount());
|
||||
data.put("JdbcRollbackCount", this.getJdbcRollbackCount());
|
||||
|
||||
data.put("JdbcPoolConnectionOpenCount", this.getJdbcPoolConnectionOpenCount());
|
||||
data.put("JdbcPoolConnectionCloseCount", this.getJdbcPoolConnectionCloseCount());
|
||||
|
||||
data.put("JdbcExecuteCount", this.getJdbcExecuteCount());
|
||||
data.put("JdbcExecuteErrorCount", this.getJdbcExecuteErrorCount());
|
||||
data.put("JdbcExecuteTimeMillis", this.getJdbcExecuteTimeMillis());
|
||||
data.put("JdbcFetchRowCount", this.getJdbcFetchRowCount());
|
||||
data.put("JdbcUpdateCount", this.getJdbcUpdateCount());
|
||||
|
||||
data.put("LastError", this.getLastError());
|
||||
data.put("LastErrorTime", this.getLastErrorTime());
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user