Connect to stackdriver to fetch logs of each suite-scheduler run.

BUG=chromium:781379
TEST=Ran
'http://localhost:8080/test/stackdriverLogging?start_time=2017-11-1T17:30:00Z&end_time=2017-11-1T18:00:00Z&resource=/cron/test_push'.

Change-Id: Id84fdf6c79f73b7e04ec375e82cc016288c74b1e
diff --git a/stackdriver_lib.py b/stackdriver_lib.py
new file mode 100644
index 0000000..d8633ba
--- /dev/null
+++ b/stackdriver_lib.py
@@ -0,0 +1,96 @@
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Module for stackdriver related functions."""
+
+import os
+
+
+LOGGING_FORMAT = ('%(time)s %(levelname)-5.5s|%(module)'
+                  '18.18s:%(lineno)4.4s| %(message)s')
+
+
+def form_logging_client_request(project_id, utc_start_time, utc_end_time,
+                                resource_name, order_by='timestamp asc'):
+  """Form a request that can be used by stackdriver logger API.
+
+  Args:
+    project_id: the GAE project id.
+    utc_start_time: A string representing UTC time, in format of
+      converter.STACKDRIVER_TIME_FORMAT.
+    utc_end_time: A string representing UTC time, in format of
+      converter.STACKDRIVER_TIME_FORMAT.
+    resource_name: the resouce of the logs to fetch.
+    order_by: the order of the logs. By default it's asc in time.
+
+  Returns:
+    A dict representing formalized request.
+  """
+  request = {}
+  request['projectIds'] = [project_id]
+  request['orderBy'] = order_by
+  request['filter'] = (
+      'protoPayload.resource = "%s" AND '
+      'protoPayload.startTime > "%s" AND '
+      'protoPayload.endTime < "%s"' % (
+          resource_name, utc_start_time, utc_end_time))
+  return request
+
+
+def parse_logging_client_response(logs):
+  """Parse json object returned by stackDriver client.
+
+  Args:
+    logs: A json object includes all logs.
+
+  Returns:
+    A string includes all lines of logs, where each line is split by
+    a newline.
+  """
+  response = ''
+  if 'entries' not in logs:
+    return response
+
+  for entry in logs['entries']:
+    if 'line' not in entry['protoPayload']:
+      continue
+
+    for line in entry['protoPayload']['line']:
+      response += _form_logging_line(line) + '\n'
+
+  return response
+
+
+def _form_logging_line(line):
+  """Form each line of logs with specified format.
+
+  Args:
+    line: A json object including all required information of a logging line.
+
+  Returns:
+    A string of logs formatted by LOGGING_FORMAT.
+  """
+  module, lineno = _parse_file_info(line)
+  formatted_line = LOGGING_FORMAT % {'time': line['time'],
+                                     'levelname': line['severity'],
+                                     'module': module,
+                                     'lineno': lineno,
+                                     'message': line['logMessage']}
+  return formatted_line
+
+
+def _parse_file_info(line):
+  """Parse the file information from line object.
+
+  Args:
+    line: A json object including file location and line number.
+
+  Returns:
+    A tuple of file location & line number in file.
+  """
+  try:
+    source = line['sourceLocation']
+    return os.path.basename(source['file']), source['line']
+  except KeyError:
+    return '', ''