Beispiel #1
0
 def setup_middleware(self):
     self.validate_config()
     self.metric_publisher = yield self.worker.start_publisher(
         MetricPublisher)
     # We don't use a VumiApi here because we don't have a Riak config for
     # it.
     self.redis = yield TxRedisManager.from_config(
         self.config['redis_manager'])
     self.metric_manager = MetricManager(self.manager_name + '.',
                                         publisher=self.metric_publisher)
     self.metric_manager.start_polling()
Beispiel #2
0
 def setup_middleware(self):
     self.validate_config()
     self.metric_publisher = yield self.worker.start_publisher(MetricPublisher)
     # We don't use a VumiApi here because we don't have a Riak config for
     # it.
     self.redis = yield TxRedisManager.from_config(self.config["redis_manager"])
     self.metric_manager = MetricManager(self.manager_name + ".", publisher=self.metric_publisher)
     self.metric_manager.start_polling()
Beispiel #3
0
 def get_metric_manager(self, prefix):
     if self.metric_publisher is None:
         raise VumiError("No metric publisher available.")
     return MetricManager(prefix, publisher=self.metric_publisher)
Beispiel #4
0
class MetricsMiddleware(BaseMiddleware):
    """
    Middleware that publishes metrics on messages flowing through.
    It tracks the number of messages sent & received on the various
    transports and the average response times for messages received.

    :param str manager_name:
        The name of the metrics publisher, this is used for the MetricManager
        publisher and all metric names will be prefixed with it.
    :param str count_suffix:
        Defaults to 'count'. This is the suffix appended to all
        `transport_name` based counters. If a message is received on endpoint
        'foo', counters are published on
        '<manager_name>.foo.inbound.<count_suffix>'
    :param str response_time_suffix:
        Defaults to 'response_time'. This is the suffix appended to all
        `transport_name` based average response time metrics. If a message is
        received its `message_id` is stored and when a reply for the given
        `message_id` is sent out, the timestamps are compared and a averaged
        metric is published.
    :param int max_lifetime:
        How long to keep a timestamp for. Anything older than this is trashed.
        Defaults to 60 seconds.
    :param dict redis_manager:
        Connection configuration details for Redis.
    :param str op_mode:
        What mode to operate in, options are `passive` or `active`.
        Defaults to passive.
        *passive*:  assumes the middleware endpoints are to be used as the
                    names for metrics publishing.
        *active*:   assumes that the individual messages are to be inspected
                    for their `transport_name` values.

        NOTE:   This does not apply for events or failures, the endpoints
                are always used for those since those message types are not
                guaranteed to have a `transport_name` value.
    """

    KNOWN_MODES = frozenset(["active", "passive"])

    def validate_config(self):
        self.manager_name = self.config["manager_name"]
        self.count_suffix = self.config.get("count_suffix", "count")
        self.response_time_suffix = self.config.get("response_time_suffix", "response_time")
        self.max_lifetime = int(self.config.get("max_lifetime", 60))
        self.op_mode = self.config.get("op_mode", "passive")
        if self.op_mode not in self.KNOWN_MODES:
            raise ConfigError("Unknown op_mode: %s" % (self.op_mode,))

    @inlineCallbacks
    def setup_middleware(self):
        self.validate_config()
        self.metric_publisher = yield self.worker.start_publisher(MetricPublisher)
        # We don't use a VumiApi here because we don't have a Riak config for
        # it.
        self.redis = yield TxRedisManager.from_config(self.config["redis_manager"])
        self.metric_manager = MetricManager(self.manager_name + ".", publisher=self.metric_publisher)
        self.metric_manager.start_polling()

    def teardown_middleware(self):
        self.metric_manager.stop_polling()
        return self.redis.close_manager()

    def get_or_create_metric(self, name, metric_class, *args, **kwargs):
        """
        Get the metric for `name`, create it with
        `metric_class(*args, **kwargs)` if it doesn't exist yet.
        """
        if name not in self.metric_manager:
            self.metric_manager.register(metric_class(name, *args, **kwargs))
        return self.metric_manager[name]

    def get_counter_metric(self, name):
        metric_name = "%s.%s" % (name, self.count_suffix)
        return self.get_or_create_metric(metric_name, Count)

    def increment_counter(self, transport_name, message_type):
        metric = self.get_counter_metric("%s.%s" % (transport_name, message_type))
        metric.inc()

    def get_response_time_metric(self, name):
        metric_name = "%s.%s" % (name, self.response_time_suffix)
        return self.get_or_create_metric(metric_name, Metric)

    def set_response_time(self, transport_name, time):
        metric = self.get_response_time_metric(transport_name)
        metric.set(time)

    def key(self, transport_name, message_id):
        return "%s:%s" % (transport_name, message_id)

    def set_inbound_timestamp(self, transport_name, message):
        key = self.key(transport_name, message["message_id"])
        return self.redis.setex(key, self.max_lifetime, repr(time.time()))

    @inlineCallbacks
    def get_outbound_timestamp(self, transport_name, message):
        key = self.key(transport_name, message["in_reply_to"])
        timestamp = yield self.redis.get(key)
        if timestamp:
            returnValue(float(timestamp))

    @inlineCallbacks
    def compare_timestamps(self, transport_name, message):
        timestamp = yield self.get_outbound_timestamp(transport_name, message)
        if timestamp:
            self.set_response_time(transport_name, time.time() - timestamp)

    def get_name(self, message, endpoint):
        if self.op_mode == "active":
            return message["transport_name"]
        return endpoint

    @inlineCallbacks
    def handle_inbound(self, message, endpoint):
        name = self.get_name(message, endpoint)
        self.increment_counter(name, "inbound")
        yield self.set_inbound_timestamp(name, message)
        returnValue(message)

    @inlineCallbacks
    def handle_outbound(self, message, endpoint):
        name = self.get_name(message, endpoint)
        self.increment_counter(name, "outbound")
        yield self.compare_timestamps(name, message)
        returnValue(message)

    def handle_event(self, event, endpoint):
        self.increment_counter(endpoint, "event.%s" % (event["event_type"]))
        if event["event_type"] == "delivery_report":
            self.increment_counter(endpoint, "event.%s.%s" % (event["event_type"], event["delivery_status"]))
        return event

    def handle_failure(self, failure, endpoint):
        self.increment_counter(endpoint, "failure.%s" % (failure["failure_code"] or "unspecified",))
        return failure
Beispiel #5
0
class MetricsMiddleware(BaseMiddleware):
    """
    Middleware that publishes metrics on messages flowing through.
    It tracks the number of messages sent & received on the various
    transports and the average response times for messages received.

    :param str manager_name:
        The name of the metrics publisher, this is used for the MetricManager
        publisher and all metric names will be prefixed with it.
    :param str count_suffix:
        Defaults to 'count'. This is the suffix appended to all
        `transport_name` based counters. If a message is received on endpoint
        'foo', counters are published on
        '<manager_name>.foo.inbound.<count_suffix>'
    :param str response_time_suffix:
        Defaults to 'response_time'. This is the suffix appended to all
        `transport_name` based average response time metrics. If a message is
        received its `message_id` is stored and when a reply for the given
        `message_id` is sent out, the timestamps are compared and a averaged
        metric is published.
    :param int max_lifetime:
        How long to keep a timestamp for. Anything older than this is trashed.
        Defaults to 60 seconds.
    :param dict redis_manager:
        Connection configuration details for Redis.
    :param str op_mode:
        What mode to operate in, options are `passive` or `active`.
        Defaults to passive.
        *passive*:  assumes the middleware endpoints are to be used as the
                    names for metrics publishing.
        *active*:   assumes that the individual messages are to be inspected
                    for their `transport_name` values.

        NOTE:   This does not apply for events or failures, the endpoints
                are always used for those since those message types are not
                guaranteed to have a `transport_name` value.
    """

    KNOWN_MODES = frozenset(['active', 'passive'])

    def validate_config(self):
        self.manager_name = self.config['manager_name']
        self.count_suffix = self.config.get('count_suffix', 'count')
        self.response_time_suffix = self.config.get('response_time_suffix',
                                                    'response_time')
        self.max_lifetime = int(self.config.get('max_lifetime', 60))
        self.op_mode = self.config.get('op_mode', 'passive')
        if self.op_mode not in self.KNOWN_MODES:
            raise ConfigError('Unknown op_mode: %s' % (self.op_mode, ))

    @inlineCallbacks
    def setup_middleware(self):
        self.validate_config()
        self.metric_publisher = yield self.worker.start_publisher(
            MetricPublisher)
        # We don't use a VumiApi here because we don't have a Riak config for
        # it.
        self.redis = yield TxRedisManager.from_config(
            self.config['redis_manager'])
        self.metric_manager = MetricManager(self.manager_name + '.',
                                            publisher=self.metric_publisher)
        self.metric_manager.start_polling()

    def teardown_middleware(self):
        self.metric_manager.stop_polling()
        return self.redis.close_manager()

    def get_or_create_metric(self, name, metric_class, *args, **kwargs):
        """
        Get the metric for `name`, create it with
        `metric_class(*args, **kwargs)` if it doesn't exist yet.
        """
        if name not in self.metric_manager:
            self.metric_manager.register(metric_class(name, *args, **kwargs))
        return self.metric_manager[name]

    def get_counter_metric(self, name):
        metric_name = '%s.%s' % (name, self.count_suffix)
        return self.get_or_create_metric(metric_name, Count)

    def increment_counter(self, transport_name, message_type):
        metric = self.get_counter_metric('%s.%s' %
                                         (transport_name, message_type))
        metric.inc()

    def get_response_time_metric(self, name):
        metric_name = '%s.%s' % (name, self.response_time_suffix)
        return self.get_or_create_metric(metric_name, Metric)

    def set_response_time(self, transport_name, time):
        metric = self.get_response_time_metric(transport_name)
        metric.set(time)

    def key(self, transport_name, message_id):
        return '%s:%s' % (transport_name, message_id)

    def set_inbound_timestamp(self, transport_name, message):
        key = self.key(transport_name, message['message_id'])
        return self.redis.setex(key, self.max_lifetime, repr(time.time()))

    @inlineCallbacks
    def get_outbound_timestamp(self, transport_name, message):
        key = self.key(transport_name, message['in_reply_to'])
        timestamp = yield self.redis.get(key)
        if timestamp:
            returnValue(float(timestamp))

    @inlineCallbacks
    def compare_timestamps(self, transport_name, message):
        timestamp = yield self.get_outbound_timestamp(transport_name, message)
        if timestamp:
            self.set_response_time(transport_name, time.time() - timestamp)

    def get_name(self, message, endpoint):
        if self.op_mode == 'active':
            return message['transport_name']
        return endpoint

    @inlineCallbacks
    def handle_inbound(self, message, endpoint):
        name = self.get_name(message, endpoint)
        self.increment_counter(name, 'inbound')
        yield self.set_inbound_timestamp(name, message)
        returnValue(message)

    @inlineCallbacks
    def handle_outbound(self, message, endpoint):
        name = self.get_name(message, endpoint)
        self.increment_counter(name, 'outbound')
        yield self.compare_timestamps(name, message)
        returnValue(message)

    def handle_event(self, event, endpoint):
        self.increment_counter(endpoint, 'event.%s' % (event['event_type']))
        if event['event_type'] == 'delivery_report':
            self.increment_counter(
                endpoint, 'event.%s.%s' %
                (event['event_type'], event['delivery_status']))
        return event

    def handle_failure(self, failure, endpoint):
        self.increment_counter(
            endpoint,
            'failure.%s' % (failure['failure_code'] or 'unspecified', ))
        return failure