content: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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
   * <p>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p>
   * 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.jstorm.task;
  
  import com.alibaba.jstorm.cluster.Common;
  import com.alibaba.jstorm.common.metric.AsmHistogram;
  import com.alibaba.jstorm.common.metric.AsmMetric;
  import com.alibaba.jstorm.metric.JStormMetrics;
  import com.alibaba.jstorm.metric.MetricDef;
  import com.alibaba.jstorm.metric.MetricType;
  import com.alibaba.jstorm.metric.MetricUtils;
  import com.alibaba.jstorm.utils.TimeUtils;
  import java.io.Serializable;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.ConcurrentMap;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class TaskBaseMetric implements Serializable {
      private static final Logger logger = LoggerFactory.getLogger(JStormMetrics.class);
  
      private static final long serialVersionUID = -7157987126460293444L;
      private String topologyId;
      private String componentId;
      private int taskId;
  
      /**
       * local metric name cache to avoid frequent metric name concatenation taskId + streamId + name ==> full metric name
       */
      private final ConcurrentMap<String, AsmMetric> metricCache = new ConcurrentHashMap<>();
  
      public TaskBaseMetric(String topologyId, String componentId, int taskId) {
          this.topologyId = topologyId;
          this.componentId = componentId;
          this.taskId = taskId;
          logger.info("init task base metric, tp id:{}, comp id:{}, task id:{}", topologyId, componentId, taskId);
      }
  
      private AsmMetric findMetric(String streamId, String name, MetricType metricType, boolean mergeTopology) {
          //String key = new StringBuilder(30).append(taskId).append(streamId).append(name).toString();
          String key = streamId + name;
          AsmMetric existingMetric = metricCache.get(key);
          if (existingMetric == null) {
              String fullName = MetricUtils.streamMetricName(topologyId, componentId, taskId, streamId, name, metricType);
              existingMetric = JStormMetrics.getStreamMetric(fullName);
              if (existingMetric == null) {
                  existingMetric = AsmMetric.Builder.build(metricType);
                  JStormMetrics.registerStreamMetric(fullName, existingMetric, mergeTopology);
              }
              AsmMetric oldMetric = metricCache.putIfAbsent(key, existingMetric);
              if (oldMetric != null) {
                  existingMetric = oldMetric;
              }
          }
  
          return existingMetric;
      }
  
      public void update(final String streamId, final String name, final Number value,
                         final MetricType metricType, boolean mergeTopology) {
          AsmMetric existingMetric = findMetric(streamId, name, metricType, mergeTopology);
          existingMetric.update(value);
      }
  
      public void updateTime(String streamId, String name, long start, long end, boolean mergeTopology) {
          updateTime(streamId, name, start, end, 1, mergeTopology);
      }
  
      /**
       * almost the same implementation of above update, but improves performance for histograms
       */
      public void updateTime(String streamId, String name, long start, long end, int count, boolean mergeTopology) {
          if (start > 0 && count > 0) {
              AsmMetric existingMetric = findMetric(streamId, name, MetricType.HISTOGRAM, mergeTopology);
  
              if (existingMetric instanceof AsmHistogram) {
                  AsmHistogram histogram = (AsmHistogram) existingMetric;
                  if (histogram.okToUpdate(end)) {
                      long elapsed = ((end - start) * TimeUtils.US_PER_MS) / count;
                      if (elapsed >= 0) {
                          histogram.update(elapsed);
                          histogram.setLastUpdateTime(end);
                      }
                  }
              }
          }
      }
  
      public void update(final String streamId, final String name, final Number value, final MetricType metricType) {
          update(streamId, name, value, metricType, true);
      }
  
      public void update(final String streamId, final String name, final int count, final MetricType metricType) {
          if (count > 0)
              update(streamId, name, count, metricType, true);
      }
  
      public void send_tuple(String stream, int num_out_tasks) {
          if (JStormMetrics.enabled && num_out_tasks > 0) {
              update(stream, MetricDef.EMMITTED_NUM, num_out_tasks, MetricType.COUNTER);
              update(stream, MetricDef.SEND_TPS, num_out_tasks, MetricType.METER);
          }
      }
  
      public void recv_tuple(String component, String stream) {
          recv_tuple(component, stream, 1);
      }
  
      public void recv_tuple(String component, String stream, int tupleNum) {
          if (JStormMetrics.enabled) {
              update(stream, fastConcat(component, MetricDef.RECV_TPS), tupleNum, MetricType.METER);
          }
      }
  
      public void tupleLifeCycle(String component, String stream, long lifeCycleStart, long endTime, int count) {
          if (count > 0) {
              updateTime(stream, fastConcat(component, MetricDef.TUPLE_LIEF_CYCLE), lifeCycleStart, endTime, count, false);
          }
      }
  
      public void bolt_acked_tuple(String component, String stream) {
          bolt_acked_tuple(component, stream, 1);
      }
  
      public void bolt_acked_tuple(String component, String stream, int tupleNum) {
          if (JStormMetrics.enabled) {
              update(stream, MetricDef.ACKED_NUM, tupleNum, MetricType.COUNTER);
          }
      }
  
      public void update_bolt_acked_latency(String component, String stream, long latencyStart, long endTime) {
          update_bolt_acked_latency(component, stream, latencyStart, endTime, 1);
      }
  
      public void update_bolt_acked_latency(String component, String stream, long latencyStart, long endTime, int tupleNum) {
          if (JStormMetrics.enabled) {
              updateTime(stream, MetricDef.PROCESS_LATENCY, latencyStart, endTime, tupleNum, false);
          }
      }
  
      @SuppressWarnings("unused")
      public void bolt_failed_tuple(String component, String stream) {
          if (JStormMetrics.enabled) {
              update(stream, MetricDef.FAILED_NUM, 1, MetricType.COUNTER);
          }
      }
  
      public void boltFailedTuple(String component, String stream, int count) {
          if (JStormMetrics.enabled) {
              update(stream, MetricDef.FAILED_NUM, count, MetricType.COUNTER);
          }
      }
  
      public void spout_acked_tuple(String stream, long latencyStart, long lifeCycleStart, long endTime) {
          if (JStormMetrics.enabled) {
              update(stream, MetricDef.ACKED_NUM, 1, MetricType.COUNTER);
              updateTime(stream, MetricDef.PROCESS_LATENCY, latencyStart, endTime, false);
              updateTime(stream, fastConcat(Common.ACKER_COMPONENT_ID, MetricDef.TUPLE_LIEF_CYCLE), lifeCycleStart, endTime, false);
          }
      }
  
      public void spoutAckedTuple(String stream, int count) {
          if (JStormMetrics.enabled) {
              update(stream, MetricDef.ACKED_NUM, count, MetricType.COUNTER);
          }
      }
  
      private String fastConcat(String componentId, String metricName) {
          StringBuilder sb = new StringBuilder(32);
          return sb.append(componentId).append(".").append(metricName).toString();
      }
  
      public void spout_failed_tuple(String stream) {
          if (JStormMetrics.enabled) {
              update(stream, MetricDef.FAILED_NUM, 1, MetricType.COUNTER);
          }
      }
  
      public void spoutFailedTuple(String stream, int count) {
          if (JStormMetrics.enabled) {
              update(stream, MetricDef.FAILED_NUM, count, MetricType.COUNTER);
          }
      }
  
      public void spoutProcessLatency(String stream, long startTime, long endTime, int count) {
          if (JStormMetrics.enabled)
              updateTime(stream, MetricDef.PROCESS_LATENCY, startTime, endTime, count, false);
      }
  }
  