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