예제 #1
0
 def test_do_post_no_logging(self):
     r = Registry()
     client = HttpClient(r)
     client.post_json(self._uri,
                      '{"status": 429}',
                      retry_delay=0,
                      disable_logging=False)
예제 #2
0
 def test_do_post_encode(self):
     r = Registry()
     client = HttpClient(r)
     client.post_json(self._uri, {"status": 202})
     tags = {
         "mode": "http-client",
         "method": "POST",
         "client": "spectator-py",
         "status": "2xx",
         "statusCode": "202"
     }
     t = r.timer("http.req.complete", tags)
     self.assertEqual(t.count(), 1)
예제 #3
0
 def test_do_post_bad_json(self):
     r = Registry()
     client = HttpClient(r)
     client.post_json(self._uri, '{"status": ', retry_delay=0)
     tags = {
         "mode": "http-client",
         "method": "POST",
         "client": "spectator-py",
         "status": "4xx",
         "statusCode": "400"
     }
     t = r.timer("http.req.complete", tags)
     self.assertEqual(t.count(), 1)
예제 #4
0
 def test_do_post_network_error(self):
     self.tearDown()
     r = Registry()
     client = HttpClient(r)
     client.post_json(self._uri, "{}")
     tags = {
         "mode": "http-client",
         "method": "POST",
         "client": "spectator-py",
         "status": "URLError",
         "statusCode": "URLError"
     }
     t = r.timer("http.req.complete", tags)
     self.assertEqual(t.count(), 1)
예제 #5
0
 def start(self, config=None):
     if self._started:
         logger.debug("registry already started")
         return RegistryStopper(None)
     else:
         self._started = True
         logger.info("starting registry")
         if config is None:
             logger.info("config not specified, using default")
             config = defaultConfig
         elif type(config) is not dict:
             logger.warn("invalid config specified, using default")
             config = defaultConfig
         frequency = config.get("frequency", 5.0)
         self._uri = config.get("uri", None)
         self._client = HttpClient(self, config.get("timeout", 1))
         self._timer = RegistryTimer(frequency, self._publish)
         self._timer.start()
         logger.debug("registry started with config: %s", config)
         return RegistryStopper(self)
예제 #6
0
class Registry:
    noopGauge = NoopGauge()
    noopCounter = NoopCounter()
    noopDistributionSummary = NoopDistributionSummary()
    noopTimer = NoopTimer()

    def __init__(self, clock=SystemClock()):
        self._clock = clock
        self._lock = threading.RLock()
        self._meters = {}
        self._started = False

    def clock(self):
        return self._clock

    def _new_meter(self, name, tags, meterFactory, meterCls, defaultIns):
        with self._lock:
            if tags is None:
                tags = {}
            meterId = MeterId(name, tags)
            meter = self._meters.get(meterId, None)
            if meter is None:
                meter = meterFactory(meterId)
                self._meters[meterId] = meter
            elif not isinstance(meter, meterCls):
                logger.warning(
                    "Meter is already defined as type %s. "
                    "Please use a unique name or tags",
                    meter.__class__.__name__)
                return defaultIns
            return meter

    def counter(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: Counter(id), Counter,
                               self.noopCounter)

    def timer(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: Timer(id, self._clock),
                               Timer, self.noopTimer)

    def distribution_summary(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: DistributionSummary(id),
                               DistributionSummary,
                               self.noopDistributionSummary)

    def gauge(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: Gauge(id), Gauge,
                               self.noopGauge)

    def __iter__(self):
        with self._lock:
            return RegistryIterator(self._meters.values())

    def start(self, config=None):
        if self._started:
            logger.debug("registry already started")
            return RegistryStopper(None)
        else:
            self._started = True
            logger.info("starting registry")
            if config is None:
                logger.info("config not specified, using default")
                config = defaultConfig
            elif type(config) is not dict:
                logger.warning("invalid config specified, using default")
                config = defaultConfig
            frequency = config.get("frequency", 5.0)
            self._uri = config.get("uri", None)
            self._batch_size = config.get("batch_size", 10000)
            self._common_tags = config.get("common_tags", {})
            self._client = HttpClient(self, config.get("timeout", 1))
            self._timer = RegistryTimer(frequency, self._publish)
            self._timer.start()
            logger.debug("registry started with config: %s", config)
            return RegistryStopper(self)

    def stop(self):
        if self._started:
            logger.info("stopping registry")
            self._timer.cancel()
            self._started = False

        # Even if not started, attempt to flush data to minimize risk
        # of data loss
        self._publish()

    def _get_measurements(self):
        snapshot = []
        with self._lock:
            for k, m in list(self._meters.items()):
                # If there are no references in user code, then we expect
                # four references to the meter: 1) meters map, 2) local
                # variable in this loop, 3) internal to ref count method,
                # and 4) internal to the garbage collector.
                if sys.getrefcount(m) == 4:
                    del self._meters[k]
                ms = m._measure()
                for id, value in ms.items():
                    if self._should_send(id, value):
                        snapshot.append((id, value))
        return snapshot

    def _send_batch(self, batch):
        json = self._measurements_to_json(batch)
        self._client.post_json(self._uri, json)

    def _publish(self):
        snapshot = self._get_measurements()

        if logger.isEnabledFor(logging.DEBUG):
            for id, value in snapshot:
                logger.debug("reporting: %s => %f", id, value)

        if self._uri is not None:
            i = 0
            while i < len(snapshot):
                end = min(i + self._batch_size, len(snapshot))
                self._send_batch(snapshot[i:end])
                i += self._batch_size

    def _should_send(self, id, value):
        max_op = 10
        op = self._operation(id.tags())
        return not math.isnan(value) and (value > 0 or op == max_op)

    def _build_string_table(self, payload, data):
        strings = {'name': 0}
        for k, v in self._common_tags.items():
            strings[k] = 0
            strings[v] = 0

        for id, _ in data:
            strings[id.name] = 0
            for k, v in id.tags().items():
                strings[k] = 0
                strings[v] = 0
        keys = list(strings.keys())
        keys.sort()
        payload.append(len(keys))
        payload.extend(keys)
        for i, k in enumerate(keys):
            strings[k] = i
        return strings

    def _measurements_to_json(self, data):
        payload = []
        strings = self._build_string_table(payload, data)
        for id, v in data:
            self._append_measurement(strings, payload, id, v)
        return payload

    def _append_measurement(self, strings, payload, id, value):
        tags = id.tags()
        op = self._operation(tags)
        if op is not None:
            common_tags = self._common_tags
            payload.append(len(tags) + 1 + len(common_tags))
            for k, v in common_tags.items():
                payload.append(strings[k])
                payload.append(strings[v])
            for k, v in tags.items():
                payload.append(strings[k])
                payload.append(strings[v])
            payload.append(strings["name"])
            payload.append(strings[id.name])
            payload.append(op)
            payload.append(value)
        else:
            logger.warning("invalid statistic for %s", id)

    def _operation(self, tags):
        addOp = 0
        maxOp = 10
        return {
            "count": addOp,
            "totalAmount": addOp,
            "totalTime": addOp,
            "totalOfSquares": addOp,
            "percentile": addOp,
            "max": maxOp,
            "gauge": maxOp,
            "activeTasks": maxOp,
            "duration": maxOp
        }.get(tags['statistic'])
예제 #7
0
class Registry:
    def __init__(self, clock=SystemClock()):
        self._clock = clock
        self._lock = threading.RLock()
        self._meters = {}
        self._started = False

    def clock(self):
        return self._clock

    def _new_meter(self, name, tags, meterFactory):
        with self._lock:
            if tags is None:
                tags = {}
            meterId = MeterId(name, tags)
            meter = self._meters.get(meterId, None)
            if meter is None:
                meter = meterFactory(meterId)
                self._meters[meterId] = meter
            return meter

    def counter(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: Counter(id))

    def timer(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: Timer(id, self._clock))

    def distribution_summary(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: DistributionSummary(id))

    def gauge(self, name, tags=None):
        return self._new_meter(name, tags, lambda id: Gauge(id))

    def __iter__(self):
        with self._lock:
            return RegistryIterator(self._meters.values())

    def start(self, config=None):
        if self._started:
            logger.debug("registry already started")
            return RegistryStopper(None)
        else:
            self._started = True
            logger.info("starting registry")
            if config is None:
                logger.info("config not specified, using default")
                config = defaultConfig
            elif type(config) is not dict:
                logger.warn("invalid config specified, using default")
                config = defaultConfig
            frequency = config.get("frequency", 5.0)
            self._uri = config.get("uri", None)
            self._client = HttpClient(self, config.get("timeout", 1))
            self._timer = RegistryTimer(frequency, self._publish)
            self._timer.start()
            logger.debug("registry started with config: %s", config)
            return RegistryStopper(self)

    def stop(self):
        if self._started:
            logger.info("stopping registry")
            self._timer.cancel()
            self._started = False

        # Even if not started, attempt to flush data to minimize risk
        # of data loss
        self._publish()

    def _publish(self):
        snapshot = {}
        with self._lock:
            for k, m in list(self._meters.items()):
                # If there are no references in user code, then we expect
                # three references to the meter: 1) meters map, 2) local
                # variable in this loop, 3) internal to ref count method,
                # and 4) internal to the garbage collector.
                if sys.getrefcount(m) == 4:
                    del self._meters[k]
                snapshot.update(m._measure())

        if logger.isEnabledFor(logging.DEBUG):
            for id, value in snapshot.items():
                logger.debug("reporting: %s => %f", id, value)

        if self._uri is not None:
            json = self._measurements_to_json(snapshot)
            self._client.post_json(self._uri, json)

    def _check_value(self, m):
        v = m['value']
        s = m['tags']['statistic']
        return not math.isnan(m['value']) and (v > 0 or s == 'gauge')

    def _build_string_table(self, payload, data):
        strings = {}
        strings["name"] = 0
        for id in data.keys():
            strings[id.name] = 0
            for k, v in id.tags().items():
                strings[k] = 0
                strings[v] = 0
        keys = strings.keys()
        keys.sort()
        payload.append(len(keys))
        payload.extend(keys)
        for i, k in enumerate(keys):
            strings[k] = i
        return strings

    def _measurements_to_json(self, data):
        payload = []
        strings = self._build_string_table(payload, data)
        for id, v in data.items():
            self._append_measurement(strings, payload, id, v)
        return payload

    def _append_measurement(self, strings, payload, id, value):
        tags = id.tags()
        op = self._operation(tags)
        if op is not None:
            payload.append(len(tags) + 1)
            for k, v in tags.items():
                payload.append(strings[k])
                payload.append(strings[v])
            payload.append(strings["name"])
            payload.append(strings[id.name])
            payload.append(op)
            payload.append(value)
        else:
            logger.warn("invalid statistic for %s", id)

        return {"op": self._operation(tags), "tags": tags, "value": value}

    def _operation(self, tags):
        addOp = 0
        maxOp = 10
        return {
            "count": addOp,
            "totalAmount": addOp,
            "totalTime": addOp,
            "totalOfSquares": addOp,
            "percentile": addOp,
            "max": maxOp,
            "gauge": maxOp,
            "activeTasks": maxOp,
            "duration": maxOp
        }.get(tags['statistic'])