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.http.stat;
  
  import com.alibaba.druid.support.profile.ProfileStat;
  
  import java.util.Date;
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
  import java.util.concurrent.atomic.AtomicLongFieldUpdater;
  
  import static com.alibaba.druid.util.JdbcSqlStatUtils.get;
  
  public class WebURIStat {
      private final String uri;
  
      private volatile int runningCount;
      private volatile int concurrentMax;
      private volatile long requestCount;
      private volatile long requestTimeNano;
      private volatile long requestTimeNanoMax;
      private volatile long requestTimeNanoMaxOccurTime;
  
      static final AtomicIntegerFieldUpdater<WebURIStat> runningCountUpdater = AtomicIntegerFieldUpdater.newUpdater(WebURIStat.class,
              "runningCount");
      static final AtomicIntegerFieldUpdater<WebURIStat> concurrentMaxUpdater = AtomicIntegerFieldUpdater.newUpdater(WebURIStat.class,
              "concurrentMax");
      static final AtomicLongFieldUpdater<WebURIStat> requestCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "requestCount");
      static final AtomicLongFieldUpdater<WebURIStat> requestTimeNanoUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "requestTimeNano");
      static final AtomicLongFieldUpdater<WebURIStat> requestTimeNanoMaxUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "requestTimeNanoMax");
  
      static final AtomicLongFieldUpdater<WebURIStat> requestTimeNanoMaxOccurTimeUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "requestTimeNanoMaxOccurTime");
  
      private volatile long jdbcFetchRowCount;
      private volatile long jdbcFetchRowPeak;                                                                                       // 单次请求读取行数的峰值
      static final AtomicLongFieldUpdater<WebURIStat> jdbcFetchRowCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcFetchRowCount");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcFetchRowPeakUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcFetchRowPeak");
  
      private volatile long jdbcUpdateCount;
      private volatile long jdbcUpdatePeak;                                                                                         // 单次请求更新行数的峰值
      static final AtomicLongFieldUpdater<WebURIStat> jdbcUpdateCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcUpdateCount");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcUpdatePeakUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcUpdatePeak");
  
      private volatile long jdbcExecuteCount;
      private volatile long jdbcExecuteErrorCount;
      private volatile long jdbcExecutePeak;                                                                                        // 单次请求执行SQL次数的峰值
      private volatile long jdbcExecuteTimeNano;
      static final AtomicLongFieldUpdater<WebURIStat> jdbcExecuteCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcExecuteCount");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcExecuteErrorCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcExecuteErrorCount");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcExecutePeakUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcExecutePeak");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcExecuteTimeNanoUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcExecuteTimeNano");
  
      private volatile long jdbcCommitCount;
      private volatile long jdbcRollbackCount;
      static final AtomicLongFieldUpdater<WebURIStat> jdbcCommitCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcCommitCount");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcRollbackCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcRollbackCount");
  
      private volatile long jdbcPoolConnectionOpenCount;
      private volatile long jdbcPoolConnectionCloseCount;
      static final AtomicLongFieldUpdater<WebURIStat> jdbcPoolConnectionOpenCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcPoolConnectionOpenCount");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcPoolConnectionCloseCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcPoolConnectionCloseCount");
  
      private volatile long jdbcResultSetOpenCount;
      private volatile long jdbcResultSetCloseCount;
  
      private volatile long errorCount;
  
      private volatile long lastAccessTimeMillis = -1L;
  
      private volatile ProfileStat profiletat = new ProfileStat();
  
      static final AtomicLongFieldUpdater<WebURIStat> jdbcResultSetOpenCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcResultSetOpenCount");
      static final AtomicLongFieldUpdater<WebURIStat> jdbcResultSetCloseCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "jdbcResultSetCloseCount");
      static final AtomicLongFieldUpdater<WebURIStat> errorCountUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "errorCount");
      static final AtomicLongFieldUpdater<WebURIStat> lastAccessTimeMillisUpdater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "lastAccessTimeMillis");
  
      private static final ThreadLocal<WebURIStat> currentLocal = new ThreadLocal<WebURIStat>();
  
      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<WebURIStat> histogram_0_1_Updater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_0_1");
      static final AtomicLongFieldUpdater<WebURIStat> histogram_1_10_Updater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_1_10");
      static final AtomicLongFieldUpdater<WebURIStat> histogram_10_100_Updater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_10_100");
      static final AtomicLongFieldUpdater<WebURIStat> histogram_100_1000_Updater = AtomicLongFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_100_1000");
      static final AtomicIntegerFieldUpdater<WebURIStat> histogram_1000_10000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_1000_10000");
      static final AtomicIntegerFieldUpdater<WebURIStat> histogram_10000_100000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_10000_100000");
      static final AtomicIntegerFieldUpdater<WebURIStat> histogram_100000_1000000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_100000_1000000");
      static final AtomicIntegerFieldUpdater<WebURIStat> histogram_1000000_more_Updater = AtomicIntegerFieldUpdater.newUpdater(WebURIStat.class,
              "histogram_1000000_more");
  
      public WebURIStat(String uri) {
          super();
          this.uri = uri;
      }
  
      public static WebURIStat current() {
          return currentLocal.get();
      }
  
      public String getUri() {
          return uri;
      }
  
      public void beforeInvoke() {
          currentLocal.set(this);
  
          int running = runningCountUpdater.incrementAndGet(this);
  
          for (; ; ) {
              int max = concurrentMaxUpdater.get(this);
              if (running > max) {
                  if (concurrentMaxUpdater.compareAndSet(this, max, running)) {
                      break;
                  }
              } else {
                  break;
              }
          }
  
          requestCountUpdater.incrementAndGet(this);
  
          WebRequestStat requestStat = WebRequestStat.current();
          if (requestStat != null) {
              this.setLastAccessTimeMillis(requestStat.getStartMillis());
          }
      }
  
      public void afterInvoke(Throwable error, long nanos) {
          runningCountUpdater.decrementAndGet(this);
          requestTimeNanoUpdater.addAndGet(this, nanos);
  
          for (; ; ) {
              long current = requestTimeNanoMaxUpdater.get(this);
              if (current < nanos) {
                  if (requestTimeNanoMaxUpdater.compareAndSet(this, current, nanos)) {
                      // 可能不准确，但是绝大多数情况下都会正确，性能换取一致性
                      requestTimeNanoMaxOccurTime = System.currentTimeMillis();
  
                      break;
                  } else {
                      continue;
                  }
              } else {
                  break;
              }
          }
  
          histogramRecord(nanos);
  
          if (error != null) {
              errorCountUpdater.incrementAndGet(this);
          }
  
          {
              WebRequestStat localStat = WebRequestStat.current();
              if (localStat != null) {
                  {
                      long fetchRowCount = localStat.getJdbcFetchRowCount();
                      this.addJdbcFetchRowCount(fetchRowCount);
  
                      for (; ; ) {
                          long peak = jdbcFetchRowPeakUpdater.get(this);
                          if (fetchRowCount <= peak) {
                              break;
                          }
  
                          if (jdbcFetchRowPeakUpdater.compareAndSet(this, peak, fetchRowCount)) {
                              break;
                          }
                      }
                  }
                  {
                      long executeCount = localStat.getJdbcExecuteCount();
                      this.addJdbcExecuteCount(executeCount);
  
                      for (; ; ) {
                          long peak = jdbcExecutePeakUpdater.get(this);
                          if (executeCount <= peak) {
                              break;
                          }
  
                          if (jdbcExecutePeakUpdater.compareAndSet(this, peak, executeCount)) {
                              break;
                          }
                      }
                  }
                  {
                      long updateCount = localStat.getJdbcUpdateCount();
                      this.addJdbcUpdateCount(updateCount);
  
                      for (; ; ) {
                          long peak = jdbcUpdatePeakUpdater.get(this);
                          if (updateCount <= peak) {
                              break;
                          }
  
                          if (jdbcUpdatePeakUpdater.compareAndSet(this, peak, updateCount)) {
                              break;
                          }
                      }
                  }
  
                  jdbcExecuteErrorCountUpdater.addAndGet(this, localStat.getJdbcExecuteErrorCount());
                  jdbcExecuteTimeNanoUpdater.addAndGet(this, localStat.getJdbcExecuteTimeNano());
  
                  this.addJdbcPoolConnectionOpenCount(localStat.getJdbcPoolConnectionOpenCount());
                  this.addJdbcPoolConnectionCloseCount(localStat.getJdbcPoolConnectionCloseCount());
  
                  this.addJdbcResultSetOpenCount(localStat.getJdbcResultSetOpenCount());
                  this.addJdbcResultSetCloseCount(localStat.getJdbcResultSetCloseCount());
              }
          }
  
          currentLocal.set(null);
      }
  
      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 int getRunningCount() {
          return this.runningCount;
      }
  
      public long getConcurrentMax() {
          return concurrentMax;
      }
  
      public long getRequestCount() {
          return requestCount;
      }
  
      public long getRequestTimeNano() {
          return requestTimeNano;
      }
  
      public long getRequestTimeMillis() {
          return getRequestTimeNano() / (1000 * 1000);
      }
  
      public void addJdbcFetchRowCount(long delta) {
          jdbcFetchRowCountUpdater.addAndGet(this, delta);
      }
  
      public long getJdbcFetchRowCount() {
          return jdbcFetchRowCount;
      }
  
      public long getJdbcFetchRowPeak() {
          return jdbcFetchRowPeak;
      }
  
      public void addJdbcUpdateCount(long updateCount) {
          jdbcUpdateCountUpdater.addAndGet(this, updateCount);
      }
  
      public long getJdbcUpdateCount() {
          return jdbcUpdateCount;
      }
  
      public long getJdbcUpdatePeak() {
          return jdbcUpdatePeak;
      }
  
      public void incrementJdbcExecuteCount() {
          jdbcExecuteCountUpdater.incrementAndGet(this);
      }
  
      public void addJdbcExecuteCount(long executeCount) {
          jdbcExecuteCountUpdater.addAndGet(this, executeCount);
      }
  
      public long getJdbcExecuteCount() {
          return jdbcExecuteCount;
      }
  
      public long getJdbcExecuteErrorCount() {
          return jdbcExecuteErrorCount;
      }
  
      public long getJdbcExecutePeak() {
          return jdbcExecutePeak;
      }
  
      public long getJdbcExecuteTimeMillis() {
          return getJdbcExecuteTimeNano() / (1000 * 1000);
      }
  
      public long getJdbcExecuteTimeNano() {
          return jdbcExecuteTimeNano;
      }
  
      public void incrementJdbcCommitCount() {
          jdbcCommitCountUpdater.incrementAndGet(this);
      }
  
      public long getJdbcCommitCount() {
          return jdbcCommitCount;
      }
  
      public void incrementJdbcRollbackCount() {
          jdbcRollbackCountUpdater.incrementAndGet(this);
      }
  
      public long getJdbcRollbackCount() {
          return jdbcRollbackCount;
      }
  
      public void setLastAccessTimeMillis(long lastAccessTimeMillis) {
          this.lastAccessTimeMillis = lastAccessTimeMillis;
      }
  
      public Date getLastAccessTime() {
          if (lastAccessTimeMillis < 0L) {
              return null;
          }
  
          return new Date(lastAccessTimeMillis);
      }
  
      public long getLastAccessTimeMillis() {
          return lastAccessTimeMillis;
      }
  
      public long getErrorCount() {
          return errorCount;
      }
  
      public long getJdbcPoolConnectionOpenCount() {
          return jdbcPoolConnectionOpenCount;
      }
  
      public void addJdbcPoolConnectionOpenCount(long delta) {
          jdbcPoolConnectionOpenCountUpdater.addAndGet(this, delta);
      }
  
      public void incrementJdbcPoolConnectionOpenCount() {
          jdbcPoolConnectionOpenCountUpdater.incrementAndGet(this);
      }
  
      public long getJdbcPoolConnectionCloseCount() {
          return jdbcPoolConnectionCloseCount;
      }
  
      public void addJdbcPoolConnectionCloseCount(long delta) {
          jdbcPoolConnectionCloseCountUpdater.addAndGet(this, delta);
      }
  
      public void incrementJdbcPoolConnectionCloseCount() {
          jdbcPoolConnectionCloseCountUpdater.incrementAndGet(this);
      }
  
      public long getJdbcResultSetOpenCount() {
          return jdbcResultSetOpenCount;
      }
  
      public void addJdbcResultSetOpenCount(long delta) {
          jdbcResultSetOpenCountUpdater.addAndGet(this, delta);
      }
  
      public long getJdbcResultSetCloseCount() {
          return jdbcResultSetCloseCount;
      }
  
      public void addJdbcResultSetCloseCount(long delta) {
          jdbcResultSetCloseCountUpdater.addAndGet(this, delta);
      }
  
      public ProfileStat getProfiletat() {
          return profiletat;
      }
  
      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 WebURIStatValue getValue(boolean reset) {
          WebURIStatValue val = new WebURIStatValue();
  
          val.setUri(uri);
  
          val.setRunningCount(runningCount);
          val.setConcurrentMax(get(this, concurrentMaxUpdater, reset));
          val.setRequestCount(get(this, requestCountUpdater, reset));
          val.setRequestTimeNano(get(this, requestTimeNanoUpdater, reset));
          val.setRequestTimeNanoMax(get(this, requestTimeNanoMaxUpdater, reset));
          val.setRequestTimeNanoMaxOccurTime(get(this, requestTimeNanoMaxOccurTimeUpdater, reset));
  
          val.setJdbcFetchRowCount(get(this, jdbcFetchRowCountUpdater, reset));
          val.setJdbcFetchRowPeak(get(this, jdbcFetchRowPeakUpdater, reset));
  
          val.setJdbcUpdateCount(get(this, jdbcUpdateCountUpdater, reset));
          val.setJdbcUpdatePeak(get(this, jdbcUpdatePeakUpdater, reset));
  
          val.setJdbcExecuteCount(get(this, jdbcExecuteCountUpdater, reset));
          val.setJdbcExecuteErrorCount(get(this, jdbcExecuteErrorCountUpdater, reset));
          val.setJdbcExecutePeak(get(this, jdbcExecutePeakUpdater, reset));
          val.setJdbcExecuteTimeNano(get(this, jdbcExecuteTimeNanoUpdater, reset));
  
          val.setJdbcCommitCount(get(this, jdbcCommitCountUpdater, reset));
          val.setJdbcRollbackCount(get(this, jdbcRollbackCountUpdater, reset));
  
          val.setJdbcPoolConnectionOpenCount(get(this, jdbcPoolConnectionOpenCountUpdater, reset));
          val.setJdbcPoolConnectionCloseCount(get(this, jdbcPoolConnectionCloseCountUpdater, reset));
  
          val.setJdbcResultSetOpenCount(get(this, jdbcResultSetOpenCountUpdater, reset));
          val.setJdbcResultSetCloseCount(get(this, jdbcResultSetCloseCountUpdater, reset));
  
          val.setErrorCount(get(this, errorCountUpdater, reset));
  
          val.setLastAccessTimeMillis(get(this, lastAccessTimeMillisUpdater, reset));
  
          val.setProfileEntryStatValueList(this.getProfiletat().getStatValue(reset));
  
          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 Map<String, Object> getStatData() {
          return getValue(false).getStatData();
      }
  }
  