500 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			500 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import sys
 | 
						|
import time
 | 
						|
 | 
						|
import pytest
 | 
						|
 | 
						|
from kafka.errors import QuotaViolationError
 | 
						|
from kafka.metrics import DictReporter, MetricConfig, MetricName, Metrics, Quota
 | 
						|
from kafka.metrics.measurable import AbstractMeasurable
 | 
						|
from kafka.metrics.stats import (Avg, Count, Max, Min, Percentile, Percentiles,
 | 
						|
                                 Rate, Total)
 | 
						|
from kafka.metrics.stats.percentiles import BucketSizing
 | 
						|
from kafka.metrics.stats.rate import TimeUnit
 | 
						|
 | 
						|
EPS = 0.000001
 | 
						|
 | 
						|
 | 
						|
@pytest.fixture
 | 
						|
def time_keeper():
 | 
						|
    return TimeKeeper()
 | 
						|
 | 
						|
 | 
						|
@pytest.fixture
 | 
						|
def config():
 | 
						|
    return MetricConfig()
 | 
						|
 | 
						|
 | 
						|
@pytest.fixture
 | 
						|
def reporter():
 | 
						|
    return DictReporter()
 | 
						|
 | 
						|
 | 
						|
@pytest.fixture
 | 
						|
def metrics(request, config, reporter):
 | 
						|
    metrics = Metrics(config, [reporter], enable_expiration=True)
 | 
						|
    request.addfinalizer(lambda: metrics.close())
 | 
						|
    return metrics
 | 
						|
 | 
						|
 | 
						|
def test_MetricName():
 | 
						|
    # The Java test only cover the differences between the deprecated
 | 
						|
    # constructors, so I'm skipping them but doing some other basic testing.
 | 
						|
 | 
						|
    # In short, metrics should be equal IFF their name, group, and tags are
 | 
						|
    # the same. Descriptions do not matter.
 | 
						|
    name1 = MetricName('name', 'group', 'A metric.', {'a': 1, 'b': 2})
 | 
						|
    name2 = MetricName('name', 'group', 'A description.', {'a': 1, 'b': 2})
 | 
						|
    assert name1 == name2
 | 
						|
 | 
						|
    name1 = MetricName('name', 'group', tags={'a': 1, 'b': 2})
 | 
						|
    name2 = MetricName('name', 'group', tags={'a': 1, 'b': 2})
 | 
						|
    assert name1 == name2
 | 
						|
 | 
						|
    name1 = MetricName('foo', 'group')
 | 
						|
    name2 = MetricName('name', 'group')
 | 
						|
    assert name1 != name2
 | 
						|
 | 
						|
    name1 = MetricName('name', 'foo')
 | 
						|
    name2 = MetricName('name', 'group')
 | 
						|
    assert name1 != name2
 | 
						|
 | 
						|
    # name and group must be non-empty. Everything else is optional.
 | 
						|
    with pytest.raises(Exception):
 | 
						|
        MetricName('', 'group')
 | 
						|
    with pytest.raises(Exception):
 | 
						|
        MetricName('name', None)
 | 
						|
    # tags must be a dict if supplied
 | 
						|
    with pytest.raises(Exception):
 | 
						|
        MetricName('name', 'group', tags=set())
 | 
						|
 | 
						|
    # Because of the implementation of __eq__ and __hash__, the values of
 | 
						|
    # a MetricName cannot be mutable.
 | 
						|
    tags = {'a': 1}
 | 
						|
    name = MetricName('name', 'group', 'description', tags=tags)
 | 
						|
    with pytest.raises(AttributeError):
 | 
						|
        name.name = 'new name'
 | 
						|
    with pytest.raises(AttributeError):
 | 
						|
        name.group = 'new name'
 | 
						|
    with pytest.raises(AttributeError):
 | 
						|
        name.tags = {}
 | 
						|
    # tags is a copy, so the instance isn't altered
 | 
						|
    name.tags['b'] = 2
 | 
						|
    assert name.tags == tags
 | 
						|
 | 
						|
 | 
						|
def test_simple_stats(mocker, time_keeper, config, metrics):
 | 
						|
    mocker.patch('time.time', side_effect=time_keeper.time)
 | 
						|
 | 
						|
    measurable = ConstantMeasurable()
 | 
						|
 | 
						|
    metrics.add_metric(metrics.metric_name('direct.measurable', 'grp1',
 | 
						|
                                            'The fraction of time an appender waits for space allocation.'),
 | 
						|
                        measurable)
 | 
						|
    sensor = metrics.sensor('test.sensor')
 | 
						|
    sensor.add(metrics.metric_name('test.avg', 'grp1'), Avg())
 | 
						|
    sensor.add(metrics.metric_name('test.max', 'grp1'), Max())
 | 
						|
    sensor.add(metrics.metric_name('test.min', 'grp1'), Min())
 | 
						|
    sensor.add(metrics.metric_name('test.rate', 'grp1'), Rate(TimeUnit.SECONDS))
 | 
						|
    sensor.add(metrics.metric_name('test.occurences', 'grp1'),Rate(TimeUnit.SECONDS, Count()))
 | 
						|
    sensor.add(metrics.metric_name('test.count', 'grp1'), Count())
 | 
						|
    percentiles = [Percentile(metrics.metric_name('test.median', 'grp1'), 50.0),
 | 
						|
                Percentile(metrics.metric_name('test.perc99_9', 'grp1'), 99.9)]
 | 
						|
    sensor.add_compound(Percentiles(100, BucketSizing.CONSTANT, 100, -100,
 | 
						|
                        percentiles=percentiles))
 | 
						|
 | 
						|
    sensor2 = metrics.sensor('test.sensor2')
 | 
						|
    sensor2.add(metrics.metric_name('s2.total', 'grp1'), Total())
 | 
						|
    sensor2.record(5.0)
 | 
						|
 | 
						|
    sum_val = 0
 | 
						|
    count = 10
 | 
						|
    for i in range(count):
 | 
						|
        sensor.record(i)
 | 
						|
        sum_val += i
 | 
						|
 | 
						|
    # prior to any time passing
 | 
						|
    elapsed_secs = (config.time_window_ms * (config.samples - 1)) / 1000.0
 | 
						|
    assert abs(count / elapsed_secs -
 | 
						|
            metrics.metrics.get(metrics.metric_name('test.occurences', 'grp1')).value()) \
 | 
						|
            < EPS, 'Occurrences(0...%d) = %f' % (count, count / elapsed_secs)
 | 
						|
 | 
						|
    # pretend 2 seconds passed...
 | 
						|
    sleep_time_seconds = 2.0
 | 
						|
    time_keeper.sleep(sleep_time_seconds)
 | 
						|
    elapsed_secs += sleep_time_seconds
 | 
						|
 | 
						|
    assert abs(5.0 - metrics.metrics.get(metrics.metric_name('s2.total', 'grp1')).value()) \
 | 
						|
            < EPS, 's2 reflects the constant value'
 | 
						|
    assert abs(4.5 - metrics.metrics.get(metrics.metric_name('test.avg', 'grp1')).value()) \
 | 
						|
            < EPS, 'Avg(0...9) = 4.5'
 | 
						|
    assert abs((count - 1) - metrics.metrics.get(metrics.metric_name('test.max', 'grp1')).value()) \
 | 
						|
            < EPS, 'Max(0...9) = 9'
 | 
						|
    assert abs(0.0 - metrics.metrics.get(metrics.metric_name('test.min', 'grp1')).value()) \
 | 
						|
            < EPS, 'Min(0...9) = 0'
 | 
						|
    assert abs((sum_val / elapsed_secs) - metrics.metrics.get(metrics.metric_name('test.rate', 'grp1')).value()) \
 | 
						|
            < EPS, 'Rate(0...9) = 1.40625'
 | 
						|
    assert abs((count / elapsed_secs) - metrics.metrics.get(metrics.metric_name('test.occurences', 'grp1')).value()) \
 | 
						|
            < EPS, 'Occurrences(0...%d) = %f' % (count, count / elapsed_secs)
 | 
						|
    assert abs(count - metrics.metrics.get(metrics.metric_name('test.count', 'grp1')).value()) \
 | 
						|
            < EPS, 'Count(0...9) = 10'
 | 
						|
 | 
						|
 | 
						|
def test_hierarchical_sensors(metrics):
 | 
						|
    parent1 = metrics.sensor('test.parent1')
 | 
						|
    parent1.add(metrics.metric_name('test.parent1.count', 'grp1'), Count())
 | 
						|
    parent2 = metrics.sensor('test.parent2')
 | 
						|
    parent2.add(metrics.metric_name('test.parent2.count', 'grp1'), Count())
 | 
						|
    child1 = metrics.sensor('test.child1', parents=[parent1, parent2])
 | 
						|
    child1.add(metrics.metric_name('test.child1.count', 'grp1'), Count())
 | 
						|
    child2 = metrics.sensor('test.child2', parents=[parent1])
 | 
						|
    child2.add(metrics.metric_name('test.child2.count', 'grp1'), Count())
 | 
						|
    grandchild = metrics.sensor('test.grandchild', parents=[child1])
 | 
						|
    grandchild.add(metrics.metric_name('test.grandchild.count', 'grp1'), Count())
 | 
						|
 | 
						|
    # increment each sensor one time
 | 
						|
    parent1.record()
 | 
						|
    parent2.record()
 | 
						|
    child1.record()
 | 
						|
    child2.record()
 | 
						|
    grandchild.record()
 | 
						|
 | 
						|
    p1 = parent1.metrics[0].value()
 | 
						|
    p2 = parent2.metrics[0].value()
 | 
						|
    c1 = child1.metrics[0].value()
 | 
						|
    c2 = child2.metrics[0].value()
 | 
						|
    gc = grandchild.metrics[0].value()
 | 
						|
 | 
						|
    # each metric should have a count equal to one + its children's count
 | 
						|
    assert 1.0 == gc
 | 
						|
    assert 1.0 + gc == c1
 | 
						|
    assert 1.0 == c2
 | 
						|
    assert 1.0 + c1 == p2
 | 
						|
    assert 1.0 + c1 + c2 == p1
 | 
						|
    assert [child1, child2] == metrics._children_sensors.get(parent1)
 | 
						|
    assert [child1] == metrics._children_sensors.get(parent2)
 | 
						|
    assert metrics._children_sensors.get(grandchild) is None
 | 
						|
 | 
						|
 | 
						|
def test_bad_sensor_hierarchy(metrics):
 | 
						|
    parent = metrics.sensor('parent')
 | 
						|
    child1 = metrics.sensor('child1', parents=[parent])
 | 
						|
    child2 = metrics.sensor('child2', parents=[parent])
 | 
						|
 | 
						|
    with pytest.raises(ValueError):
 | 
						|
        metrics.sensor('gc', parents=[child1, child2])
 | 
						|
 | 
						|
 | 
						|
def test_remove_sensor(metrics):
 | 
						|
    size = len(metrics.metrics)
 | 
						|
    parent1 = metrics.sensor('test.parent1')
 | 
						|
    parent1.add(metrics.metric_name('test.parent1.count', 'grp1'), Count())
 | 
						|
    parent2 = metrics.sensor('test.parent2')
 | 
						|
    parent2.add(metrics.metric_name('test.parent2.count', 'grp1'), Count())
 | 
						|
    child1 = metrics.sensor('test.child1', parents=[parent1, parent2])
 | 
						|
    child1.add(metrics.metric_name('test.child1.count', 'grp1'), Count())
 | 
						|
    child2 = metrics.sensor('test.child2', parents=[parent2])
 | 
						|
    child2.add(metrics.metric_name('test.child2.count', 'grp1'), Count())
 | 
						|
    grandchild1 = metrics.sensor('test.gchild2', parents=[child2])
 | 
						|
    grandchild1.add(metrics.metric_name('test.gchild2.count', 'grp1'), Count())
 | 
						|
 | 
						|
    sensor = metrics.get_sensor('test.parent1')
 | 
						|
    assert sensor is not None
 | 
						|
    metrics.remove_sensor('test.parent1')
 | 
						|
    assert metrics.get_sensor('test.parent1') is None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.parent1.count', 'grp1')) is None
 | 
						|
    assert metrics.get_sensor('test.child1') is None
 | 
						|
    assert metrics._children_sensors.get(sensor) is None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.child1.count', 'grp1')) is None
 | 
						|
 | 
						|
    sensor = metrics.get_sensor('test.gchild2')
 | 
						|
    assert sensor is not None
 | 
						|
    metrics.remove_sensor('test.gchild2')
 | 
						|
    assert metrics.get_sensor('test.gchild2') is None
 | 
						|
    assert metrics._children_sensors.get(sensor) is None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.gchild2.count', 'grp1')) is None
 | 
						|
 | 
						|
    sensor = metrics.get_sensor('test.child2')
 | 
						|
    assert sensor is not None
 | 
						|
    metrics.remove_sensor('test.child2')
 | 
						|
    assert metrics.get_sensor('test.child2') is None
 | 
						|
    assert metrics._children_sensors.get(sensor) is None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.child2.count', 'grp1')) is None
 | 
						|
 | 
						|
    sensor = metrics.get_sensor('test.parent2')
 | 
						|
    assert sensor is not None
 | 
						|
    metrics.remove_sensor('test.parent2')
 | 
						|
    assert metrics.get_sensor('test.parent2') is None
 | 
						|
    assert metrics._children_sensors.get(sensor) is None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.parent2.count', 'grp1')) is None
 | 
						|
 | 
						|
    assert size == len(metrics.metrics)
 | 
						|
 | 
						|
 | 
						|
def test_remove_inactive_metrics(mocker, time_keeper, metrics):
 | 
						|
    mocker.patch('time.time', side_effect=time_keeper.time)
 | 
						|
 | 
						|
    s1 = metrics.sensor('test.s1', None, 1)
 | 
						|
    s1.add(metrics.metric_name('test.s1.count', 'grp1'), Count())
 | 
						|
 | 
						|
    s2 = metrics.sensor('test.s2', None, 3)
 | 
						|
    s2.add(metrics.metric_name('test.s2.count', 'grp1'), Count())
 | 
						|
 | 
						|
    purger = Metrics.ExpireSensorTask
 | 
						|
    purger.run(metrics)
 | 
						|
    assert metrics.get_sensor('test.s1') is not None, \
 | 
						|
            'Sensor test.s1 must be present'
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.s1.count', 'grp1')) is not None, \
 | 
						|
            'MetricName test.s1.count must be present'
 | 
						|
    assert metrics.get_sensor('test.s2') is not None, \
 | 
						|
            'Sensor test.s2 must be present'
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.s2.count', 'grp1')) is not None, \
 | 
						|
            'MetricName test.s2.count must be present'
 | 
						|
 | 
						|
    time_keeper.sleep(1.001)
 | 
						|
    purger.run(metrics)
 | 
						|
    assert metrics.get_sensor('test.s1') is None, \
 | 
						|
            'Sensor test.s1 should have been purged'
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.s1.count', 'grp1')) is None, \
 | 
						|
            'MetricName test.s1.count should have been purged'
 | 
						|
    assert metrics.get_sensor('test.s2') is not None, \
 | 
						|
            'Sensor test.s2 must be present'
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.s2.count', 'grp1')) is not None, \
 | 
						|
            'MetricName test.s2.count must be present'
 | 
						|
 | 
						|
    # record a value in sensor s2. This should reset the clock for that sensor.
 | 
						|
    # It should not get purged at the 3 second mark after creation
 | 
						|
    s2.record()
 | 
						|
 | 
						|
    time_keeper.sleep(2)
 | 
						|
    purger.run(metrics)
 | 
						|
    assert metrics.get_sensor('test.s2') is not None, \
 | 
						|
            'Sensor test.s2 must be present'
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.s2.count', 'grp1')) is not None, \
 | 
						|
            'MetricName test.s2.count must be present'
 | 
						|
 | 
						|
    # After another 1 second sleep, the metric should be purged
 | 
						|
    time_keeper.sleep(1)
 | 
						|
    purger.run(metrics)
 | 
						|
    assert metrics.get_sensor('test.s1') is None, \
 | 
						|
            'Sensor test.s2 should have been purged'
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.s1.count', 'grp1')) is None, \
 | 
						|
            'MetricName test.s2.count should have been purged'
 | 
						|
 | 
						|
    # After purging, it should be possible to recreate a metric
 | 
						|
    s1 = metrics.sensor('test.s1', None, 1)
 | 
						|
    s1.add(metrics.metric_name('test.s1.count', 'grp1'), Count())
 | 
						|
    assert metrics.get_sensor('test.s1') is not None, \
 | 
						|
        'Sensor test.s1 must be present'
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test.s1.count', 'grp1')) is not None, \
 | 
						|
            'MetricName test.s1.count must be present'
 | 
						|
 | 
						|
 | 
						|
def test_remove_metric(metrics):
 | 
						|
    size = len(metrics.metrics)
 | 
						|
    metrics.add_metric(metrics.metric_name('test1', 'grp1'), Count())
 | 
						|
    metrics.add_metric(metrics.metric_name('test2', 'grp1'), Count())
 | 
						|
 | 
						|
    assert metrics.remove_metric(metrics.metric_name('test1', 'grp1')) is not None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test1', 'grp1')) is None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test2', 'grp1')) is not None
 | 
						|
 | 
						|
    assert metrics.remove_metric(metrics.metric_name('test2', 'grp1')) is not None
 | 
						|
    assert metrics.metrics.get(metrics.metric_name('test2', 'grp1')) is None
 | 
						|
 | 
						|
    assert size == len(metrics.metrics)
 | 
						|
 | 
						|
 | 
						|
def test_event_windowing(mocker, time_keeper):
 | 
						|
    mocker.patch('time.time', side_effect=time_keeper.time)
 | 
						|
 | 
						|
    count = Count()
 | 
						|
    config = MetricConfig(event_window=1, samples=2)
 | 
						|
    count.record(config, 1.0, time_keeper.ms())
 | 
						|
    count.record(config, 1.0, time_keeper.ms())
 | 
						|
    assert 2.0 == count.measure(config, time_keeper.ms())
 | 
						|
    count.record(config, 1.0, time_keeper.ms())  # first event times out
 | 
						|
    assert 2.0 == count.measure(config, time_keeper.ms())
 | 
						|
 | 
						|
 | 
						|
def test_time_windowing(mocker, time_keeper):
 | 
						|
    mocker.patch('time.time', side_effect=time_keeper.time)
 | 
						|
 | 
						|
    count = Count()
 | 
						|
    config = MetricConfig(time_window_ms=1, samples=2)
 | 
						|
    count.record(config, 1.0, time_keeper.ms())
 | 
						|
    time_keeper.sleep(.001)
 | 
						|
    count.record(config, 1.0, time_keeper.ms())
 | 
						|
    assert 2.0 == count.measure(config, time_keeper.ms())
 | 
						|
    time_keeper.sleep(.001)
 | 
						|
    count.record(config, 1.0, time_keeper.ms())  # oldest event times out
 | 
						|
    assert 2.0 == count.measure(config, time_keeper.ms())
 | 
						|
 | 
						|
 | 
						|
def test_old_data_has_no_effect(mocker, time_keeper):
 | 
						|
    mocker.patch('time.time', side_effect=time_keeper.time)
 | 
						|
 | 
						|
    max_stat = Max()
 | 
						|
    min_stat = Min()
 | 
						|
    avg_stat = Avg()
 | 
						|
    count_stat = Count()
 | 
						|
    window_ms = 100
 | 
						|
    samples = 2
 | 
						|
    config = MetricConfig(time_window_ms=window_ms, samples=samples)
 | 
						|
    max_stat.record(config, 50, time_keeper.ms())
 | 
						|
    min_stat.record(config, 50, time_keeper.ms())
 | 
						|
    avg_stat.record(config, 50, time_keeper.ms())
 | 
						|
    count_stat.record(config, 50, time_keeper.ms())
 | 
						|
 | 
						|
    time_keeper.sleep(samples * window_ms / 1000.0)
 | 
						|
    assert float('-inf') == max_stat.measure(config, time_keeper.ms())
 | 
						|
    assert float(sys.maxsize) == min_stat.measure(config, time_keeper.ms())
 | 
						|
    assert 0.0 == avg_stat.measure(config, time_keeper.ms())
 | 
						|
    assert 0 == count_stat.measure(config, time_keeper.ms())
 | 
						|
 | 
						|
 | 
						|
def test_duplicate_MetricName(metrics):
 | 
						|
    metrics.sensor('test').add(metrics.metric_name('test', 'grp1'), Avg())
 | 
						|
    with pytest.raises(ValueError):
 | 
						|
        metrics.sensor('test2').add(metrics.metric_name('test', 'grp1'), Total())
 | 
						|
 | 
						|
 | 
						|
def test_Quotas(metrics):
 | 
						|
    sensor = metrics.sensor('test')
 | 
						|
    sensor.add(metrics.metric_name('test1.total', 'grp1'), Total(),
 | 
						|
               MetricConfig(quota=Quota.upper_bound(5.0)))
 | 
						|
    sensor.add(metrics.metric_name('test2.total', 'grp1'), Total(),
 | 
						|
               MetricConfig(quota=Quota.lower_bound(0.0)))
 | 
						|
    sensor.record(5.0)
 | 
						|
    with pytest.raises(QuotaViolationError):
 | 
						|
        sensor.record(1.0)
 | 
						|
 | 
						|
    assert abs(6.0 - metrics.metrics.get(metrics.metric_name('test1.total', 'grp1')).value()) \
 | 
						|
            < EPS
 | 
						|
 | 
						|
    sensor.record(-6.0)
 | 
						|
    with pytest.raises(QuotaViolationError):
 | 
						|
        sensor.record(-1.0)
 | 
						|
 | 
						|
 | 
						|
def test_Quotas_equality():
 | 
						|
    quota1 = Quota.upper_bound(10.5)
 | 
						|
    quota2 = Quota.lower_bound(10.5)
 | 
						|
    assert quota1 != quota2, 'Quota with different upper values should not be equal'
 | 
						|
 | 
						|
    quota3 = Quota.lower_bound(10.5)
 | 
						|
    assert quota2 == quota3, 'Quota with same upper and bound values should be equal'
 | 
						|
 | 
						|
 | 
						|
def test_Percentiles(metrics):
 | 
						|
    buckets = 100
 | 
						|
    _percentiles = [
 | 
						|
        Percentile(metrics.metric_name('test.p25', 'grp1'), 25),
 | 
						|
        Percentile(metrics.metric_name('test.p50', 'grp1'), 50),
 | 
						|
        Percentile(metrics.metric_name('test.p75', 'grp1'), 75),
 | 
						|
    ]
 | 
						|
    percs = Percentiles(4 * buckets, BucketSizing.CONSTANT, 100.0, 0.0,
 | 
						|
                        percentiles=_percentiles)
 | 
						|
    config = MetricConfig(event_window=50, samples=2)
 | 
						|
    sensor = metrics.sensor('test', config)
 | 
						|
    sensor.add_compound(percs)
 | 
						|
    p25 = metrics.metrics.get(metrics.metric_name('test.p25', 'grp1'))
 | 
						|
    p50 = metrics.metrics.get(metrics.metric_name('test.p50', 'grp1'))
 | 
						|
    p75 = metrics.metrics.get(metrics.metric_name('test.p75', 'grp1'))
 | 
						|
 | 
						|
    # record two windows worth of sequential values
 | 
						|
    for i in range(buckets):
 | 
						|
        sensor.record(i)
 | 
						|
 | 
						|
    assert abs(p25.value() - 25) < 1.0
 | 
						|
    assert abs(p50.value() - 50) < 1.0
 | 
						|
    assert abs(p75.value() - 75) < 1.0
 | 
						|
 | 
						|
    for i in range(buckets):
 | 
						|
        sensor.record(0.0)
 | 
						|
 | 
						|
    assert p25.value() < 1.0
 | 
						|
    assert p50.value() < 1.0
 | 
						|
    assert p75.value() < 1.0
 | 
						|
 | 
						|
def test_rate_windowing(mocker, time_keeper, metrics):
 | 
						|
    mocker.patch('time.time', side_effect=time_keeper.time)
 | 
						|
 | 
						|
    # Use the default time window. Set 3 samples
 | 
						|
    config = MetricConfig(samples=3)
 | 
						|
    sensor = metrics.sensor('test.sensor', config)
 | 
						|
    sensor.add(metrics.metric_name('test.rate', 'grp1'), Rate(TimeUnit.SECONDS))
 | 
						|
 | 
						|
    sum_val = 0
 | 
						|
    count = config.samples - 1
 | 
						|
    # Advance 1 window after every record
 | 
						|
    for i in range(count):
 | 
						|
        sensor.record(100)
 | 
						|
        sum_val += 100
 | 
						|
        time_keeper.sleep(config.time_window_ms / 1000.0)
 | 
						|
 | 
						|
    # Sleep for half the window.
 | 
						|
    time_keeper.sleep(config.time_window_ms / 2.0 / 1000.0)
 | 
						|
 | 
						|
    # prior to any time passing
 | 
						|
    elapsed_secs = (config.time_window_ms * (config.samples - 1) + config.time_window_ms / 2.0) / 1000.0
 | 
						|
 | 
						|
    kafka_metric = metrics.metrics.get(metrics.metric_name('test.rate', 'grp1'))
 | 
						|
    assert abs((sum_val / elapsed_secs) - kafka_metric.value()) < EPS, \
 | 
						|
            'Rate(0...2) = 2.666'
 | 
						|
    assert abs(elapsed_secs - (kafka_metric.measurable.window_size(config, time.time() * 1000) / 1000.0)) \
 | 
						|
            < EPS, 'Elapsed Time = 75 seconds'
 | 
						|
 | 
						|
 | 
						|
def test_reporter(metrics):
 | 
						|
    reporter = DictReporter()
 | 
						|
    foo_reporter = DictReporter(prefix='foo')
 | 
						|
    metrics.add_reporter(reporter)
 | 
						|
    metrics.add_reporter(foo_reporter)
 | 
						|
    sensor = metrics.sensor('kafka.requests')
 | 
						|
    sensor.add(metrics.metric_name('pack.bean1.avg', 'grp1'), Avg())
 | 
						|
    sensor.add(metrics.metric_name('pack.bean2.total', 'grp2'), Total())
 | 
						|
    sensor2 = metrics.sensor('kafka.blah')
 | 
						|
    sensor2.add(metrics.metric_name('pack.bean1.some', 'grp1'), Total())
 | 
						|
    sensor2.add(metrics.metric_name('pack.bean2.some', 'grp1',
 | 
						|
                                    tags={'a': 42, 'b': 'bar'}), Total())
 | 
						|
 | 
						|
    # kafka-metrics-count > count is the total number of metrics and automatic
 | 
						|
    expected = {
 | 
						|
        'kafka-metrics-count': {'count': 5.0},
 | 
						|
        'grp2': {'pack.bean2.total': 0.0},
 | 
						|
        'grp1': {'pack.bean1.avg': 0.0, 'pack.bean1.some': 0.0},
 | 
						|
        'grp1.a=42,b=bar': {'pack.bean2.some': 0.0},
 | 
						|
    }
 | 
						|
    assert expected == reporter.snapshot()
 | 
						|
 | 
						|
    for key in list(expected.keys()):
 | 
						|
        metrics = expected.pop(key)
 | 
						|
        expected['foo.%s' % key] = metrics
 | 
						|
    assert expected == foo_reporter.snapshot()
 | 
						|
 | 
						|
 | 
						|
class ConstantMeasurable(AbstractMeasurable):
 | 
						|
    _value = 0.0
 | 
						|
 | 
						|
    def measure(self, config, now):
 | 
						|
        return self._value
 | 
						|
 | 
						|
 | 
						|
class TimeKeeper(object):
 | 
						|
    """
 | 
						|
    A clock that you can manually advance by calling sleep
 | 
						|
    """
 | 
						|
    def __init__(self, auto_tick_ms=0):
 | 
						|
        self._millis = time.time() * 1000
 | 
						|
        self._auto_tick_ms = auto_tick_ms
 | 
						|
 | 
						|
    def time(self):
 | 
						|
        return self.ms() / 1000.0
 | 
						|
 | 
						|
    def ms(self):
 | 
						|
        self.sleep(self._auto_tick_ms)
 | 
						|
        return self._millis
 | 
						|
 | 
						|
    def sleep(self, seconds):
 | 
						|
        self._millis += (seconds * 1000)
 |