[autotest] Functional test apache_error_log_metrics

Added a flag --debug-metrics-file to apache_error_log_metrics, which
allows it to send metrics to a file instead of production.

Added a unit test using --debug-metrics-file which tests the script
end-to-end.

BUG=chromium:721481
TEST=The new unit test passes.

Change-Id: I239a89e0089d28cb31613c06f05bd79434d9a739
Reviewed-on: https://chromium-review.googlesource.com/503528
Commit-Ready: Paul Hobbs <phobbs@google.com>
Tested-by: Paul Hobbs <phobbs@google.com>
Reviewed-by: Paul Hobbs <phobbs@google.com>
diff --git a/site_utils/stats/apache_error_log_metrics.py b/site_utils/stats/apache_error_log_metrics.py
index a77d16b..729b3b2 100755
--- a/site_utils/stats/apache_error_log_metrics.py
+++ b/site_utils/stats/apache_error_log_metrics.py
@@ -89,12 +89,22 @@
                     emitter(m)
 
 
-def Main():
-    """Sets up logging and runs matchers against stdin"""
+def ParseArgs():
+    """Parses the command line arguments."""
     p = argparse.ArgumentParser(
         description='Parses apache logs and emits metrics to Monarch')
     p.add_argument('--output-logfile')
-    args = p.parse_args()
+    p.add_argument('--debug-metrics-file',
+                   help='Output metrics to the given file instead of sending '
+                   'them to production.')
+    return p.parse_args()
+
+
+def Main():
+    """Sets up logging and runs matchers against stdin"""
+    args = ParseArgs()
+
+    # Set up logging.
     root = logging.getLogger()
     if args.output_logfile:
         handler = handlers.RotatingFileHandler(
@@ -104,8 +114,15 @@
         root.addHandler(logging.StreamHandler(sys.stdout))
     root.setLevel(logging.DEBUG)
 
-    ts_mon_config.SetupTsMonGlobalState('apache_error_log_metrics')
-    RunMatchers(sys.stdin, MATCHERS)
+    # Set up metrics sending and go.
+    ts_mon_args = {}
+    if args.debug_metrics_file:
+        ts_mon_args['debug_file'] = args.debug_metrics_file
+
+    with ts_mon_config.SetupTsMonGlobalState('apache_error_log_metrics',
+                                             **ts_mon_args):
+      RunMatchers(sys.stdin, MATCHERS)
+      metrics.Flush()
 
 
 if __name__ == '__main__':
diff --git a/site_utils/stats/apache_error_log_metrics_unittest.py b/site_utils/stats/apache_error_log_metrics_unittest.py
index 7980fa3..ea63800 100644
--- a/site_utils/stats/apache_error_log_metrics_unittest.py
+++ b/site_utils/stats/apache_error_log_metrics_unittest.py
@@ -1,6 +1,9 @@
 """Tests for apache_error_log_metrics."""
 
 import os
+import re
+import subprocess
+import tempfile
 import unittest
 
 import common
@@ -8,6 +11,11 @@
 import apache_error_log_metrics
 
 
+SCRIPT_PATH = os.path.abspath(
+    os.path.join(os.path.dirname(__file__),
+                 'apache_error_log_metrics.py'))
+
+
 class ApacheErrorTest(unittest.TestCase):
     """Unittest for the apache error log regexp."""
 
@@ -61,6 +69,53 @@
         self.assertEqual('error', matched[4].group('log_level'))
         self.assertEqual(None, matched[4].group('mod_wsgi'))
 
+    def testApacheErrorLogScriptWithNonMatchingLine(self):
+        """Try shelling out the the script with --debug-file.
+
+        Sending it a non-matching line should result in no output from
+        ERROR_LOG_METRIC.
+        """
+        with tempfile.NamedTemporaryFile() as temp_file:
+            p = subprocess.Popen([SCRIPT_PATH,
+                                  '--debug-metrics-file', temp_file.name],
+                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+            p.communicate('an-example-log-line')
+
+            with open(temp_file.name) as fh:
+                contents = fh.read()
+
+            # We have to use re.search here with a word border character ('\b')
+            # because the ERROR_LOG_LINE_METRIC contains ERROR_LOG_METRIC as a
+            # substring.
+            self.assertTrue(re.search(
+                apache_error_log_metrics.ERROR_LOG_LINE_METRIC[1:] + r'\b',
+                contents))
+            self.assertFalse(re.search(
+                apache_error_log_metrics.ERROR_LOG_METRIC[1:] + r'\b',
+                contents))
+
+    def testApachErrorLogScriptWithMatchingLine(self):
+        """Try shelling out the the script with --debug-file.
+
+        Sending it a line which matches the first-line regex should result in
+        output from ERROR_LOG_METRIC.
+        """
+        with tempfile.NamedTemporaryFile() as temp_file:
+            p = subprocess.Popen([SCRIPT_PATH,
+                                  '--debug-metrics-file', temp_file.name],
+                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+            p.communicate('[foo] [:bar] [pid 123] WARNING')
+
+            with open(temp_file.name) as fh:
+                contents = fh.read()
+
+            self.assertTrue(re.search(
+                apache_error_log_metrics.ERROR_LOG_LINE_METRIC[1:] + r'\b',
+                contents))
+            self.assertTrue(re.search(
+                apache_error_log_metrics.ERROR_LOG_METRIC[1:] + r'\b',
+                contents))
+
 
 if __name__ == '__main__':
     unittest.main()