def test_valid_publish_statements_plot_happy(): Program( Plot('A', 'foo', fx=[Max()], label="lol") ).validate() Program( Plot('A', 'foo', fx=None, label="lol") ).validate()
def test_program_init_add_statement_invalid(value): """Ensure that neither Program's constructor nor add_statement allows non-statement values. """ with pytest.raises(ValueError): Program(value) with pytest.raises(ValueError): Program().add_statements(value)
def test_detector_with_assign_combinator(): """ We should correctly generate a detector comprised of two assignment functions """ cpu_util_string = 'cpu.utilization' sum_string = 'utilization_sum' count_string = 'utilization_count' mean_string = 'utilization_mean' sum_data = Data(cpu_util_string).sum() count_data = Data(cpu_util_string).count() utilization_sum = Assign(sum_string, sum_data) utilization_count = Assign(count_string, count_data) mean_data = Div(Ref(sum_string), Ref(count_string)) utilization_mean = Assign(mean_string, mean_data) detect = Detect(When(GT(Ref(mean_string), 50))).publish(label='detector') program = Program(utilization_sum, utilization_count, utilization_mean, detect) detector = Detector().with_program(program) assert detector.options["programText"] == "{0}\n{1}\n{2}\n{3}".format( str(utilization_sum), str(utilization_count), str(utilization_mean), str(detect)) assert program.statements.pop() == detect assert program.statements.pop() == utilization_mean assert program.statements.pop() == utilization_count assert program.statements.pop() == utilization_sum
def from_chart(self, chart, update_fn): """Given a Chart and an update fn, return a SignalFlow program. Arguments: chart: the Chart object containing the desired SignalFlow program. update_fn: a function of type Program -> Program, allowing you to access the program of a given chart and return a modified version for this detector. """ if not issubclass(chart.__class__, Chart): msg = 'Expected a Chart but got a "{0}" instead when building ' +\ 'a Detector named "{1}".' raise ValueError(msg.format( chart.__class__.__name__, self.__get__('name', 'undefined') )) program = deepcopy(chart.__get__('programText', Program())) if not issubclass(program.__class__, Program): msg = """ Detector.from_chart only supports Charts that implement a Program. "{0}" contains a "{1}". You might consider contacting the Chart author to update their configuration to implement a proper `Program` from the `signal_analog.flow` module. """ raise ValueError(msg.format( chart.__class__.__name__, program.__class__.__name__ )) self.options.update({'programText': str(update_fn(program))}) return self
def test_op_combines_mul(): data1 = Data('request.mean')\ .publish(label='A') data2 = Data('request.count')\ .publish(label='B') muldata = Op(Mul(data1, data2)) program = Program(muldata) assert program.statements[0] == muldata
def test_detector_from_chart(): program = Program(Data('cpu.utilization').publish(label='Z')) chart = TimeSeriesChart().with_program(program) def helper(p): return Program(Detect(LT(p.find_label('Z'), 10)).publish(label='foo')) detector = Detector().from_chart(chart, helper) assert detector.options['programText'] == str(helper(program))
def fun(name, threshold): program = Program( Detect(GT(Data("cpu.utilization"), threshold)).publish(label=name)) rule = (Rule().for_label(name).with_severity( Severity.Info).with_notifications( EmailNotification("*****@*****.**"))) return (Detector(session=session).with_name(name).with_program( program).with_rules(rule))
def test_detector_from_chart_mod_prog(): """We shouldn't be able to muck about with programs from charts.""" program = Program(Data('disk.utilization').publish(label='X')) prog_size = len(program.statements) chart = TimeSeriesChart().with_program(program) def bad_helper(p): p.add_statements(Data("I shouldn't exist").publish(label='Y')) return Program(Detect(LT(p.find_label('Z'), 10)).publish(label='foo')) Detector().from_chart(chart, bad_helper) # bad_helper should not be allowed to add new program statements to # the originating chart's program. assert prog_size == len(program.statements)
def test_program_init_valid_empty(): assert Program().statements == []
from signal_analog.flow import Data, Detect, Filter, Program from signal_analog.combinators import And, LT """ Example 1: from scratch This is useful when you want to monitor something that isn't tied to an existing chart or dashboard. """ alert_label = 'CPU is too low for 75% of the last 2 minutes!' filters = And(Filter('app', 'my-app'), Filter('env', 'test')) data = Data('cpu.utilization', filter=filters).publish(label='A') cpu_too_low = Detect(When(LT(data, 50), '2m', 0.75)).publish(alert_label) program = Program(cpu_too_low) info_rule = Rule()\ .for_label(alert_label)\ .with_severity(Severity.Info)\ .with_notifications(EmailNotification('*****@*****.**')) detector = Detector()\ .with_name('TEST: example detector')\ .with_program(program)\ .with_rules(info_rule) """ Example 2: from an existing chart This is useful when you want to alert on behavior seen in a chart that was
def test_valid_publish_statement_comb_valid(): Program( Op(Div(Data('foo'), Data('bar'))).publish(label='foobar') ).validate()
def test_valid_publish_statements_comb_invalid(): with pytest.raises(ProgramDoesNotPublishTimeseriesError): Program( Op(Div(Data('foo'), Data('bar'))) ).validate()
def test_find_label_empty(): assert Program().find_label('A') is None
def test_find_label_unpublished(): data = Data('cpu.utilization', filter=Filter('app', 'test-app')) program = Program(data) assert program.find_label('A') is None
def test_valid_publish_statements_happy(): data = Data('requests.mean').publish(label='foo') Program(data).validate()
def test_valid_publish_statements_default(): data = Data('requests.mean') with pytest.raises(ProgramDoesNotPublishTimeseriesError): Program(data).validate()
def bad_helper(p): p.add_statements(Data("I shouldn't exist").publish(label='Y')) return Program(Detect(LT(p.find_label('Z'), 10)).publish(label='foo'))
def helper(p): return Program(Detect(LT(p.find_label('Z'), 10)).publish(label='foo'))
def test_program_init_valid_statements(): data = Data('cpu.utilization', filter=Filter('app', 'test-app')) program = Program(data) assert program.statements[0] == data
def test_when(): data1 = Data('request.mean')\ .publish(label='A') when = When(GT(data1, 50), '5m', 0.5) program = Program(when) assert program.statements[0] == when
def test_add_statements(): data = Data('foo').publish(label='A', enable='False') program = Program() program.add_statements(data) assert program.statements[0] == data
def __program__(self, app, env): app_filter = And(Filter('app', 'my-app'), Filter('env', 'prod')) return Program(Data('cpu.user', filter=app_filter).mean().publish('A'))
def test_find_label_published(): data = Data('cpu.utilization', filter=Filter('app', 'test-app'))\ .publish(label='A') program = Program(data) assert program.find_label('A') == data
def detector_helper(prog): d = prog.find_label('A') return Program(Detect(LT(d, 1)).publish(alert_label))
def test_valid_publish_statements_multi(): Program( Data('requests.mean'), Data('foo').publish(label='foo') ).validate()
def test_detector_with_program(): program = Program( Data('foo').publish(label='A'), Data('bar').publish(label='B')) d = Detector().with_program(program) assert d.options['programText'] == str(program)
"""Examples for the `signal_analog.charts` module.""" from signal_analog.charts \ import TimeSeriesChart, PlotType, PublishLabelOptions, PaletteColor from signal_analog.flow import Data, Filter, Program from signal_analog.combinators import And """ Example 1: single-use chart This is useful when you just want to create a chart and aren't worried about re-usability. """ # Look at the mean of the cpu.user metric for my-app in the prod environment app_filter = And(Filter('app', 'my-app'), Filter('env', 'prod')) program = Program(Data('cpu.user', filter=app_filter).mean().publish('A')) chart = TimeSeriesChart()\ .with_name('CPU Used %')\ .with_description('% CPU used by user')\ .stack_chart(True)\ .with_default_plot_type(PlotType.area_chart)\ .with_program(program) """ Example 2: make a re-usable chart (or template) This is useful when you want your chart to be broadly applicable/used by others """ class UserCpuUsedPercentChart(TimeSeriesChart):
def test_valid_publish_statements_assign_happy(): Program( Assign('A', Data('foo').publish(label='lol')) ).validate()
#!/usr/bin/env python """Examples of how to use the `signal_analog.flow` module. Some basic understanding of SignalFx is assumed. """ from signal_analog.flow import Data, Filter, Program # A program is a convenient wrapper around SignalFlow statements with a few # utilities like `find_label` that returns a SignalFlow statement based on it's # label. program = Program() # A timeseries representing the 'cpu.utilization' metric that is filtered # down to just the 'shoeadmin' application. Also analyze the mean over the # previous minute and compare it to the data from last week. data = Data('cpu.utilization', filter=Filter('app', 'shoeadmin'))\ .mean(over='1m')\ .timeshift('1w')\ .publish('A') program.add_statements(data) print('{0}\n\t{1}'.format(program, str(program) == str(program.find_label('A'))))
def test_valid_publish_statements_assign_invalid(): with pytest.raises(ProgramDoesNotPublishTimeseriesError): Program( Assign('A', Data('foo')) ).validate()