def get_formatted_metrics(self, metrics, enable_meta_metrics=False): # type: (Iterable[Metric], bool) -> List[six.binary_type] meta_timer = None if enable_meta_metrics: meta_timer = Timer('', resolution=TimerResolution.MICROSECONDS) formatted_metrics = [] for metric in metrics: if metric.value is None: continue existing_tags_string = self._global_tags_string if isinstance(metric, Counter): type_label = self.METRIC_TYPE_COUNTER elif isinstance(metric, Gauge): type_label = self.METRIC_TYPE_GAUGE existing_tags_string = self._global_gauge_tags_string elif isinstance(metric, Timer): type_label = self._metric_type_timer elif isinstance(metric, Histogram): type_label = self._metric_type_histogram else: continue metric_tags_string = self._generate_tag_string( metric.tags, existing_tags_string) formatted_metrics.append(b'%s:%d|%s%s' % (self._get_binary_value( metric.name), metric.value, type_label, metric_tags_string)) if not formatted_metrics: return [] if meta_timer: meta_timer.stop() formatted_metrics.insert( 0, b'pymetrics.meta.publish.statsd.format_metrics:%d|%s%s' % ( cast(int, meta_timer.value), self._metric_type_timer, self._global_tags_string, ), ) return formatted_metrics
def get_formatted_metrics(self, metrics, enable_meta_metrics=False): # type: (Iterable[Metric], bool) -> List[six.binary_type] meta_timer = None if enable_meta_metrics: meta_timer = Timer('', resolution=TimerResolution.MICROSECONDS) formatted_metrics = [] for metric in metrics: if metric.value is None: continue if isinstance(metric, Counter): type_label = self.METRIC_TYPE_COUNTER elif isinstance(metric, Gauge): type_label = self.METRIC_TYPE_GAUGE elif isinstance(metric, Timer): type_label = self._metric_type_timer elif isinstance(metric, Histogram): type_label = self._metric_type_histogram else: continue # not possible unless a new metric type is added formatted_metrics.append( b'%s:%d|%s' % (self._get_binary_value(metric.name), metric.value, type_label) ) if not formatted_metrics: return [] if meta_timer: meta_timer.stop() formatted_metrics.insert( 0, b'pymetrics.meta.publish.statsd.format_metrics:%d|%s' % ( cast(int, meta_timer.value), self._metric_type_timer, ) ) return formatted_metrics
def get_all_metrics(self): # type: () -> List[Metric] meta_timer = None if self._configuration and self._configuration.enable_meta_metrics is True: meta_timer = Timer('pymetrics.meta.recorder.get_all_metrics', resolution=TimerResolution.MICROSECONDS) metrics = [] # type: List[Metric] metrics.extend(six.itervalues(self.counters)) metrics.extend(gauge for gauges in six.itervalues(self.gauges) for gauge in gauges if gauge.value is not None) metrics.extend(histogram for histograms in six.itervalues(self.histograms) for histogram in histograms if histogram.value is not None) metrics.extend(timer for timers in six.itervalues(self.timers) for timer in timers if timer.value is not None) if meta_timer: meta_timer.stop() metrics.insert(0, meta_timer) return metrics
def test_timer(): timer = Timer('test.timer.1', tag_4='value_4') assert timer.name == 'test.timer.1' assert timer.tags['tag_4'] == 'value_4' assert timer.value is None timer = Timer('test.timer.2', initial_value=3.75) assert timer.name == 'test.timer.2' assert not timer.tags assert timer.value == 4 with freezegun.freeze_time() as frozen_time: timer.start() frozen_time.tick(_milliseconds(15)) timer.stop() assert timer.value == 15 timer.start() frozen_time.tick(_milliseconds(27)) timer.stop() assert timer.value == 42 with timer: frozen_time.tick(_milliseconds(8)) frozen_time.tick(_microseconds(100)) assert timer.value == 50 def around(foo, bar): assert foo == 'baz' assert bar == 'qux' frozen_time.tick(_milliseconds(11)) frozen_time.tick(_microseconds(600)) return 'Ipsum' assert timer.record_over_function(around, 'baz', bar='qux') == 'Ipsum' assert timer.value == 62 timer = Timer('test.timer.3', resolution=TimerResolution.MICROSECONDS) with freezegun.freeze_time() as frozen_time: timer.start() frozen_time.tick(_milliseconds(1)) frozen_time.tick(_microseconds(103)) timer.stop() assert timer.value == 1103 with timer: frozen_time.tick(_microseconds(209)) assert timer.value == 1312 timer.start() assert timer.value is None frozen_time.tick(_milliseconds(2)) timer.stop() assert timer.value == 3312 assert repr(timer) == 'Timer(name="test.timer.3", value=3312)' # make sure no error is raised, nothing happens timer.stop() timer.stop() timer.stop() assert repr(timer) == 'Timer(name="test.timer.3", value=3312)'
def test_metrics(self): p_metrics = [ self._counter('foo.bar'), self._counter('baz.qux'), Gauge('a.b', initial_value=4), Gauge('a.b', initial_value=7), Gauge('a.b', initial_value=5), Gauge('c.d', initial_value=3), ] # type: List[Metric] timer = Timer('one.two') timer.stop() p_metrics.append(timer) with freezegun.freeze_time() as frozen_time: timer = Timer('one.two') frozen_time.tick(self._milliseconds(50)) timer.stop() p_metrics.append(timer) timer = Timer('one.two') frozen_time.tick(self._milliseconds(30)) timer.stop() p_metrics.append(timer) timer = Timer('three.four') frozen_time.tick(self._milliseconds(10)) timer.stop() p_metrics.append(timer) p_metrics.extend([ Histogram('h.foo', initial_value=1), Histogram('h.foo', initial_value=3), Histogram('h.foo', initial_value=2), Histogram('h.bar', initial_value=77), ]) publisher = SqlitePublisher() publisher.publish(p_metrics) connection = SqlitePublisher.get_connection() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_counters WHERE metric_name = 'foo.bar';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'foo.bar' assert metrics[0][str('metric_value')] == 1 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_counters WHERE metric_name = 'baz.qux';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'baz.qux' assert metrics[0][str('metric_value')] == 1 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_gauges WHERE metric_name = 'a.b';") metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'a.b' assert metrics[0][str('metric_value')] == 5 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_gauges WHERE metric_name = 'c.d';") metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'c.d' assert metrics[0][str('metric_value')] == 3 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_timers WHERE metric_name = 'one.two';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 3 assert metrics[0][str('metric_name')] == 'one.two' assert metrics[0][str('metric_value')] < 0.001 assert metrics[1][str('metric_name')] == 'one.two' assert 0.05 <= metrics[1][str('metric_value')] < 0.06 assert metrics[2][str('metric_name')] == 'one.two' assert 0.03 <= metrics[2][str('metric_value')] < 0.04 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_timers WHERE metric_name = 'three.four';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'three.four' assert 0.01 <= metrics[0][str('metric_value')] < 0.02 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_histograms WHERE metric_name = 'h.foo';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 3 assert metrics[0][str('metric_name')] == 'h.foo' assert metrics[0][str('metric_value')] == 1 assert metrics[1][str('metric_name')] == 'h.foo' assert metrics[1][str('metric_value')] == 3 assert metrics[2][str('metric_name')] == 'h.foo' assert metrics[2][str('metric_value')] == 2 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_histograms WHERE metric_name = 'h.bar';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'h.bar' assert metrics[0][str('metric_value')] == 77 finally: cursor.close() p_metrics = [ self._counter('foo.bar'), Gauge('a.b', initial_value=22), Gauge('c.d', initial_value=11), ] with freezegun.freeze_time() as frozen_time: timer = Timer('three.four', resolution=TimerResolution.MICROSECONDS) frozen_time.tick(self._milliseconds(20)) timer.stop() p_metrics.append(timer) p_metrics.append(Histogram('h.foo', initial_value=8)) publisher = SqlitePublisher() publisher.initialize_if_necessary() publisher.publish(p_metrics) cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_counters WHERE metric_name = 'foo.bar';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 2 assert metrics[0][str('metric_name')] == 'foo.bar' assert metrics[0][str('metric_value')] == 1 assert metrics[1][str('metric_name')] == 'foo.bar' assert metrics[1][str('metric_value')] == 1 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_counters WHERE metric_name = 'baz.qux';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'baz.qux' assert metrics[0][str('metric_value')] == 1 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_gauges WHERE metric_name = 'a.b';") metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'a.b' assert metrics[0][str('metric_value')] == 22 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_gauges WHERE metric_name = 'c.d';") metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'c.d' assert metrics[0][str('metric_value')] == 11 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_timers WHERE metric_name = 'one.two';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 3 assert metrics[0][str('metric_name')] == 'one.two' assert metrics[0][str('metric_value')] < 0.001 assert metrics[1][str('metric_name')] == 'one.two' assert 0.05 <= metrics[1][str('metric_value')] < 0.06 assert metrics[2][str('metric_name')] == 'one.two' assert 0.03 <= metrics[2][str('metric_value')] < 0.04 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_timers WHERE metric_name = 'three.four';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 2 assert metrics[0][str('metric_name')] == 'three.four' assert 0.01 <= metrics[0][str('metric_value')] < 0.02 assert metrics[1][str('metric_name')] == 'three.four' assert 0.02 <= metrics[1][str('metric_value')] < 0.03 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_histograms WHERE metric_name = 'h.foo';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 4 assert metrics[0][str('metric_name')] == 'h.foo' assert metrics[0][str('metric_value')] == 1 assert metrics[1][str('metric_name')] == 'h.foo' assert metrics[1][str('metric_value')] == 3 assert metrics[2][str('metric_name')] == 'h.foo' assert metrics[2][str('metric_value')] == 2 assert metrics[3][str('metric_name')] == 'h.foo' assert metrics[3][str('metric_value')] == 8 finally: cursor.close() cursor = connection.cursor() try: # noinspection PyTypeChecker cursor.execute( "SELECT * FROM pymetrics_histograms WHERE metric_name = 'h.bar';" ) metrics = list(cursor.fetchall()) assert len(metrics) == 1 assert metrics[0][str('metric_name')] == 'h.bar' assert metrics[0][str('metric_value')] == 77 finally: cursor.close()
def _send_chunked_payload(self, payload, number_of_metrics, error_logger=None, enable_meta_metrics=False): # type: (six.binary_type, int, six.text_type, bool) -> None meta_timer = None error = error_max_packet = False sock = None try: if enable_meta_metrics: meta_timer = Timer('', resolution=TimerResolution.MICROSECONDS) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(self.timeout) sock.connect((self.host, self.port)) sock.sendall(payload) except Exception as e: error = True if isinstance(e, socket.error) and e.errno == errno.EMSGSIZE: error_max_packet = True if error_logger: extra = {'data': { 'payload_length': len(payload), 'num_metrics': number_of_metrics, 'enable_meta_metrics': enable_meta_metrics, }} if error_max_packet: logging.getLogger(error_logger).error( 'Failed to send metrics to statsd because UDP packet too big', extra=extra, ) else: logging.getLogger(error_logger).exception( 'Failed to send metrics to statsd {}:{}'.format(self.host, self.port), extra=extra, ) finally: if sock: # noinspection PyBroadException try: sock.close() except Exception: pass if meta_timer: meta_timer.stop() if enable_meta_metrics: num_bytes = len(payload) # TODO temporary; the length of the packet that we tried to send payload = b'pymetrics.meta.publish.statsd.send:1|%s' % self.METRIC_TYPE_COUNTER payload += b'\npymetrics.meta.publish.statsd.send.num_metrics:%d|%s' % ( number_of_metrics, self._metric_type_histogram ) if meta_timer: payload += b'\npymetrics.meta.publish.statsd.send.timer:%d|%s' % ( cast(int, meta_timer.value), self._metric_type_timer, ) if error: if error_max_packet: payload += b'\npymetrics.meta.publish.statsd.send.error.max_packet:1|%s' % self.METRIC_TYPE_COUNTER else: payload += b'\npymetrics.meta.publish.statsd.send.error.unknown:1|%s' % self.METRIC_TYPE_COUNTER # TODO The following three stats are temporary, to test out potential MTU problems noted above if num_bytes >= MAX_IPV4_PAYLOAD_SIZE_BYTES: payload += b'\npymetrics.meta.publish.statsd.send.exceeds_max_packet:1|%s' % self.METRIC_TYPE_COUNTER if num_bytes >= MAX_GIG_E_PAYLOAD_SIZE_BYTES: payload += b'\npymetrics.meta.publish.statsd.send.exceeds_max_gig_e:1|%s' % self.METRIC_TYPE_COUNTER if num_bytes >= MAX_FAST_E_PAYLOAD_SIZE_BYTES: payload += b'\npymetrics.meta.publish.statsd.send.exceeds_max_fast_e:1|%s' % self.METRIC_TYPE_COUNTER sock = None # noinspection PyBroadException try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(self.timeout) sock.connect((self.host, self.port)) sock.sendall(payload) except Exception: if error_logger: logging.getLogger(error_logger).exception( 'Failed to send meta metrics to statsd {}:{}'.format(self.host, self.port), ) finally: if sock: # noinspection PyBroadException try: sock.close() except Exception: pass