event_log.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #
  2. # Copyright (C) 2017 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. from __future__ import print_function
  16. import json
  17. import multiprocessing
  18. from pyversion import is_python3
  19. TASK_COMMAND = 'command'
  20. TASK_SYNC_NETWORK = 'sync-network'
  21. TASK_SYNC_LOCAL = 'sync-local'
  22. class EventLog(object):
  23. """Event log that records events that occurred during a repo invocation.
  24. Events are written to the log as a consecutive JSON entries, one per line.
  25. Each entry contains the following keys:
  26. - id: A ('RepoOp', ID) tuple, suitable for storing in a datastore.
  27. The ID is only unique for the invocation of the repo command.
  28. - name: Name of the object being operated upon.
  29. - task_name: The task that was performed.
  30. - start: Timestamp of when the operation started.
  31. - finish: Timestamp of when the operation finished.
  32. - success: Boolean indicating if the operation was successful.
  33. - try_count: A counter indicating the try count of this task.
  34. Optionally:
  35. - parent: A ('RepoOp', ID) tuple indicating the parent event for nested
  36. events.
  37. Valid task_names include:
  38. - command: The invocation of a subcommand.
  39. - sync-network: The network component of a sync command.
  40. - sync-local: The local component of a sync command.
  41. Specific tasks may include additional informational properties.
  42. """
  43. def __init__(self):
  44. """Initializes the event log."""
  45. self._log = []
  46. self._next_id = _EventIdGenerator()
  47. self._parent = None
  48. def Add(self, name, task_name, start, finish=None, success=None,
  49. try_count=1, kind='RepoOp'):
  50. """Add an event to the log.
  51. Args:
  52. name: Name of the object being operated upon.
  53. task_name: A sub-task that was performed for name.
  54. start: Timestamp of when the operation started.
  55. finish: Timestamp of when the operation finished.
  56. success: Boolean indicating if the operation was successful.
  57. try_count: A counter indicating the try count of this task.
  58. kind: The kind of the object for the unique identifier.
  59. Returns:
  60. A dictionary of the event added to the log.
  61. """
  62. event = {
  63. 'id': (kind, self._next_id.__next__() if is_python3() else self._next_id.next()),
  64. 'name': name,
  65. 'task_name': task_name,
  66. 'start_time': start,
  67. 'try': try_count,
  68. }
  69. if self._parent:
  70. event['parent'] = self._parent['id']
  71. if success is not None or finish is not None:
  72. self.FinishEvent(event, finish, success)
  73. self._log.append(event)
  74. return event
  75. def AddSync(self, project, task_name, start, finish, success):
  76. """Add a event to the log for a sync command.
  77. Args:
  78. project: Project being synced.
  79. task_name: A sub-task that was performed for name.
  80. One of (TASK_SYNC_NETWORK, TASK_SYNC_LOCAL)
  81. start: Timestamp of when the operation started.
  82. finish: Timestamp of when the operation finished.
  83. success: Boolean indicating if the operation was successful.
  84. Returns:
  85. A dictionary of the event added to the log.
  86. """
  87. event = self.Add(project.relpath, task_name, start, finish, success)
  88. if event is not None:
  89. event['project'] = project.name
  90. if project.revisionExpr:
  91. event['revision'] = project.revisionExpr
  92. if project.remote.url:
  93. event['project_url'] = project.remote.url
  94. if project.remote.fetchUrl:
  95. event['remote_url'] = project.remote.fetchUrl
  96. try:
  97. event['git_hash'] = project.GetCommitRevisionId()
  98. except Exception:
  99. pass
  100. return event
  101. def GetStatusString(self, success):
  102. """Converst a boolean success to a status string.
  103. Args:
  104. success: Boolean indicating if the operation was successful.
  105. Returns:
  106. status string.
  107. """
  108. return 'pass' if success else 'fail'
  109. def FinishEvent(self, event, finish, success):
  110. """Finishes an incomplete event.
  111. Args:
  112. event: An event that has been added to the log.
  113. finish: Timestamp of when the operation finished.
  114. success: Boolean indicating if the operation was successful.
  115. Returns:
  116. A dictionary of the event added to the log.
  117. """
  118. event['status'] = self.GetStatusString(success)
  119. event['finish_time'] = finish
  120. return event
  121. def SetParent(self, event):
  122. """Set a parent event for all new entities.
  123. Args:
  124. event: The event to use as a parent.
  125. """
  126. self._parent = event
  127. def Write(self, filename):
  128. """Writes the log out to a file.
  129. Args:
  130. filename: The file to write the log to.
  131. """
  132. with open(filename, 'w+') as f:
  133. for e in self._log:
  134. json.dump(e, f, sort_keys=True)
  135. f.write('\n')
  136. def _EventIdGenerator():
  137. """Returns multi-process safe iterator that generates locally unique id.
  138. Yields:
  139. A unique, to this invocation of the program, integer id.
  140. """
  141. eid = multiprocessing.Value('i', 1)
  142. while True:
  143. with eid.get_lock():
  144. val = eid.value
  145. eid.value += 1
  146. yield val