github.com/apache/beam/sdks/v2@v2.48.2/python/apache_beam/metrics/metric_test.py (about)

     1  #
     2  # Licensed to the Apache Software Foundation (ASF) under one or more
     3  # contributor license agreements.  See the NOTICE file distributed with
     4  # this work for additional information regarding copyright ownership.
     5  # The ASF licenses this file to You under the Apache License, Version 2.0
     6  # (the "License"); you may not use this file except in compliance with
     7  # the License.  You may obtain a copy of the License at
     8  #
     9  #    http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  #
    17  
    18  # pytype: skip-file
    19  
    20  # beam-playground:
    21  #   name: MetricTest
    22  #   description: Unit-test for the Metric example.
    23  #   multifile: false
    24  #   context_line: 52
    25  #   categories:
    26  #   - Metrics
    27  #   complexity: MEDIUM
    28  #   tags:
    29  #     - metrics
    30  #     - test
    31  
    32  import unittest
    33  
    34  import hamcrest as hc
    35  import pytest
    36  
    37  import apache_beam as beam
    38  from apache_beam import metrics
    39  from apache_beam.metrics.cells import DistributionData
    40  from apache_beam.metrics.execution import MetricKey
    41  from apache_beam.metrics.execution import MetricsContainer
    42  from apache_beam.metrics.execution import MetricsEnvironment
    43  from apache_beam.metrics.metric import MetricResults
    44  from apache_beam.metrics.metric import Metrics
    45  from apache_beam.metrics.metric import MetricsFilter
    46  from apache_beam.metrics.metricbase import MetricName
    47  from apache_beam.runners.worker import statesampler
    48  from apache_beam.testing.metric_result_matchers import DistributionMatcher
    49  from apache_beam.testing.metric_result_matchers import MetricResultMatcher
    50  from apache_beam.testing.test_pipeline import TestPipeline
    51  from apache_beam.testing.util import assert_that
    52  from apache_beam.testing.util import equal_to
    53  from apache_beam.utils import counters
    54  
    55  
    56  class NameTest(unittest.TestCase):
    57    def test_basic_metric_name(self):
    58      name = MetricName('namespace1', 'name1')
    59      self.assertEqual(name.namespace, 'namespace1')
    60      self.assertEqual(name.name, 'name1')
    61      self.assertEqual(name, MetricName('namespace1', 'name1'))
    62  
    63      key = MetricKey('step1', name)
    64      self.assertEqual(key.step, 'step1')
    65      self.assertEqual(key.metric.namespace, 'namespace1')
    66      self.assertEqual(key.metric.name, 'name1')
    67      self.assertEqual(key, MetricKey('step1', MetricName('namespace1', 'name1')))
    68  
    69  
    70  class MetricResultsTest(unittest.TestCase):
    71    def test_metric_filter_namespace_matching(self):
    72      filter = MetricsFilter().with_namespace('ns1')
    73      name = MetricName('ns1', 'name1')
    74      key = MetricKey('step1', name)
    75      self.assertTrue(MetricResults.matches(filter, key))
    76  
    77    def test_metric_filter_name_matching(self):
    78      filter = MetricsFilter().with_name('name1').with_namespace('ns1')
    79      name = MetricName('ns1', 'name1')
    80      key = MetricKey('step1', name)
    81      self.assertTrue(MetricResults.matches(filter, key))
    82  
    83      filter = MetricsFilter().with_name('name1')
    84      name = MetricName('ns1', 'name1')
    85      key = MetricKey('step1', name)
    86      self.assertTrue(MetricResults.matches(filter, key))
    87  
    88    def test_metric_filter_step_matching(self):
    89      name = MetricName('ns1', 'name1')
    90      filter = MetricsFilter().with_step('Step1')
    91  
    92      key = MetricKey('Step1', name)
    93      self.assertTrue(MetricResults.matches(filter, key))
    94  
    95      key = MetricKey('Step10', name)
    96      self.assertFalse(MetricResults.matches(filter, key))
    97  
    98      key = MetricKey('Step10/Step1', name)
    99      self.assertTrue(MetricResults.matches(filter, key))
   100  
   101      key = MetricKey('Top1/Outer1/Inner1', name)
   102  
   103      filter = MetricsFilter().with_step('Top1/Outer1/Inner1')
   104      self.assertTrue(MetricResults.matches(filter, key))
   105  
   106      filter = MetricsFilter().with_step('Top1/Outer1')
   107      self.assertTrue(MetricResults.matches(filter, key))
   108  
   109      filter = MetricsFilter().with_step('Outer1/Inner1')
   110      self.assertTrue(MetricResults.matches(filter, key))
   111  
   112      filter = MetricsFilter().with_step('Top1/Inner1')
   113      self.assertFalse(MetricResults.matches(filter, key))
   114  
   115  
   116  class MetricsTest(unittest.TestCase):
   117    def test_get_namespace_class(self):
   118      class MyClass(object):
   119        pass
   120  
   121      self.assertEqual(
   122          '{}.{}'.format(MyClass.__module__, MyClass.__name__),
   123          Metrics.get_namespace(MyClass))
   124  
   125    def test_get_namespace_string(self):
   126      namespace = 'MyNamespace'
   127      self.assertEqual(namespace, Metrics.get_namespace(namespace))
   128  
   129    def test_get_namespace_error(self):
   130      with self.assertRaises(ValueError):
   131        Metrics.get_namespace(object())
   132  
   133    def test_counter_empty_name(self):
   134      with self.assertRaises(ValueError):
   135        Metrics.counter("namespace", "")
   136  
   137    def test_counter_empty_namespace(self):
   138      with self.assertRaises(ValueError):
   139        Metrics.counter("", "names")
   140  
   141    def test_distribution_empty_name(self):
   142      with self.assertRaises(ValueError):
   143        Metrics.distribution("namespace", "")
   144  
   145    def test_distribution_empty_namespace(self):
   146      with self.assertRaises(ValueError):
   147        Metrics.distribution("", "names")
   148  
   149    # Do not change the behaviour of str(), do tno update/delete this test case
   150    # if the behaviour of str() is changed. Doing so will
   151    # break end user beam code which depends on the str() behaviour.
   152    def test_user_metric_name_str(self):
   153      mn = MetricName("my_namespace", "my_name")
   154      expected_str = 'MetricName(namespace=my_namespace, name=my_name)'
   155      self.assertEqual(str(mn), expected_str)
   156  
   157    def test_general_urn_metric_name_str(self):
   158      mn = MetricName(
   159          "my_namespace", "my_name", urn='my_urn', labels={'key': 'value'})
   160      expected_str = (
   161          "MetricName(namespace=my_namespace, name=my_name, "
   162          "urn=my_urn, labels={'key': 'value'})")
   163      self.assertEqual(str(mn), expected_str)
   164  
   165    @pytest.mark.it_validatesrunner
   166    def test_user_counter_using_pardo(self):
   167      class SomeDoFn(beam.DoFn):
   168        """A custom dummy DoFn using yield."""
   169        static_counter_elements = metrics.Metrics.counter(
   170            "SomeDoFn", 'metrics_static_counter_element')
   171  
   172        def __init__(self):
   173          self.user_counter_elements = metrics.Metrics.counter(
   174              self.__class__, 'metrics_user_counter_element')
   175  
   176        def process(self, element):
   177          self.static_counter_elements.inc(2)
   178          self.user_counter_elements.inc()
   179          distro = Metrics.distribution(self.__class__, 'element_dist')
   180          distro.update(element)
   181          yield element
   182  
   183      pipeline = TestPipeline()
   184      nums = pipeline | 'Input' >> beam.Create([1, 2, 3, 4])
   185      results = nums | 'ApplyPardo' >> beam.ParDo(SomeDoFn())
   186      assert_that(results, equal_to([1, 2, 3, 4]))
   187  
   188      res = pipeline.run()
   189      res.wait_until_finish()
   190  
   191      # Verify static counter.
   192      metric_results = (
   193          res.metrics().query(
   194              MetricsFilter().with_metric(SomeDoFn.static_counter_elements)))
   195      outputs_static_counter = metric_results['counters'][0]
   196  
   197      self.assertEqual(
   198          outputs_static_counter.key.metric.name,
   199          'metrics_static_counter_element')
   200      self.assertEqual(outputs_static_counter.committed, 8)
   201  
   202      # Verify user counter.
   203      metric_results = (
   204          res.metrics().query(
   205              MetricsFilter().with_name('metrics_user_counter_element')))
   206      outputs_user_counter = metric_results['counters'][0]
   207  
   208      self.assertEqual(
   209          outputs_user_counter.key.metric.name, 'metrics_user_counter_element')
   210      self.assertEqual(outputs_user_counter.committed, 4)
   211  
   212      # Verify user distribution counter.
   213      metric_results = res.metrics().query()
   214      matcher = MetricResultMatcher(
   215          step='ApplyPardo',
   216          namespace=hc.contains_string('SomeDoFn'),
   217          name='element_dist',
   218          committed=DistributionMatcher(
   219              sum_value=hc.greater_than_or_equal_to(0),
   220              count_value=hc.greater_than_or_equal_to(0),
   221              min_value=hc.greater_than_or_equal_to(0),
   222              max_value=hc.greater_than_or_equal_to(0)))
   223      hc.assert_that(
   224          metric_results['distributions'], hc.contains_inanyorder(matcher))
   225  
   226    def test_create_counter_distribution(self):
   227      sampler = statesampler.StateSampler('', counters.CounterFactory())
   228      statesampler.set_current_tracker(sampler)
   229      state1 = sampler.scoped_state(
   230          'mystep', 'myState', metrics_container=MetricsContainer('mystep'))
   231  
   232      try:
   233        sampler.start()
   234        with state1:
   235          counter_ns = 'aCounterNamespace'
   236          distro_ns = 'aDistributionNamespace'
   237          name = 'a_name'
   238          counter = Metrics.counter(counter_ns, name)
   239          distro = Metrics.distribution(distro_ns, name)
   240          counter.inc(10)
   241          counter.dec(3)
   242          distro.update(10)
   243          distro.update(2)
   244          self.assertTrue(isinstance(counter, Metrics.DelegatingCounter))
   245          self.assertTrue(isinstance(distro, Metrics.DelegatingDistribution))
   246  
   247          del distro
   248          del counter
   249  
   250          container = MetricsEnvironment.current_container()
   251          self.assertEqual(
   252              container.get_counter(MetricName(counter_ns,
   253                                               name)).get_cumulative(),
   254              7)
   255          self.assertEqual(
   256              container.get_distribution(MetricName(distro_ns,
   257                                                    name)).get_cumulative(),
   258              DistributionData(12, 2, 2, 10))
   259      finally:
   260        sampler.stop()
   261  
   262  
   263  if __name__ == '__main__':
   264    unittest.main()