|
|
@@ -0,0 +1,177 @@
|
|
|
+#
|
|
|
+# Copyright (C) 2017 The Android Open Source Project
|
|
|
+#
|
|
|
+# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+# you may not use this file except in compliance with the License.
|
|
|
+# You may obtain a copy of the License at
|
|
|
+#
|
|
|
+# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+#
|
|
|
+# Unless required by applicable law or agreed to in writing, software
|
|
|
+# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+# See the License for the specific language governing permissions and
|
|
|
+# limitations under the License.
|
|
|
+
|
|
|
+from __future__ import print_function
|
|
|
+
|
|
|
+import json
|
|
|
+import multiprocessing
|
|
|
+
|
|
|
+TASK_COMMAND = 'command'
|
|
|
+TASK_SYNC_NETWORK = 'sync-network'
|
|
|
+TASK_SYNC_LOCAL = 'sync-local'
|
|
|
+
|
|
|
+class EventLog(object):
|
|
|
+ """Event log that records events that occurred during a repo invocation.
|
|
|
+
|
|
|
+ Events are written to the log as a consecutive JSON entries, one per line.
|
|
|
+ Each entry contains the following keys:
|
|
|
+ - id: A ('RepoOp', ID) tuple, suitable for storing in a datastore.
|
|
|
+ The ID is only unique for the invocation of the repo command.
|
|
|
+ - name: Name of the object being operated upon.
|
|
|
+ - task_name: The task that was performed.
|
|
|
+ - start: Timestamp of when the operation started.
|
|
|
+ - finish: Timestamp of when the operation finished.
|
|
|
+ - success: Boolean indicating if the operation was successful.
|
|
|
+ - try_count: A counter indicating the try count of this task.
|
|
|
+
|
|
|
+ Optionally:
|
|
|
+ - parent: A ('RepoOp', ID) tuple indicating the parent event for nested
|
|
|
+ events.
|
|
|
+
|
|
|
+ Valid task_names include:
|
|
|
+ - command: The invocation of a subcommand.
|
|
|
+ - sync-network: The network component of a sync command.
|
|
|
+ - sync-local: The local component of a sync command.
|
|
|
+
|
|
|
+ Specific tasks may include additional informational properties.
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ """Initializes the event log."""
|
|
|
+ self._log = []
|
|
|
+ self._next_id = _EventIdGenerator()
|
|
|
+ self._parent = None
|
|
|
+
|
|
|
+ def Add(self, name, task_name, start, finish=None, success=None,
|
|
|
+ try_count=1, kind='RepoOp'):
|
|
|
+ """Add an event to the log.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ name: Name of the object being operated upon.
|
|
|
+ task_name: A sub-task that was performed for name.
|
|
|
+ start: Timestamp of when the operation started.
|
|
|
+ finish: Timestamp of when the operation finished.
|
|
|
+ success: Boolean indicating if the operation was successful.
|
|
|
+ try_count: A counter indicating the try count of this task.
|
|
|
+ kind: The kind of the object for the unique identifier.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ A dictionary of the event added to the log.
|
|
|
+ """
|
|
|
+ event = {
|
|
|
+ 'id': (kind, self._next_id.next()),
|
|
|
+ 'name': name,
|
|
|
+ 'task_name': task_name,
|
|
|
+ 'start_time': start,
|
|
|
+ 'try': try_count,
|
|
|
+ }
|
|
|
+
|
|
|
+ if self._parent:
|
|
|
+ event['parent'] = self._parent['id']
|
|
|
+
|
|
|
+ if success is not None or finish is not None:
|
|
|
+ self.FinishEvent(event, finish, success)
|
|
|
+
|
|
|
+ self._log.append(event)
|
|
|
+ return event
|
|
|
+
|
|
|
+ def AddSync(self, project, task_name, start, finish, success):
|
|
|
+ """Add a event to the log for a sync command.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ project: Project being synced.
|
|
|
+ task_name: A sub-task that was performed for name.
|
|
|
+ One of (TASK_SYNC_NETWORK, TASK_SYNC_LOCAL)
|
|
|
+ start: Timestamp of when the operation started.
|
|
|
+ finish: Timestamp of when the operation finished.
|
|
|
+ success: Boolean indicating if the operation was successful.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ A dictionary of the event added to the log.
|
|
|
+ """
|
|
|
+ event = self.Add(project.relpath, success, start, finish, task_name)
|
|
|
+ if event is not None:
|
|
|
+ event['project'] = project.name
|
|
|
+ if project.revisionExpr:
|
|
|
+ event['revision'] = project.revisionExpr
|
|
|
+ if project.remote.url:
|
|
|
+ event['project_url'] = project.remote.url
|
|
|
+ if project.remote.fetchUrl:
|
|
|
+ event['remote_url'] = project.remote.fetchUrl
|
|
|
+ try:
|
|
|
+ event['git_hash'] = project.GetCommitRevisionId()
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+ return event
|
|
|
+
|
|
|
+ def GetStatusString(self, success):
|
|
|
+ """Converst a boolean success to a status string.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ success: Boolean indicating if the operation was successful.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ status string.
|
|
|
+ """
|
|
|
+ return 'pass' if success else 'fail'
|
|
|
+
|
|
|
+ def FinishEvent(self, event, finish, success):
|
|
|
+ """Finishes an incomplete event.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ event: An event that has been added to the log.
|
|
|
+ finish: Timestamp of when the operation finished.
|
|
|
+ success: Boolean indicating if the operation was successful.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ A dictionary of the event added to the log.
|
|
|
+ """
|
|
|
+ event['status'] = self.GetStatusString(success)
|
|
|
+ event['finish_time'] = finish
|
|
|
+ return event
|
|
|
+
|
|
|
+ def SetParent(self, event):
|
|
|
+ """Set a parent event for all new entities.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ event: The event to use as a parent.
|
|
|
+ """
|
|
|
+ self._parent = event
|
|
|
+
|
|
|
+ def Write(self, filename):
|
|
|
+ """Writes the log out to a file.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ filename: The file to write the log to.
|
|
|
+ """
|
|
|
+ with open(filename, 'w+') as f:
|
|
|
+ for e in self._log:
|
|
|
+ json.dump(e, f, sort_keys=True)
|
|
|
+ f.write('\n')
|
|
|
+
|
|
|
+
|
|
|
+def _EventIdGenerator():
|
|
|
+ """Returns multi-process safe iterator that generates locally unique id.
|
|
|
+
|
|
|
+ Yields:
|
|
|
+ A unique, to this invocation of the program, integer id.
|
|
|
+ """
|
|
|
+ eid = multiprocessing.Value('i', 1)
|
|
|
+
|
|
|
+ while True:
|
|
|
+ with eid.get_lock():
|
|
|
+ val = eid.value
|
|
|
+ eid.value += 1
|
|
|
+ yield val
|