Exemple #1
0
 def setUp(self):
     self.config_commandline = {
         "module": "scalyr_agent.builtin_monitors.linux_process_metrics",
         "id": "myapp",
         "commandline": ".foo.*",
     }
     self.monitor = ProcessMonitor(self.config_commandline, scalyr_logging.getLogger("syslog_monitor[test]"))
Exemple #2
0
    def test_gather_sample_by_pid_failure_pid_doesnt_exist(self):
        monitor_config = {
            "module": "linux_process_metrics",
            "id": "my-id",
            "pid": 65555,
        }
        mock_logger = mock.Mock()
        monitor = ProcessMonitor(monitor_config, mock_logger)

        monitor.gather_sample()
        self.assertEqual(mock_logger.error.call_count, 3)
        self.assertEqual(mock_logger.emit_value.call_count, 0)
Exemple #3
0
 def test_initialize_monitor(self):
     monitor = ProcessMonitor(
         self.config_commandline,
         scalyr_logging.getLogger("syslog_monitor[test]"))
     self.assertEqual(monitor._ProcessMonitor__metrics_history,
                      defaultdict(dict))
     self.assertEqual(monitor._ProcessMonitor__aggregated_metrics, {})
 def setUp(self):
     self.config_commandline = {
         "module": "scalyr_agent.builtin_monitors.linux_process_metrics",
         "id": "myapp",
         "commandline": ".foo.*",
     }
     self.monitor = ProcessMonitor(self.config_commandline, scalyr_logging.getLogger("syslog_monitor[test]"))
Exemple #5
0
    def test_gather_sample_by_commandline_success(self):
        monitor_config = {
            "module": "linux_process_metrics",
            "id": "my-id",
            "commandline": ".*%s.*" % (" ".join(sys.argv)),
        }
        mock_logger = mock.Mock()
        monitor = ProcessMonitor(monitor_config, mock_logger)

        monitor_module = "scalyr_agent.builtin_monitors.linux_process_metrics"
        monitor_info = load_monitor_class(monitor_module, [])[1]

        monitor.gather_sample()

        self.assertEqual(mock_logger.error.call_count, 0)
        self.assertEqual(mock_logger.emit_value.call_count, len(monitor_info.metrics))
    def test_late_process_pid_setting(self):
        self.config = {
            "module": "scalyr_agent.builtin_monitors.linux_process_metrics",
            "id": "myapp",
            # the process ID is not known yet.
            "pid": "$$TBD",
        }
        self.monitor = ProcessMonitor(
            self.config, scalyr_logging.getLogger("syslog_monitor[test]"))

        # it can not select any process until the process id is set.
        self.assertEqual(self.monitor._select_processes(), [])
        self.assertEqual(self.monitor._select_processes(), [])

        # set the process id.
        self.monitor.set_pid(os.getpid())

        #  it has to select the current PID after it has been set.
        self.assertEqual(self.monitor._select_processes(), [os.getpid()])
Exemple #7
0
class TestProcessMonitorRunningTotal(ScalyrTestCase):
    """
    Tests the calculations of the running totals of the metrics.
    """
    def setUp(self):
        super(TestProcessMonitorRunningTotal, self).setUp()
        self.config_commandline = {
            "module": "scalyr_agent.builtin_monitors.linux_process_metrics",
            "id": "myapp",
            "commandline": ".foo.*",
        }
        self.monitor = ProcessMonitor(
            self.config_commandline,
            scalyr_logging.getLogger("syslog_monitor[test]"))

    def test_no_history(self):
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_single_process_single_epoch(self):
        metric = Metric("fakemetric", "faketype")
        metrics = {metric: 21}
        self.monitor.record_metrics(555, metrics)
        self.monitor._ProcessMonitor__pids = [555]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {555: {metric: [21]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics,
                         {metric: 21})

    def test_single_process_multiple_epoch(self):
        metric = Metric("fakemetric", "faketype")

        # epoch 1
        metrics = {metric: 21}
        self.monitor.record_metrics(555, metrics)
        self.monitor._ProcessMonitor__pids = [555]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {555: {metric: [21]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics,
                         {metric: 21})

        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics = {metric: 21.5}
        self.monitor.record_metrics(555, metrics)
        self.monitor._ProcessMonitor__pids = [555]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {555: {metric: [21, 21.5]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics,
                         {metric: 21.5})

    def test_multiple_process_multiple_epochs(self):
        metric1 = Metric("fakemetric1", "faketype1")
        metric2 = Metric("fakemetric2", "faketype2")

        # epoch 1
        metrics1 = {metric1: 21}
        metrics2 = {metric2: 100.0}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {1: {metric1: [21]}, 2: {metric2: [100.0]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: 21,
                metric2: 100.0
            },
        )
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 21.11}
        metrics2 = {metric2: 100.11}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {
                metric1: [21, 21.11]
            },
            2: {
                metric2: [100.0, 100.11]
            }
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: 21.11,
                metric2: 100.11
            },
        )

    def test_multiple_process_multiple_epochs_cumulative_metrics(self):
        metric1 = Metric("app.cpu", "system")

        # epoch 1
        metrics1 = {metric1: 20}
        metrics2 = {metric1: 40}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {1: {metric1: [20]}, 2: {metric1: [40]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics,
                         {metric1: 0})
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 22}
        metrics2 = {metric1: 44}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {1: {metric1: [20, 22]}, 2: {metric1: [40, 44]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {metric1: (22 - 20) + (44 - 40)},
        )

        # epoch 3
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 25}
        metrics2 = {metric1: 48}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        # we only keep the last 2 historical values
        expected_history = {1: {metric1: [22, 25]}, 2: {metric1: [44, 48]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {metric1: (22 - 20) + (44 - 40) + (25 - 22) + (48 - 44)},
        )

    def test_multiple_process_multiple_epochs_cumulative_metrics_one_process_death(
        self, ):
        """
        Same as test_multiple_process_multiple_epochs_cumulative_metrics
        but one process dies after epoch 2
        """

        metric1 = Metric("app.cpu", "system")

        # epoch 1
        metrics1 = {metric1: 21}
        metrics2 = {metric1: 100.0}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {1: {metric1: [21]}, 2: {metric1: [100.0]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics,
                         {metric1: 0})
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 30.1}
        metrics2 = {metric1: 100.2}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {
                metric1: [21, 30.1]
            },
            2: {
                metric1: [100.0, 100.2]
            }
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {metric1: (30.1 - 21) + (100.2 - 100.0)},
        )

        # epoch 3
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 26.0}
        metrics2 = {metric1: 103}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)

        # Process 1 dies.. boom
        self.monitor._ProcessMonitor__pids = [2]
        self.monitor._calculate_aggregated_metrics()

        # we only keep the last 2 historical values
        expected_history = {
            1: {
                metric1: [30.1, 26.0]
            },
            2: {
                metric1: [100.2, 103]
            }
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)

        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {metric1: (30.1 - 21) + (100.2 - 100.0) + (103 - 100.2)},
        )

    def test_multiple_process_multiple_epochs_cumulative_metrics_all_process_death(
        self, ):
        """
        Same as test_multiple_process_multiple_epochs_cumulative_metrics_one_process_death
        but all processes die after epoch 2
        """

        metric1 = Metric("app.cpu", "system")

        # epoch 1
        metrics1 = {metric1: 20}
        metrics2 = {metric1: 40}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {1: {metric1: [20]}, 2: {metric1: [40]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics,
                         {metric1: 0})
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 25}
        metrics2 = {metric1: 46}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {1: {metric1: [20, 25]}, 2: {metric1: [40, 46]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {metric1: (25 - 20) + (46 - 40)},
        )

        # epoch 3
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 23}
        metrics2 = {metric1: 43}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)

        # Process 1 and 2 die.. boom
        # we should ensure the total running value for metric doesn't go down.
        self.monitor._ProcessMonitor__pids = []
        self.monitor._calculate_aggregated_metrics()

        # we only keep the last 2 historical values
        expected_history = {1: {metric1: [25, 23]}, 2: {metric1: [46, 43]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)

        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {metric1: (25 - 20) + (46 - 40)},
        )
Exemple #8
0
class TestProcessMonitorRecordMetrics(ScalyrTestCase):
    """
    Tests the record_metrics method of ProcessMonitor class
    """
    def setUp(self):
        super(TestProcessMonitorRecordMetrics, self).setUp()
        self.config_commandline = {
            "module": "scalyr_agent.builtin_monitors.linux_process_metrics",
            "id": "myapp",
            "commandline": ".foo.*",
        }

        self.monitor = ProcessMonitor(
            self.config_commandline,
            scalyr_logging.getLogger("syslog_monitor[test]"))

    def test_empty_metrics(self):
        self.monitor.record_metrics(666, {})
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         defaultdict(dict))

    def test_single_process_single_epoch(self):
        metric = Metric("fakemetric", "faketype")
        metrics = {metric: 21}
        self.monitor.record_metrics(555, metrics)
        expected_history = {555: {metric: [21]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_single_process_multiple_epochs(self):
        metric = Metric("fakemetric", "faketype")

        # epoch 1
        self.monitor.record_metrics(777, {metric: 1.2})
        expected_history = {777: {metric: [1.2]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

        # epoch 2
        self.monitor.record_metrics(777, {metric: 1.9})
        expected_history = {777: {metric: [1.2, 1.9]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_multi_process_single_epoch(self):
        metric1 = Metric("fakemetric1", "faketype1")
        metric2 = Metric("fakemetric2", "faketype2")

        self.monitor.record_metrics(111, {metric1: 1.2})
        self.monitor.record_metrics(222, {metric2: 2.87})
        expected_history = {111: {metric1: [1.2]}, 222: {metric2: [2.87]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_multi_process_multi_epochs(self):
        metric1 = Metric("fakemetric1", "faketype1")
        metric2 = Metric("fakemetric2", "faketype2")

        # epoch 1
        self.monitor.record_metrics(111, {metric1: 1.2})
        self.monitor.record_metrics(222, {metric2: 2.87})
        expected_history = {111: {metric1: [1.2]}, 222: {metric2: [2.87]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

        # epoch 2
        self.monitor.record_metrics(111, {metric1: 1.6})
        self.monitor.record_metrics(222, {metric2: 2.92})
        expected_history = {
            111: {
                metric1: [1.2, 1.6]
            },
            222: {
                metric2: [2.87, 2.92]
            }
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history,
                         expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})
class TestProcessMonitorRecordMetrics(ScalyrTestCase):
    """
    Tests the record_metrics method of ProcessMonitor class
    """

    def setUp(self):
        self.config_commandline = {
            "module": "scalyr_agent.builtin_monitors.linux_process_metrics",
            "id": "myapp",
            "commandline": ".foo.*",
        }

        self.monitor = ProcessMonitor(self.config_commandline, scalyr_logging.getLogger("syslog_monitor[test]"))

    def test_empty_metrics(self):
        self.monitor.record_metrics(666, {})
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, defaultdict(dict))

    def test_single_process_single_epoch(self):
        metric = Metric('fakemetric', 'faketype')
        metrics = {
            metric: 21
        }
        self.monitor.record_metrics(555, metrics)
        expected_history = {
            555: {metric: [21]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_single_process_multiple_epochs(self):
        metric = Metric('fakemetric', 'faketype')

        # epoch 1
        self.monitor.record_metrics(777, {metric: 1.2})
        expected_history = {
            777: {metric: [1.2]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

        # epoch 2
        self.monitor.record_metrics(777, {metric: 1.9})
        expected_history = {
            777: {metric: [1.2, 1.9]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_multi_process_single_epoch(self):
        metric1 = Metric('fakemetric1', 'faketype1')
        metric2 = Metric('fakemetric2', 'faketype2')

        self.monitor.record_metrics(111, {metric1: 1.2})
        self.monitor.record_metrics(222, {metric2: 2.87})
        expected_history = {
            111: {metric1: [1.2]},
            222: {metric2: [2.87]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_multi_process_multi_epochs(self):
        metric1 = Metric('fakemetric1', 'faketype1')
        metric2 = Metric('fakemetric2', 'faketype2')

        # epoch 1
        self.monitor.record_metrics(111, {metric1: 1.2})
        self.monitor.record_metrics(222, {metric2: 2.87})
        expected_history = {
            111: {metric1: [1.2]},
            222: {metric2: [2.87]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

        # epoch 2
        self.monitor.record_metrics(111, {metric1: 1.6})
        self.monitor.record_metrics(222, {metric2: 2.92})
        expected_history = {
            111: {metric1: [1.2, 1.6]},
            222: {metric2: [2.87, 2.92]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})
class TestProcessMonitorRunningTotal(ScalyrTestCase):
    """
    Tests the calculations of the running totals of the metrics.
    """

    def setUp(self):
        self.config_commandline = {
            "module": "scalyr_agent.builtin_monitors.linux_process_metrics",
            "id": "myapp",
            "commandline": ".foo.*",
        }
        self.monitor = ProcessMonitor(self.config_commandline, scalyr_logging.getLogger("syslog_monitor[test]"))

    def test_no_history(self):
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {})

    def test_single_process_single_epoch(self):
        metric = Metric('fakemetric', 'faketype')
        metrics = {
            metric: 21
        }
        self.monitor.record_metrics(555, metrics)
        self.monitor._ProcessMonitor__pids = [555]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            555: {metric: [21]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {metric: 21})

    def test_single_process_multiple_epoch(self):
        metric = Metric('fakemetric', 'faketype')

        # epoch 1
        metrics = {metric: 21}
        self.monitor.record_metrics(555, metrics)
        self.monitor._ProcessMonitor__pids = [555]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {555: {metric: [21]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {metric: 21})

        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics = {metric: 21.5}
        self.monitor.record_metrics(555, metrics)
        self.monitor._ProcessMonitor__pids = [555]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {555: {metric: [21, 21.5]}}
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(self.monitor._ProcessMonitor__aggregated_metrics, {metric: 21.5})

    def test_multiple_process_multiple_epochs(self):
        metric1 = Metric('fakemetric1', 'faketype1')
        metric2 = Metric('fakemetric2', 'faketype2')

        # epoch 1
        metrics1 = {metric1: 21}
        metrics2 = {metric2: 100.0}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [21]},
            2: {metric2: [100.0]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: 21,
                metric2: 100.0
            }
        )
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 21.11}
        metrics2 = {metric2: 100.11}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [21, 21.11]},
            2: {metric2: [100.0, 100.11]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: 21.11,
                metric2: 100.11
            }
        )

    def test_multiple_process_multiple_epochs_cumulative_metrics(self):
        metric1 = Metric('app.cpu', 'system')

        # epoch 1
        metrics1 = {metric1: 20}
        metrics2 = {metric1: 40}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [20]},
            2: {metric1: [40]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: 0
            }
        )
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 22}
        metrics2 = {metric1: 44}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [20, 22]},
            2: {metric1: [40, 44]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: (22 - 20) + (44 - 40)
            }
        )

        # epoch 3
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 25}
        metrics2 = {metric1: 48}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        # we only keep the last 2 historical values
        expected_history = {
            1: {metric1: [22, 25]},
            2: {metric1: [44, 48]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: (22 - 20) + (44 - 40) + (25 - 22) + (48 - 44)
            }
        )

    def test_multiple_process_multiple_epochs_cumulative_metrics_one_process_death(self):
        """
        Same as test_multiple_process_multiple_epochs_cumulative_metrics
        but one process dies after epoch 2
        """

        metric1 = Metric('app.cpu', 'system')

        # epoch 1
        metrics1 = {metric1: 21}
        metrics2 = {metric1: 100.0}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [21]},
            2: {metric1: [100.0]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: 0
            }
        )
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 30.1}
        metrics2 = {metric1: 100.2}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [21, 30.1]},
            2: {metric1: [100.0, 100.2]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: (30.1 - 21) + (100.2 - 100.0)
            }
        )

        # epoch 3
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 26.0}
        metrics2 = {metric1: 103}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)

        # Process 1 dies.. boom
        self.monitor._ProcessMonitor__pids = [2]
        self.monitor._calculate_aggregated_metrics()

        # we only keep the last 2 historical values
        expected_history = {
            1: {metric1: [30.1, 26.0]},
            2: {metric1: [100.2, 103]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)

        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: (30.1 - 21) + (100.2 - 100.0) + (103 - 100.2)
            }
        )

    def test_multiple_process_multiple_epochs_cumulative_metrics_all_process_death(self):
        """
        Same as test_multiple_process_multiple_epochs_cumulative_metrics_one_process_death
        but all processes die after epoch 2
        """

        metric1 = Metric('app.cpu', 'system')

        # epoch 1
        metrics1 = {metric1: 20}
        metrics2 = {metric1: 40}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1,2 ]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [20]},
            2: {metric1: [40]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: 0
            }
        )
        # epoch 2
        # before epoch 2, the reset is called for absolute metrics
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 25}
        metrics2 = {metric1: 46}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)
        self.monitor._ProcessMonitor__pids = [1, 2]
        self.monitor._calculate_aggregated_metrics()
        expected_history = {
            1: {metric1: [20, 25]},
            2: {metric1: [40, 46]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)
        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: (25 - 20) + (46 - 40)
            }
        )

        # epoch 3
        self.monitor._reset_absolute_metrics()

        metrics1 = {metric1: 23}
        metrics2 = {metric1: 43}
        self.monitor.record_metrics(1, metrics1)
        self.monitor.record_metrics(2, metrics2)

        # Process 1 and 2 die.. boom
        # we should ensure the total running value for metric doesn't go down.
        self.monitor._ProcessMonitor__pids = []
        self.monitor._calculate_aggregated_metrics()

        # we only keep the last 2 historical values
        expected_history = {
            1: {metric1: [25, 23]},
            2: {metric1: [46, 43]}
        }
        self.assertEqual(self.monitor._ProcessMonitor__metrics_history, expected_history)

        self.assertEqual(
            self.monitor._ProcessMonitor__aggregated_metrics,
            {
                metric1: (25 - 20) + (46 - 40)
            }
        )