github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/sawtooth_validator/metrics/metrics.py (about)

     1  # Copyright 2018 Intel Corporation
     2  #
     3  # Licensed under the Apache License, Version 2.0 (the "License");
     4  # you may not use this file except in compliance with the License.
     5  # You may obtain a copy of the License at
     6  #
     7  #     http://www.apache.org/licenses/LICENSE-2.0
     8  #
     9  # Unless required by applicable law or agreed to in writing, software
    10  # distributed under the License is distributed on an "AS IS" BASIS,
    11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  # See the License for the specific language governing permissions and
    13  # limitations under the License.
    14  # ------------------------------------------------------------------------------
    15  """
    16  Metric types
    17  ------------
    18  
    19  Currently, three types of metrics are supported: gauge, counter, and timer.
    20  
    21  - Gauge: Used to record a value that changes arbitrarily.
    22  - Counter: Used to record a value that increments or decrements.
    23  - Timer: Used to record the duration of tasks.
    24  
    25  To add more metric types, corresponding mock metrics must be added to the end
    26  of metrics.py as these mocks are used when metric reporting is disabled.
    27  
    28  API Usage
    29  ---------
    30  
    31  To create a new metric, a handle to the metrics collector must be created at
    32  the beggining of the module with:
    33  
    34      from sawtooth_validator import metrics
    35      COLLECTOR = metrics.get_collector(__name__)
    36  
    37  This creates a new handle which will tag all metrics created with the handle
    38  using the full module name. To create a new metric, call the function with the
    39  corresponding name:
    40  
    41      important_value_gauge = COLLECTOR.gauge("important_value")
    42  
    43  The above creates a metric named
    44  `"sawtooth_validator.module_name.important_value"` and tags it with the
    45  hostname where the validator is running. If metrics reporting for this metric
    46  is disabled, a mock object is returned which implements the same API as the
    47  regular metric object. **Note:** Mock objects do not maintain state.
    48  
    49  If the metric is part of a class, the instance should be passed in when the
    50  metric is created like so:
    51  
    52      important_instance_value_counter = COLLECTOR.counter(
    53          "important_instance_value", instance=self)
    54  
    55  This automatically adds the class name to the metric name.
    56  
    57  Additionally, a metrics reporting level can be set and additional tags can be
    58  added when it is created using the `level` and `tags` parameters:
    59  
    60      important_timer = COLLECTOR.timer(
    61          "important_timer",
    62          instance=self,
    63          level=metrics.DEBUG,
    64          tags={
    65              "name": self.name,
    66              "pid": os.getpid(),
    67              "instance": id(self),
    68          })
    69  
    70  Tags should be used to separate metrics from multiple sources that are
    71  collected in the same place. For example, `InstrumentedThreadPoolExecutor` uses
    72  tags to distinguish threadpool metrics by the threadpool's name. While you
    73  could also separate out these metrics by instance id, adding a name tag makes
    74  interpreting the metrics much easier.
    75  """
    76  
    77  import platform
    78  
    79  
    80  DEBUG = 10
    81  INFO = 20
    82  PERF = 30
    83  DEFAULT = INFO
    84  
    85  
    86  def init_metrics(level=None, registry=None):
    87      """
    88      Initialize metrics reporting with the given level and registry. This
    89      should be called before get_collector().
    90      """
    91      MetricsCollector.set_instance(level, registry)
    92  
    93  
    94  def get_collector(module_name):
    95      """
    96      Get a handle to the metrics collector.
    97      """
    98      return MetricsCollectorHandle(module_name.split(".")[-1])
    99  
   100  
   101  class MetricsCollector:
   102      __instance = None
   103  
   104      @classmethod
   105      def set_instance(cls, level=None, registry=None):
   106          cls.__instance = cls(level, registry)
   107  
   108      @classmethod
   109      def get_instance(cls, level=None, registry=None):
   110          if cls.__instance is None:
   111              cls.set_instance(level, registry)
   112          return cls.__instance
   113  
   114      def __init__(self, level=None, registry=None):
   115          if level is None:
   116              level = DEFAULT
   117          self._level = level
   118  
   119          self._noop_registry = NoOpMetricsRegistry()
   120          self._registry = registry
   121  
   122          self._base_tags = (
   123              ("host", platform.node()),
   124          )
   125  
   126      def gauge(self, identifier, level, instance=None, tags=None):
   127          if self._registry is None or self._disabled(identifier, level):
   128              return self._noop_registry.gauge(identifier)
   129  
   130          return self._registry.gauge(
   131              self._join(identifier, instance, tags))
   132  
   133      def counter(self, identifier, level, instance=None, tags=None):
   134          if self._registry is None or self._disabled(identifier, level):
   135              return self._noop_registry.counter(identifier)
   136  
   137          return self._registry.counter(
   138              self._join(identifier, instance, tags))
   139  
   140      def timer(self, identifier, level, instance=None, tags=None):
   141          if self._registry is None or self._disabled(identifier, level):
   142              return self._noop_registry.timer(identifier)
   143  
   144          return self._registry.timer(
   145              self._join(identifier, instance, tags))
   146  
   147      # Private methods
   148      def _disabled(self, identifier, level):
   149          """Check if the metric is enabled based on the level."""
   150          return level < self._level
   151  
   152      def _join(self, identifier, instance=None, tags=None):
   153          """
   154          Join the identifier tuple with periods ".", combine the arbitrary tags
   155          with the base tags and the identifier tag, convert tags to "tag=value"
   156          format, and then join everything with ",".
   157          """
   158          tag_list = []
   159          if tags is not None:
   160              tag_list.extend(tags.items())
   161          tag_list.extend(self._base_tags)
   162          return ".".join(identifier) + "," + ",".join(
   163              "{}={}".format(k, v)
   164              for k, v in tag_list
   165          )
   166  
   167  
   168  class MetricsCollectorHandle:
   169      def __init__(self, module_name):
   170          self._module_name = module_name
   171  
   172      def gauge(self, metric_name, level=DEFAULT, instance=None, tags=None):
   173          return MetricsCollector.get_instance().gauge(
   174              identifier=self._create_identifier(metric_name, instance),
   175              level=level,
   176              instance=instance,
   177              tags=tags)
   178  
   179      def counter(self, metric_name, level=DEFAULT, instance=None, tags=None):
   180          return MetricsCollector.get_instance().counter(
   181              identifier=self._create_identifier(metric_name, instance),
   182              level=level,
   183              instance=instance,
   184              tags=tags)
   185  
   186      def timer(self, metric_name, level=DEFAULT, instance=None, tags=None):
   187          return MetricsCollector.get_instance().timer(
   188              identifier=self._create_identifier(metric_name, instance),
   189              level=level,
   190              instance=instance,
   191              tags=tags)
   192  
   193      def _create_identifier(self, metric_name, instance=None):
   194          if instance is None:
   195              return (self._module_name, metric_name)
   196          return (self._module_name, instance.__class__.__name__, metric_name)
   197  
   198  
   199  class NoOpMetricsRegistry:
   200      def __init__(self):
   201          self._noop_gauge = NoOpGauge()
   202          self._noop_counter = NoOpCounter()
   203          self._noop_timer = NoOpTimer()
   204  
   205      def gauge(self, identifier):
   206          return self._noop_gauge
   207  
   208      def counter(self, identifier):
   209          return self._noop_counter
   210  
   211      def timer(self, identifier):
   212          return self._noop_timer
   213  
   214  
   215  class NoOpGauge:
   216      def set_value(self, *args, **kwargs):
   217          pass
   218  
   219      def get_value(self, *args, **kwargs):
   220          return 0
   221  
   222  
   223  class NoOpCounter:
   224      def inc(self, *args, **kwargs):
   225          pass
   226  
   227      def dec(self, *args, **kwargs):
   228          pass
   229  
   230  
   231  class NoOpTimer:
   232      def __init__(self):
   233          self._ctx = NoOpTimerContext()
   234  
   235      def time(self):
   236          return self._ctx
   237  
   238  
   239  class NoOpTimerContext():
   240      def __enter__(self):
   241          pass
   242  
   243      def __exit__(self, exception_type, exception_value, traceback):
   244          pass
   245  
   246      def stop(self, *args, **kwargs):
   247          pass