content: /*
   * Copyright 1999-2018 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.support.spring.stat;
  
  import com.alibaba.druid.support.profile.Profiler;
  
  import java.util.Date;
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
  import java.util.concurrent.atomic.AtomicLong;
  import java.util.concurrent.atomic.AtomicLongFieldUpdater;
  
  import static com.alibaba.druid.util.JdbcSqlStatUtils.get;
  
  public class SpringMethodStat {
      private static final ThreadLocal<SpringMethodStat> currentLocal = new ThreadLocal<SpringMethodStat>();
  
      private final SpringMethodInfo methodInfo;
  
      private final AtomicInteger runningCount = new AtomicInteger();
      private final AtomicInteger concurrentMax = new AtomicInteger();
      private final AtomicLong executeCount = new AtomicLong(0);
      private final AtomicLong executeErrorCount = new AtomicLong(0);
      private final AtomicLong executeTimeNano = new AtomicLong();
  
      private final AtomicLong jdbcFetchRowCount = new AtomicLong();
      private final AtomicLong jdbcUpdateCount = new AtomicLong();
      private final AtomicLong jdbcExecuteCount = new AtomicLong();
      private final AtomicLong jdbcExecuteErrorCount = new AtomicLong();
      private final AtomicLong jdbcExecuteTimeNano = new AtomicLong();
  
      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 final AtomicLong jdbcResultSetOpenCount = new AtomicLong();
      private final AtomicLong jdbcResultSetCloseCount = new AtomicLong();
  
      private volatile Throwable lastError;
      private volatile long lastErrorTimeMillis;
  
      private volatile long histogram_0_1;
      private volatile long histogram_1_10;
      private volatile long histogram_10_100;
      private volatile long histogram_100_1000;
      private volatile int histogram_1000_10000;
      private volatile int histogram_10000_100000;
      private volatile int histogram_100000_1000000;
      private volatile int histogram_1000000_more;
  
      static final AtomicLongFieldUpdater<SpringMethodStat> histogram_0_1_Updater = AtomicLongFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_0_1");
      static final AtomicLongFieldUpdater<SpringMethodStat> histogram_1_10_Updater = AtomicLongFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_1_10");
      static final AtomicLongFieldUpdater<SpringMethodStat> histogram_10_100_Updater = AtomicLongFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_10_100");
      static final AtomicLongFieldUpdater<SpringMethodStat> histogram_100_1000_Updater = AtomicLongFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_100_1000");
      static final AtomicIntegerFieldUpdater<SpringMethodStat> histogram_1000_10000_Updater = AtomicIntegerFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_1000_10000");
      static final AtomicIntegerFieldUpdater<SpringMethodStat> histogram_10000_100000_Updater = AtomicIntegerFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_10000_100000");
      static final AtomicIntegerFieldUpdater<SpringMethodStat> histogram_100000_1000000_Updater = AtomicIntegerFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_100000_1000000");
      static final AtomicIntegerFieldUpdater<SpringMethodStat> histogram_1000000_more_Updater = AtomicIntegerFieldUpdater.newUpdater(SpringMethodStat.class,
              "histogram_1000000_more");
  
      public SpringMethodStat(SpringMethodInfo methodInfo) {
          this.methodInfo = methodInfo;
      }
  
      public SpringMethodStatValue getStatValue(boolean reset) {
          SpringMethodStatValue val = new SpringMethodStatValue();
  
          val.setClassName(this.getMethodInfo().getClassName());
          val.setSignature(this.getMethodInfo().getSignature());
  
          val.setRunningCount(this.getRunningCount());
  
          val.setConcurrentMax(get(this.concurrentMax, reset));
          val.setExecuteCount(get(this.executeCount, reset));
          val.setExecuteErrorCount(get(this.executeErrorCount, reset));
          val.setExecuteTimeNano(get(this.executeTimeNano, reset));
  
          val.setJdbcFetchRowCount(get(this.jdbcFetchRowCount, reset));
          val.setJdbcUpdateCount(get(this.jdbcUpdateCount, reset));
          val.setJdbcExecuteCount(get(this.jdbcExecuteCount, reset));
          val.setJdbcExecuteErrorCount(get(this.jdbcExecuteErrorCount, reset));
          val.setJdbcExecuteTimeNano(get(this.jdbcExecuteTimeNano, reset));
  
          val.setJdbcCommitCount(get(this.jdbcCommitCount, reset));
          val.setJdbcRollbackCount(get(this.jdbcRollbackCount, reset));
  
          val.setJdbcPoolConnectionOpenCount(get(this.jdbcPoolConnectionOpenCount, reset));
          val.setJdbcPoolConnectionCloseCount(get(this.jdbcPoolConnectionCloseCount, reset));
  
          val.setJdbcResultSetOpenCount(get(this.jdbcResultSetOpenCount, reset));
          val.setJdbcResultSetCloseCount(get(this.jdbcResultSetCloseCount, reset));
  
          val.setLastError(this.lastError);
          val.setLastErrorTimeMillis(this.lastErrorTimeMillis);
          if (reset) {
              this.lastError = null;
              this.lastErrorTimeMillis = 0;
          }
  
          val.histogram_0_1 = get(this, histogram_0_1_Updater, reset);
          val.histogram_1_10 = get(this, histogram_1_10_Updater, reset);
          val.histogram_10_100 = get(this, histogram_10_100_Updater, reset);
          val.histogram_100_1000 = get(this, histogram_100_1000_Updater, reset);
          val.histogram_1000_10000 = get(this, histogram_1000_10000_Updater, reset);
          val.histogram_10000_100000 = get(this, histogram_10000_100000_Updater, reset);
          val.histogram_100000_1000000 = get(this, histogram_100000_1000000_Updater, reset);
          val.histogram_1000000_more = get(this, histogram_1000000_more_Updater, reset);
  
          return val;
      }
  
      public void reset() {
          concurrentMax.set(0);
          executeCount.set(0);
          executeErrorCount.set(0);
          executeTimeNano.set(0);
  
          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);
  
          jdbcResultSetOpenCount.set(0);
          jdbcResultSetCloseCount.set(0);
  
          lastError = null;
          lastErrorTimeMillis = 0;
  
          histogram_0_1_Updater.set(this, 0);
          histogram_1_10_Updater.set(this, 0);
          histogram_10_100_Updater.set(this, 0);
          histogram_100_1000_Updater.set(this, 0);
          histogram_1000_10000_Updater.set(this, 0);
          histogram_10000_100000_Updater.set(this, 0);
          histogram_100000_1000000_Updater.set(this, 0);
          histogram_1000000_more_Updater.set(this, 0);
      }
  
      public SpringMethodInfo getMethodInfo() {
          return methodInfo;
      }
  
      public static SpringMethodStat current() {
          return currentLocal.get();
      }
  
      public static void setCurrent(SpringMethodStat current) {
          currentLocal.set(current);
      }
  
      public void beforeInvoke() {
          currentLocal.set(this);
  
          int running = runningCount.incrementAndGet();
  
          for (; ; ) {
              int max = concurrentMax.get();
              if (running > max) {
                  if (concurrentMax.compareAndSet(max, running)) {
                      break;
                  }
              } else {
                  break;
              }
          }
  
          executeCount.incrementAndGet();
  
          Profiler.enter(methodInfo.getSignature(), Profiler.PROFILE_TYPE_SPRING);
      }
  
      public void afterInvoke(Throwable error, long nanos) {
          runningCount.decrementAndGet();
          executeTimeNano.addAndGet(nanos);
          histogramRecord(nanos);
  
          if (error != null) {
              executeErrorCount.incrementAndGet();
              lastError = error;
              lastErrorTimeMillis = System.currentTimeMillis();
          }
  
          Profiler.release(nanos);
      }
  
      private void histogramRecord(long nanos) {
          final long millis = nanos / 1000 / 1000;
  
          if (millis < 1) {
              histogram_0_1_Updater.incrementAndGet(this);
          } else if (millis < 10) {
              histogram_1_10_Updater.incrementAndGet(this);
          } else if (millis < 100) {
              histogram_10_100_Updater.incrementAndGet(this);
          } else if (millis < 1000) {
              histogram_100_1000_Updater.incrementAndGet(this);
          } else if (millis < 10000) {
              histogram_1000_10000_Updater.incrementAndGet(this);
          } else if (millis < 100000) {
              histogram_10000_100000_Updater.incrementAndGet(this);
          } else if (millis < 1000000) {
              histogram_100000_1000000_Updater.incrementAndGet(this);
          } else {
              histogram_1000000_more_Updater.incrementAndGet(this);
          }
      }
  
      public long[] getHistogramValues() {
          return new long[]{
                  //
                  histogram_0_1, //
                  histogram_1_10, //
                  histogram_10_100, //
                  histogram_100_1000, //
                  histogram_1000_10000, //
                  histogram_10000_100000, //
                  histogram_100000_1000000, //
                  histogram_1000000_more //
          };
      }
  
      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();
      }
  
      public int getConcurrentMax() {
          return concurrentMax.get();
      }
  
      public long getExecuteCount() {
          return executeCount.get();
      }
  
      public long getExecuteErrorCount() {
          return executeErrorCount.get();
      }
  
      public long getExecuteTimeNano() {
          return executeTimeNano.get();
      }
  
      public long getExecuteTimeMillis() {
          return getExecuteTimeNano() / (1000 * 1000);
      }
  
      public void addJdbcFetchRowCount(long delta) {
          this.jdbcFetchRowCount.addAndGet(delta);
      }
  
      public long getJdbcFetchRowCount() {
          return jdbcFetchRowCount.get();
      }
  
      public void addJdbcUpdateCount(long updateCount) {
          this.jdbcUpdateCount.addAndGet(updateCount);
      }
  
      public long getJdbcUpdateCount() {
          return jdbcUpdateCount.get();
      }
  
      public void incrementJdbcExecuteCount() {
          jdbcExecuteCount.incrementAndGet();
      }
  
      public void addJdbcExecuteCount(long executeCount) {
          jdbcExecuteCount.addAndGet(executeCount);
      }
  
      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();
      }
  
      public long getJdbcExecuteTimeMillis() {
          return getJdbcExecuteTimeNano() / (1000 * 1000);
      }
  
      public long getJdbcExecuteTimeNano() {
          return jdbcExecuteTimeNano.get();
      }
  
      public void addJdbcExecuteTimeNano(long nano) {
          jdbcExecuteTimeNano.addAndGet(nano);
      }
  
      public void incrementJdbcCommitCount() {
          jdbcCommitCount.incrementAndGet();
      }
  
      public long getJdbcCommitCount() {
          return jdbcCommitCount.get();
      }
  
      public void addJdbcCommitCount(long commitCount) {
          this.jdbcCommitCount.addAndGet(commitCount);
      }
  
      public void incrementJdbcRollbackCount() {
          jdbcRollbackCount.incrementAndGet();
      }
  
      public long getJdbcRollbackCount() {
          return jdbcRollbackCount.get();
      }
  
      public void addJdbcRollbackCount(long rollbackCount) {
          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 long getJdbcResultSetOpenCount() {
          return jdbcResultSetOpenCount.get();
      }
  
      public void addJdbcResultSetOpenCount(long delta) {
          jdbcResultSetOpenCount.addAndGet(delta);
      }
  
      public void incrementJdbcResultSetOpenCount() {
          jdbcResultSetOpenCount.incrementAndGet();
      }
  
      public long getJdbcResultSetCloseCount() {
          return jdbcResultSetCloseCount.get();
      }
  
      public void addJdbcResultSetCloseCount(long delta) {
          jdbcResultSetCloseCount.addAndGet(delta);
      }
  
      public void incrementJdbcResultSetCloseCount() {
          jdbcResultSetCloseCount.incrementAndGet();
      }
  
      public Map<String, Object> getStatData() {
          return getStatValue(false).getData();
      }
  }
  